From 0da28fd44cc6515e453ebd72d9a23d9aa8d88920 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 6 Nov 2013 11:20:40 -0800 Subject: [PATCH 01/17] debbugging for node shutdown in VS --- libraries/voxel-server-library/src/VoxelNodeData.cpp | 12 ++++++++++++ libraries/voxel-server-library/src/VoxelServer.cpp | 6 +++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/libraries/voxel-server-library/src/VoxelNodeData.cpp b/libraries/voxel-server-library/src/VoxelNodeData.cpp index 73f0987d97..adaed73a83 100644 --- a/libraries/voxel-server-library/src/VoxelNodeData.cpp +++ b/libraries/voxel-server-library/src/VoxelNodeData.cpp @@ -35,6 +35,8 @@ VoxelNodeData::VoxelNodeData(Node* owningNode) : _lastVoxelPacketLength = 0; _duplicatePacketCount = 0; resetVoxelPacket(); + + qDebug("VoxelNodeData::VoxelNodeData() this=%p owningNode=%p\n", this, owningNode); } void VoxelNodeData::initializeVoxelSendThread(VoxelServer* voxelServer) { @@ -42,6 +44,10 @@ void VoxelNodeData::initializeVoxelSendThread(VoxelServer* voxelServer) { QUuid nodeUUID = getOwningNode()->getUUID(); _voxelSendThread = new VoxelSendThread(nodeUUID, voxelServer); _voxelSendThread->initialize(true); + + qDebug("VoxelNodeData::initializeVoxelSendThread() this=%p owningNode=%p _voxelSendThread=%p\n", + this, getOwningNode(), _voxelSendThread); + qDebug() << "VoxelNodeData::initializeVoxelSendThread() nodeUUID=" << nodeUUID << "\n"; } bool VoxelNodeData::packetIsDuplicate() const { @@ -111,6 +117,12 @@ void VoxelNodeData::writeToPacket(unsigned char* buffer, int bytes) { } VoxelNodeData::~VoxelNodeData() { + + qDebug("VoxelNodeData::~VoxelNodeData() this=%p owningNode=%p _voxelSendThread=%p\n", + this, getOwningNode(), _voxelSendThread); + QUuid nodeUUID = getOwningNode()->getUUID(); + qDebug() << "VoxelNodeData::initializeVoxelSendThread() nodeUUID=" << nodeUUID << "\n"; + delete[] _voxelPacket; delete[] _lastVoxelPacket; diff --git a/libraries/voxel-server-library/src/VoxelServer.cpp b/libraries/voxel-server-library/src/VoxelServer.cpp index 1053564f3b..04f9f64d70 100644 --- a/libraries/voxel-server-library/src/VoxelServer.cpp +++ b/libraries/voxel-server-library/src/VoxelServer.cpp @@ -46,7 +46,11 @@ const char* VOXELS_PERSIST_FILE = "/etc/highfidelity/voxel-server/resources/voxe void attachVoxelNodeDataToNode(Node* newNode) { if (newNode->getLinkedData() == NULL) { - newNode->setLinkedData(new VoxelNodeData(newNode)); + VoxelNodeData* voxelNodeData = new VoxelNodeData(newNode); + QUuid nodeUUID = newNode->getUUID(); + qDebug("attachVoxelNodeDataToNode() newNode=%p voxelNodeData=%p\n", newNode, voxelNodeData); + qDebug() << "attachVoxelNodeDataToNode() node UUID:" << nodeUUID << "\n"; + newNode->setLinkedData(voxelNodeData); } } From 5cd0ecde18b3651f98de73becae4cb72cd27e38d Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 6 Nov 2013 12:10:19 -0800 Subject: [PATCH 02/17] small tweak in debug out --- libraries/voxel-server-library/src/VoxelNodeData.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/voxel-server-library/src/VoxelNodeData.cpp b/libraries/voxel-server-library/src/VoxelNodeData.cpp index adaed73a83..8151839b7d 100644 --- a/libraries/voxel-server-library/src/VoxelNodeData.cpp +++ b/libraries/voxel-server-library/src/VoxelNodeData.cpp @@ -121,7 +121,7 @@ VoxelNodeData::~VoxelNodeData() { qDebug("VoxelNodeData::~VoxelNodeData() this=%p owningNode=%p _voxelSendThread=%p\n", this, getOwningNode(), _voxelSendThread); QUuid nodeUUID = getOwningNode()->getUUID(); - qDebug() << "VoxelNodeData::initializeVoxelSendThread() nodeUUID=" << nodeUUID << "\n"; + qDebug() << "VoxelNodeData::~VoxelNodeData() nodeUUID=" << nodeUUID << "\n"; delete[] _voxelPacket; delete[] _lastVoxelPacket; From 503de083f8cbc92a9506874612170c6ef480ae18 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 6 Nov 2013 12:27:45 -0800 Subject: [PATCH 03/17] an actual patch for two finger mouselook fail after dialog --- interface/src/Menu.cpp | 124 ++++++++++++++++++++++++----------------- 1 file changed, 72 insertions(+), 52 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index dbae859d24..21dfdc247a 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include @@ -718,16 +719,17 @@ void Menu::aboutApp() { InfoView::forcedShow(); } -void updateDSHostname(const QString& domainServerHostname) { - QString newHostname(DEFAULT_DOMAIN_HOSTNAME); +void sendFakeEnterEvent() { + QPoint lastCursorPosition = QCursor::pos(); - if (domainServerHostname.size() > 0) { - // the user input a new hostname, use that - newHostname = domainServerHostname; + QWindowList windows = QGuiApplication::topLevelWindows(); + foreach(QWindow* window, windows) { + if (window->isActive()) { + QPoint windowPosition = window->mapFromGlobal(lastCursorPosition); + QEnterEvent enterEvent = QEnterEvent(windowPosition, windowPosition, lastCursorPosition); + QCoreApplication::sendEvent(window, &enterEvent); + } } - - // give our nodeList the new domain-server hostname - NodeList::getInstance()->setDomainHostname(newHostname); } const int QLINE_MINIMUM_WIDTH = 400; @@ -743,11 +745,15 @@ void Menu::login() { loginDialog.resize(loginDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW, loginDialog.size().height()); int dialogReturn = loginDialog.exec(); + if (dialogReturn == QDialog::Accepted && !loginDialog.textValue().isEmpty() && loginDialog.textValue() != username) { // there has been a username change // ask for a profile reset with the new username Application::getInstance()->resetProfile(loginDialog.textValue()); + } + + sendFakeEnterEvent(); } void Menu::editPreferences() { @@ -811,52 +817,52 @@ void Menu::editPreferences() { layout->addWidget(buttons); int ret = dialog.exec(); - if (ret != QDialog::Accepted) { - return; - } - - QUrl faceModelURL(faceURLEdit->text()); - - if (faceModelURL.toString() != faceURLString) { - // change the faceModelURL in the profile, it will also update this user's BlendFace - applicationInstance->getProfile()->setFaceModelURL(faceModelURL); + if (ret == QDialog::Accepted) { + QUrl faceModelURL(faceURLEdit->text()); - // send the new face mesh URL to the data-server (if we have a client UUID) - DataServerClient::putValueForKey(DataServerKey::FaceMeshURL, - faceModelURL.toString().toLocal8Bit().constData()); - } - - QUrl skeletonModelURL(skeletonURLEdit->text()); - - if (skeletonModelURL.toString() != skeletonURLString) { - // change the skeletonModelURL in the profile, it will also update this user's Body - applicationInstance->getProfile()->setSkeletonModelURL(skeletonModelURL); + if (faceModelURL.toString() != faceURLString) { + // change the faceModelURL in the profile, it will also update this user's BlendFace + applicationInstance->getProfile()->setFaceModelURL(faceModelURL); + + // send the new face mesh URL to the data-server (if we have a client UUID) + DataServerClient::putValueForKey(DataServerKey::FaceMeshURL, + faceModelURL.toString().toLocal8Bit().constData()); + } - // send the new skeleton model URL to the data-server (if we have a client UUID) - DataServerClient::putValueForKey(DataServerKey::SkeletonURL, - skeletonModelURL.toString().toLocal8Bit().constData()); + QUrl skeletonModelURL(skeletonURLEdit->text()); + + if (skeletonModelURL.toString() != skeletonURLString) { + // change the skeletonModelURL in the profile, it will also update this user's Body + applicationInstance->getProfile()->setSkeletonModelURL(skeletonModelURL); + + // send the new skeleton model URL to the data-server (if we have a client UUID) + DataServerClient::putValueForKey(DataServerKey::SkeletonURL, + skeletonModelURL.toString().toLocal8Bit().constData()); + } + + QUrl avatarVoxelURL(avatarURL->text()); + applicationInstance->getAvatar()->getVoxels()->setVoxelURL(avatarVoxelURL); + + Avatar::sendAvatarURLsMessage(avatarVoxelURL); + + applicationInstance->getAvatar()->getHead().setPupilDilation(pupilDilation->value() / (float)pupilDilation->maximum()); + + _maxVoxels = maxVoxels->value(); + applicationInstance->getVoxels()->setMaxVoxels(_maxVoxels); + + applicationInstance->getAvatar()->setLeanScale(leanScale->value()); + + _audioJitterBufferSamples = audioJitterBufferSamples->value(); + + if (_audioJitterBufferSamples != 0) { + applicationInstance->getAudio()->setJitterBufferSamples(_audioJitterBufferSamples); + } + + _fieldOfView = fieldOfView->value(); + applicationInstance->resizeGL(applicationInstance->getGLWidget()->width(), applicationInstance->getGLWidget()->height()); } - QUrl avatarVoxelURL(avatarURL->text()); - applicationInstance->getAvatar()->getVoxels()->setVoxelURL(avatarVoxelURL); - - Avatar::sendAvatarURLsMessage(avatarVoxelURL); - - applicationInstance->getAvatar()->getHead().setPupilDilation(pupilDilation->value() / (float)pupilDilation->maximum()); - - _maxVoxels = maxVoxels->value(); - applicationInstance->getVoxels()->setMaxVoxels(_maxVoxels); - - applicationInstance->getAvatar()->setLeanScale(leanScale->value()); - - _audioJitterBufferSamples = audioJitterBufferSamples->value(); - - if (_audioJitterBufferSamples != 0) { - applicationInstance->getAudio()->setJitterBufferSamples(_audioJitterBufferSamples); - } - - _fieldOfView = fieldOfView->value(); - applicationInstance->resizeGL(applicationInstance->getGLWidget()->width(), applicationInstance->getGLWidget()->height()); + sendFakeEnterEvent(); } void Menu::goToDomain() { @@ -876,9 +882,19 @@ void Menu::goToDomain() { domainDialog.resize(domainDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW, domainDialog.size().height()); int dialogReturn = domainDialog.exec(); - if (dialogReturn == QDialog::Accepted && !domainDialog.textValue().isEmpty()) { - updateDSHostname(domainDialog.textValue()); + if (dialogReturn == QDialog::Accepted) { + QString newHostname(DEFAULT_DOMAIN_HOSTNAME); + + if (domainDialog.textValue().size() > 0) { + // the user input a new hostname, use that + newHostname = domainDialog.textValue(); + } + + // give our nodeList the new domain-server hostname + NodeList::getInstance()->setDomainHostname(domainDialog.textValue()); } + + sendFakeEnterEvent(); } void Menu::goToLocation() { @@ -918,6 +934,8 @@ void Menu::goToLocation() { } } } + + sendFakeEnterEvent(); } void Menu::goToUser() { @@ -935,6 +953,8 @@ void Menu::goToUser() { DataServerClient::getValuesForKeysAndUserString((QStringList() << DataServerKey::Domain << DataServerKey::Position), userDialog.textValue()); } + + sendFakeEnterEvent(); } void Menu::bandwidthDetails() { From a4fcff6b19ba8117af9a159a21c9e09a0520873b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 6 Nov 2013 12:36:52 -0800 Subject: [PATCH 04/17] only send fake event to the OGLWidget --- interface/src/Menu.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 21dfdc247a..92c6bbf63b 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -721,15 +721,11 @@ void Menu::aboutApp() { void sendFakeEnterEvent() { QPoint lastCursorPosition = QCursor::pos(); + QGLWidget* glWidget = Application::getInstance()->getGLWidget(); - QWindowList windows = QGuiApplication::topLevelWindows(); - foreach(QWindow* window, windows) { - if (window->isActive()) { - QPoint windowPosition = window->mapFromGlobal(lastCursorPosition); - QEnterEvent enterEvent = QEnterEvent(windowPosition, windowPosition, lastCursorPosition); - QCoreApplication::sendEvent(window, &enterEvent); - } - } + QPoint windowPosition = glWidget->mapFromGlobal(lastCursorPosition); + QEnterEvent enterEvent = QEnterEvent(windowPosition, windowPosition, lastCursorPosition); + QCoreApplication::sendEvent(glWidget, &enterEvent); } const int QLINE_MINIMUM_WIDTH = 400; From c3914e648f56c4a73465b56daa8a3f91f426383c Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 6 Nov 2013 13:52:31 -0800 Subject: [PATCH 05/17] lock our node while using it to prevent crash on domain shutdown --- .../src/VoxelSendThread.cpp | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/libraries/voxel-server-library/src/VoxelSendThread.cpp b/libraries/voxel-server-library/src/VoxelSendThread.cpp index 1a7762825c..18f0485983 100644 --- a/libraries/voxel-server-library/src/VoxelSendThread.cpp +++ b/libraries/voxel-server-library/src/VoxelSendThread.cpp @@ -27,27 +27,31 @@ bool VoxelSendThread::process() { uint64_t start = usecTimestampNow(); Node* node = NodeList::getInstance()->nodeWithUUID(_nodeUUID); - VoxelNodeData* nodeData = NULL; if (node) { - nodeData = (VoxelNodeData*) node->getLinkedData(); - } + node->lock(); // make sure the node list doesn't kill our node while we're using it + VoxelNodeData* nodeData = NULL; - int packetsSent = 0; + nodeData = (VoxelNodeData*) node->getLinkedData(); + + int packetsSent = 0; - // Sometimes the node data has not yet been linked, in which case we can't really do anything - if (nodeData) { - bool viewFrustumChanged = nodeData->updateCurrentViewFrustum(); - if (_myServer->wantsDebugVoxelSending()) { - printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged)); + // Sometimes the node data has not yet been linked, in which case we can't really do anything + if (nodeData) { + bool viewFrustumChanged = nodeData->updateCurrentViewFrustum(); + if (_myServer->wantsDebugVoxelSending()) { + printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged)); + } + packetsSent = deepestLevelVoxelDistributor(node, nodeData, viewFrustumChanged); } - packetsSent = deepestLevelVoxelDistributor(node, nodeData, viewFrustumChanged); + + node->unlock(); // we're done with this node for now. } // dynamically sleep until we need to fire off the next set of voxels int elapsed = (usecTimestampNow() - start); int usecToSleep = VOXEL_SEND_INTERVAL_USECS - elapsed; - + if (usecToSleep > 0) { usleep(usecToSleep); } else { @@ -55,6 +59,7 @@ bool VoxelSendThread::process() { std::cout << "Last send took too much time, not sleeping!\n"; } } + return isStillRunning(); // keep running till they terminate us } From f9291e6e5cae387aecf3ec73c93460eff1475556 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 6 Nov 2013 15:22:17 -0800 Subject: [PATCH 06/17] implement ReadWriteLock for VoxelTree to fix crashes in VoxelServer --- libraries/voxel-server-library/src/VoxelPersistThread.cpp | 6 ++++++ libraries/voxel-server-library/src/VoxelSendThread.cpp | 4 ++-- libraries/voxel-server-library/src/VoxelServer.cpp | 4 ---- libraries/voxel-server-library/src/VoxelServer.h | 3 --- .../src/VoxelServerPacketProcessor.cpp | 8 ++++---- libraries/voxels/src/VoxelTree.h | 8 ++++++++ 6 files changed, 20 insertions(+), 13 deletions(-) diff --git a/libraries/voxel-server-library/src/VoxelPersistThread.cpp b/libraries/voxel-server-library/src/VoxelPersistThread.cpp index 75954aacec..0a22b0a2ec 100644 --- a/libraries/voxel-server-library/src/VoxelPersistThread.cpp +++ b/libraries/voxel-server-library/src/VoxelPersistThread.cpp @@ -33,7 +33,9 @@ bool VoxelPersistThread::process() { { PerformanceWarning warn(true, "Loading Voxel File", true); + _tree->lockForRead(); persistantFileRead = _tree->readFromSVOFile(_filename); + _tree->unlock(); } if (persistantFileRead) { @@ -41,7 +43,9 @@ bool VoxelPersistThread::process() { // after done inserting all these voxels, then reaverage colors qDebug("BEGIN Voxels Re-Averaging\n"); + _tree->lockForWrite(); _tree->reaverageVoxelColors(_tree->rootNode); + _tree->unlock(); qDebug("DONE WITH Voxels Re-Averaging\n"); } @@ -70,7 +74,9 @@ bool VoxelPersistThread::process() { // check the dirty bit and persist here... if (_tree->isDirty()) { qDebug("saving voxels to file %s...\n",_filename); + _tree->lockForRead(); _tree->writeToSVOFile(_filename); + _tree->unlock(); _tree->clearDirtyBit(); // tree is clean after saving qDebug("DONE saving voxels to file...\n"); } diff --git a/libraries/voxel-server-library/src/VoxelSendThread.cpp b/libraries/voxel-server-library/src/VoxelSendThread.cpp index 18f0485983..9e669b4da7 100644 --- a/libraries/voxel-server-library/src/VoxelSendThread.cpp +++ b/libraries/voxel-server-library/src/VoxelSendThread.cpp @@ -117,7 +117,7 @@ int VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int& /// Version of voxel distributor that sends the deepest LOD level at once int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nodeData, bool viewFrustumChanged) { - _myServer->lockTree(); + _myServer->getTree()->lockForRead(); int truePacketsSent = 0; int trueBytesSent = 0; @@ -360,7 +360,7 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod } // end if bag wasn't empty, and so we sent stuff... - _myServer->unlockTree(); + _myServer->getTree()->unlock(); return truePacketsSent; } diff --git a/libraries/voxel-server-library/src/VoxelServer.cpp b/libraries/voxel-server-library/src/VoxelServer.cpp index 04f9f64d70..a5cb8f9620 100644 --- a/libraries/voxel-server-library/src/VoxelServer.cpp +++ b/libraries/voxel-server-library/src/VoxelServer.cpp @@ -343,8 +343,6 @@ void VoxelServer::run() { parsePayload(); } - pthread_mutex_init(&_treeLock, NULL); - qInstallMessageHandler(Logging::verboseMessageHandler); const char* STATUS_PORT = "--statusPort"; @@ -591,8 +589,6 @@ void VoxelServer::run() { // tell our NodeList we're done with notifications nodeList->removeHook(&_nodeWatcher); - - pthread_mutex_destroy(&_treeLock); } diff --git a/libraries/voxel-server-library/src/VoxelServer.h b/libraries/voxel-server-library/src/VoxelServer.h index 218205caaf..ca0b07dd63 100644 --- a/libraries/voxel-server-library/src/VoxelServer.h +++ b/libraries/voxel-server-library/src/VoxelServer.h @@ -48,8 +48,6 @@ public: VoxelTree& getServerTree() { return _serverTree; } JurisdictionMap* getJurisdiction() { return _jurisdiction; } - void lockTree() { pthread_mutex_lock(&_treeLock); } - void unlockTree() { pthread_mutex_unlock(&_treeLock); } VoxelTree* getTree() { return &_serverTree; } int getPacketsPerClientPerInterval() const { return _packetsPerClientPerInterval; } @@ -80,7 +78,6 @@ private: JurisdictionSender* _jurisdictionSender; VoxelServerPacketProcessor* _voxelServerPacketProcessor; VoxelPersistThread* _voxelPersistThread; - pthread_mutex_t _treeLock; EnvironmentData _environmentData[3]; NodeWatcher _nodeWatcher; // used to cleanup AGENT data when agents are killed diff --git a/libraries/voxel-server-library/src/VoxelServerPacketProcessor.cpp b/libraries/voxel-server-library/src/VoxelServerPacketProcessor.cpp index 4b3e870592..d156228544 100644 --- a/libraries/voxel-server-library/src/VoxelServerPacketProcessor.cpp +++ b/libraries/voxel-server-library/src/VoxelServerPacketProcessor.cpp @@ -86,9 +86,9 @@ void VoxelServerPacketProcessor::processPacket(sockaddr& senderAddress, unsigned delete[] vertices; } - _myServer->lockTree(); + _myServer->getTree()->lockForWrite(); _myServer->getServerTree().readCodeColorBufferToTree(voxelData, destructive); - _myServer->unlockTree(); + _myServer->getTree()->unlock(); // skip to next voxel edit record in the packet voxelData += voxelDataSize; @@ -114,9 +114,9 @@ void VoxelServerPacketProcessor::processPacket(sockaddr& senderAddress, unsigned } else if (packetData[0] == PACKET_TYPE_ERASE_VOXEL) { // Send these bits off to the VoxelTree class to process them - _myServer->lockTree(); + _myServer->getTree()->lockForWrite(); _myServer->getServerTree().processRemoveVoxelBitstream((unsigned char*)packetData, packetLength); - _myServer->unlockTree(); + _myServer->getTree()->unlock(); // Make sure our Node and NodeList knows we've heard from this node. Node* node = NodeList::getInstance()->nodeWithAddress(&senderAddress); diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index 933a1f436e..f9a5c38eb9 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -21,6 +21,7 @@ #include "VoxelEditPacketSender.h" #include +#include // Callback function, for recuseTreeWithOperation typedef bool (*RecurseVoxelTreeOperation)(VoxelNode* node, void* extraData); @@ -185,6 +186,11 @@ public: // reads voxels from square image with alpha as a Y-axis bool readFromSquareARGB32Pixels(const char *filename); bool readFromSchematicFile(const char* filename); + + // VoxelTree does not currently handle its own locking, caller must use these to lock/unlock + void lockForRead() { lock.lockForRead(); } + void lockForWrite() { lock.lockForWrite(); } + void unlock() { lock.unlock(); } unsigned long getVoxelCount(); @@ -266,6 +272,8 @@ private: static bool nudgeCheck(VoxelNode* node, void* extraData); void nudgeLeaf(VoxelNode* node, void* extraData); void chunkifyLeaf(VoxelNode* node); + + QReadWriteLock lock; }; float boundaryDistanceForRenderLevel(unsigned int renderLevel, float voxelSizeScale); From 76ad42015a6afabe4e133e2033e4f4db8b17e1cb Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 6 Nov 2013 16:41:08 -0800 Subject: [PATCH 07/17] added debugging to JurisdictionMap for server crashes --- libraries/voxels/src/JurisdictionMap.cpp | 47 ++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/libraries/voxels/src/JurisdictionMap.cpp b/libraries/voxels/src/JurisdictionMap.cpp index 3da1467f46..c95a71ac75 100644 --- a/libraries/voxels/src/JurisdictionMap.cpp +++ b/libraries/voxels/src/JurisdictionMap.cpp @@ -109,8 +109,47 @@ JurisdictionMap::JurisdictionMap(unsigned char* rootOctalCode, const std::vector init(rootOctalCode, endNodes); } +void myDebugoutputBits(unsigned char byte, bool withNewLine) { + if (isalnum(byte)) { + printf("[ %d (%c): ", byte, byte); + } else { + printf("[ %d (0x%x): ", byte, byte); + } + + for (int i = 0; i < 8; i++) { + printf("%d", byte >> (7 - i) & 1); + } + printf(" ] "); + + if (withNewLine) { + printf("\n"); + } +} + + +void myDebugPrintOctalCode(const unsigned char* octalCode, bool withNewLine) { + if (!octalCode) { + printf("NULL"); + } else { + for (int i = 0; i < bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(octalCode)); i++) { + myDebugoutputBits(octalCode[i],false); + } + } + if (withNewLine) { + printf("\n"); + } +} + + JurisdictionMap::JurisdictionMap(const char* rootHexCode, const char* endNodesHexCodes) { + + qDebug("JurisdictionMap::JurisdictionMap(const char* rootHexCode=[%p] %s, const char* endNodesHexCodes=[%p] %s)\n", + rootHexCode, rootHexCode, endNodesHexCodes, endNodesHexCodes); + _rootOctalCode = hexStringToOctalCode(QString(rootHexCode)); + + qDebug("JurisdictionMap::JurisdictionMap() _rootOctalCode=%p octalCode=", _rootOctalCode); + myDebugPrintOctalCode(_rootOctalCode, true); QString endNodesHexStrings(endNodesHexCodes); QString delimiterPattern(","); @@ -120,8 +159,16 @@ JurisdictionMap::JurisdictionMap(const char* rootHexCode, const char* endNodesHe QString endNodeHexString = endNodeList.at(i); unsigned char* endNodeOctcode = hexStringToOctalCode(endNodeHexString); + + qDebug("JurisdictionMap::JurisdictionMap() endNodeList(%d)=%s\n", + i, endNodeHexString.toLocal8Bit().constData()); + //printOctalCode(endNodeOctcode); _endNodes.push_back(endNodeOctcode); + + qDebug("JurisdictionMap::JurisdictionMap() endNodeOctcode=%p octalCode=", endNodeOctcode); + myDebugPrintOctalCode(endNodeOctcode, true); + } } From 100439784d7ca08d68f1b88b60bd109c2d52896d Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 6 Nov 2013 17:37:37 -0800 Subject: [PATCH 08/17] got rid of redundant getTree() --- libraries/voxel-server-library/src/VoxelSendThread.cpp | 4 ++-- libraries/voxel-server-library/src/VoxelServer.h | 2 -- .../src/VoxelServerPacketProcessor.cpp | 8 ++++---- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/libraries/voxel-server-library/src/VoxelSendThread.cpp b/libraries/voxel-server-library/src/VoxelSendThread.cpp index 9e669b4da7..4455f1e379 100644 --- a/libraries/voxel-server-library/src/VoxelSendThread.cpp +++ b/libraries/voxel-server-library/src/VoxelSendThread.cpp @@ -117,7 +117,7 @@ int VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int& /// Version of voxel distributor that sends the deepest LOD level at once int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nodeData, bool viewFrustumChanged) { - _myServer->getTree()->lockForRead(); + _myServer->getServerTree().lockForRead(); int truePacketsSent = 0; int trueBytesSent = 0; @@ -360,7 +360,7 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod } // end if bag wasn't empty, and so we sent stuff... - _myServer->getTree()->unlock(); + _myServer->getServerTree().unlock(); return truePacketsSent; } diff --git a/libraries/voxel-server-library/src/VoxelServer.h b/libraries/voxel-server-library/src/VoxelServer.h index ca0b07dd63..f938bebd7e 100644 --- a/libraries/voxel-server-library/src/VoxelServer.h +++ b/libraries/voxel-server-library/src/VoxelServer.h @@ -48,8 +48,6 @@ public: VoxelTree& getServerTree() { return _serverTree; } JurisdictionMap* getJurisdiction() { return _jurisdiction; } - VoxelTree* getTree() { return &_serverTree; } - int getPacketsPerClientPerInterval() const { return _packetsPerClientPerInterval; } bool getSendMinimalEnvironment() const { return _sendMinimalEnvironment; } EnvironmentData* getEnvironmentData(int i) { return &_environmentData[i]; } diff --git a/libraries/voxel-server-library/src/VoxelServerPacketProcessor.cpp b/libraries/voxel-server-library/src/VoxelServerPacketProcessor.cpp index d156228544..5fd2028f50 100644 --- a/libraries/voxel-server-library/src/VoxelServerPacketProcessor.cpp +++ b/libraries/voxel-server-library/src/VoxelServerPacketProcessor.cpp @@ -86,9 +86,9 @@ void VoxelServerPacketProcessor::processPacket(sockaddr& senderAddress, unsigned delete[] vertices; } - _myServer->getTree()->lockForWrite(); + _myServer->getServerTree().lockForWrite(); _myServer->getServerTree().readCodeColorBufferToTree(voxelData, destructive); - _myServer->getTree()->unlock(); + _myServer->getServerTree().unlock(); // skip to next voxel edit record in the packet voxelData += voxelDataSize; @@ -114,9 +114,9 @@ void VoxelServerPacketProcessor::processPacket(sockaddr& senderAddress, unsigned } else if (packetData[0] == PACKET_TYPE_ERASE_VOXEL) { // Send these bits off to the VoxelTree class to process them - _myServer->getTree()->lockForWrite(); + _myServer->getServerTree().lockForWrite(); _myServer->getServerTree().processRemoveVoxelBitstream((unsigned char*)packetData, packetLength); - _myServer->getTree()->unlock(); + _myServer->getServerTree().unlock(); // Make sure our Node and NodeList knows we've heard from this node. Node* node = NodeList::getInstance()->nodeWithAddress(&senderAddress); From 4cbb98028f34ddcb5fd5aa068887fd64c68c0059 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 7 Nov 2013 12:07:34 -0800 Subject: [PATCH 09/17] temp remove audio from agent, fix GOL placeholder --- assignment-client/src/Agent.cpp | 29 +++++-------------- .../resources/web/assignment/placeholder.js | 5 ++++ 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 206d115c84..4319ca706e 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -57,7 +57,7 @@ void Agent::run() { NodeList* nodeList = NodeList::getInstance(); nodeList->setOwnerType(NODE_TYPE_AGENT); - const char AGENT_NODE_TYPES_OF_INTEREST[2] = { NODE_TYPE_VOXEL_SERVER, NODE_TYPE_AUDIO_MIXER }; + const char AGENT_NODE_TYPES_OF_INTEREST[1] = { NODE_TYPE_VOXEL_SERVER }; nodeList->setNodeTypesOfInterest(AGENT_NODE_TYPES_OF_INTEREST, sizeof(AGENT_NODE_TYPES_OF_INTEREST)); @@ -114,9 +114,11 @@ void Agent::run() { QScriptValue treeScaleValue = engine.newVariant(QVariant(TREE_SCALE)); engine.globalObject().setProperty("TREE_SCALE", treeScaleValue); + + const unsigned int VISUAL_DATA_CALLBACK_USECS = (1.0 / 60.0) * 1000 * 1000; // let the VoxelPacketSender know how frequently we plan to call it - voxelScripter.getVoxelPacketSender()->setProcessCallIntervalHint(INJECT_INTERVAL_USECS); + voxelScripter.getVoxelPacketSender()->setProcessCallIntervalHint(VISUAL_DATA_CALLBACK_USECS); // hook in a constructor for audio injectorss AudioInjector scriptedAudioInjector(BUFFER_LENGTH_SAMPLES_PER_CHANNEL); @@ -158,28 +160,14 @@ void Agent::run() { NodeList::getInstance()->sendDomainServerCheckIn(); } - // find the audio-mixer in the NodeList so we can inject audio at it - Node* audioMixer = NodeList::getInstance()->soloNodeOfType(NODE_TYPE_AUDIO_MIXER); - - - if (audioMixer && audioMixer->getActiveSocket()) { - emit willSendAudioDataCallback(); - } - - int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * INJECT_INTERVAL_USECS) - usecTimestampNow(); + int usecToSleep = usecTimestamp(&startTime) + (thisFrame++ * VISUAL_DATA_CALLBACK_USECS) - usecTimestampNow(); if (usecToSleep > 0) { usleep(usecToSleep); } - if (audioMixer && NodeList::getInstance()->getNodeActiveSocketOrPing(audioMixer) && scriptedAudioInjector.hasSamplesToInject()) { - // we have an audio mixer and samples to inject, send those off - scriptedAudioInjector.injectAudio(NodeList::getInstance()->getNodeSocket(), audioMixer->getActiveSocket()); - - // clear out the audio injector so that it doesn't re-send what we just sent - scriptedAudioInjector.clear(); - } - if (voxelScripter.getVoxelPacketSender()->voxelServersExist()) { + timeval thisSend = {}; + gettimeofday(&thisSend, NULL); // allow the scripter's call back to setup visual data emit willSendVisualDataCallback(); @@ -188,14 +176,13 @@ void Agent::run() { // since we're in non-threaded mode, call process so that the packets are sent voxelScripter.getVoxelPacketSender()->process(); - } if (engine.hasUncaughtException()) { int line = engine.uncaughtExceptionLineNumber(); qDebug() << "Uncaught exception at line" << line << ":" << engine.uncaughtException().toString() << "\n"; } - + while (NodeList::getInstance()->getNodeSocket()->receive((sockaddr*) &senderAddress, receivedData, &receivedBytes) && packetVersionMatch(receivedData)) { if (receivedData[0] == PACKET_TYPE_VOXEL_JURISDICTION) { diff --git a/domain-server/resources/web/assignment/placeholder.js b/domain-server/resources/web/assignment/placeholder.js index 74891d12e8..ee8f89cdd6 100644 --- a/domain-server/resources/web/assignment/placeholder.js +++ b/domain-server/resources/web/assignment/placeholder.js @@ -78,6 +78,11 @@ function updateCells() { nextCells[i][j] = -1; } } + + if (Math.random() < 0.001) { + // Random mutation to keep things interesting in there. + nextCells[i][j] = 1; + } } } From fe962e6be1708facb37ea4182eff27cf8f439d9c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 7 Nov 2013 12:38:28 -0800 Subject: [PATCH 10/17] only call deleteLater for linkedData if we have a QCoreApplication instance --- assignment-client/src/main.cpp | 5 ----- libraries/shared/src/Node.cpp | 5 ++++- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/main.cpp b/assignment-client/src/main.cpp index c866f5f00c..728bcb3c00 100644 --- a/assignment-client/src/main.cpp +++ b/assignment-client/src/main.cpp @@ -12,9 +12,6 @@ #include #include -#include - - #include #include #include @@ -41,8 +38,6 @@ int argc = 0; char** argv = NULL; void childClient() { - QCoreApplication application(::argc, ::argv); - // set the logging target to the the CHILD_TARGET_NAME Logging::setTargetName(CHILD_TARGET_NAME); diff --git a/libraries/shared/src/Node.cpp b/libraries/shared/src/Node.cpp index 093f479c48..e62dc2fd53 100644 --- a/libraries/shared/src/Node.cpp +++ b/libraries/shared/src/Node.cpp @@ -21,6 +21,7 @@ #include "SharedUtil.h" #include "UDPSocket.h" +#include #include Node::Node(const QUuid& uuid, char type, sockaddr* publicSocket, sockaddr* localSocket) : @@ -43,8 +44,10 @@ Node::~Node() { delete _publicSocket; delete _localSocket; - if (_linkedData) { + if (QCoreApplication::instance()) { _linkedData->deleteLater(); + } else { + delete _linkedData; } delete _bytesReceivedMovingAverage; From 24c88512e2f1e06bf3f9d81aa46d909f64d6a23f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 7 Nov 2013 12:41:29 -0800 Subject: [PATCH 11/17] only attempt to deleteLater on linkedData if it exists --- libraries/shared/src/Node.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/shared/src/Node.cpp b/libraries/shared/src/Node.cpp index e62dc2fd53..205d3109a5 100644 --- a/libraries/shared/src/Node.cpp +++ b/libraries/shared/src/Node.cpp @@ -45,7 +45,9 @@ Node::~Node() { delete _localSocket; if (QCoreApplication::instance()) { - _linkedData->deleteLater(); + if (_linkedData) { + _linkedData->deleteLater(); + } } else { delete _linkedData; } From b5f6a51728919ba82b8320d26d0a33d3d53d6227 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 7 Nov 2013 13:03:04 -0800 Subject: [PATCH 12/17] fix thread contention issues and VS crash on domain server shutdown and restart --- .../src/VoxelNodeData.cpp | 1 + .../voxel-server-library/src/VoxelNodeData.h | 2 +- .../src/VoxelPersistThread.cpp | 29 ++++----- .../src/VoxelPersistThread.h | 11 +++- .../src/VoxelSendThread.cpp | 44 ++++++++------ .../voxel-server-library/src/VoxelServer.cpp | 59 ++++++++++++++++++- .../voxel-server-library/src/VoxelServer.h | 4 ++ .../src/VoxelServerPacketProcessor.cpp | 2 +- libraries/voxels/src/VoxelQuery.cpp | 1 + libraries/voxels/src/VoxelQuery.h | 2 +- libraries/voxels/src/VoxelTree.cpp | 3 +- libraries/voxels/src/VoxelTree.h | 2 + 12 files changed, 116 insertions(+), 44 deletions(-) diff --git a/libraries/voxel-server-library/src/VoxelNodeData.cpp b/libraries/voxel-server-library/src/VoxelNodeData.cpp index 8151839b7d..2cede147b3 100644 --- a/libraries/voxel-server-library/src/VoxelNodeData.cpp +++ b/libraries/voxel-server-library/src/VoxelNodeData.cpp @@ -129,6 +129,7 @@ VoxelNodeData::~VoxelNodeData() { if (_voxelSendThread) { _voxelSendThread->terminate(); delete _voxelSendThread; + qDebug("VoxelNodeData::~VoxelNodeData() DELETED _voxelSendThread=%p\n", _voxelSendThread); } } diff --git a/libraries/voxel-server-library/src/VoxelNodeData.h b/libraries/voxel-server-library/src/VoxelNodeData.h index 1ad4558b81..c21b99a308 100644 --- a/libraries/voxel-server-library/src/VoxelNodeData.h +++ b/libraries/voxel-server-library/src/VoxelNodeData.h @@ -24,7 +24,7 @@ class VoxelServer; class VoxelNodeData : public VoxelQuery { public: VoxelNodeData(Node* owningNode); - ~VoxelNodeData(); + virtual ~VoxelNodeData(); void resetVoxelPacket(); // resets voxel packet to after "V" header diff --git a/libraries/voxel-server-library/src/VoxelPersistThread.cpp b/libraries/voxel-server-library/src/VoxelPersistThread.cpp index 0a22b0a2ec..0f2718fe1f 100644 --- a/libraries/voxel-server-library/src/VoxelPersistThread.cpp +++ b/libraries/voxel-server-library/src/VoxelPersistThread.cpp @@ -20,34 +20,28 @@ VoxelPersistThread::VoxelPersistThread(VoxelTree* tree, const char* filename, in _tree(tree), _filename(filename), _persistInterval(persistInterval), - _initialLoad(false) { + _initialLoadComplete(false), + _loadTimeUSecs(0) { } bool VoxelPersistThread::process() { - if (!_initialLoad) { - _initialLoad = true; + if (!_initialLoadComplete) { + uint64_t loadStarted = usecTimestampNow(); qDebug("loading voxels from file: %s...\n", _filename); bool persistantFileRead; - + + _tree->lockForWrite(); { PerformanceWarning warn(true, "Loading Voxel File", true); - _tree->lockForRead(); persistantFileRead = _tree->readFromSVOFile(_filename); - _tree->unlock(); } + _tree->unlock(); - if (persistantFileRead) { - PerformanceWarning warn(true, "Voxels Re-Averaging", true); - - // after done inserting all these voxels, then reaverage colors - qDebug("BEGIN Voxels Re-Averaging\n"); - _tree->lockForWrite(); - _tree->reaverageVoxelColors(_tree->rootNode); - _tree->unlock(); - qDebug("DONE WITH Voxels Re-Averaging\n"); - } + _loadCompleted = time(0); + uint64_t loadDone = usecTimestampNow(); + _loadTimeUSecs = loadDone - loadStarted; _tree->clearDirtyBit(); // the tree is clean since we just loaded it qDebug("DONE loading voxels from file... fileRead=%s\n", debug::valueOf(persistantFileRead)); @@ -65,6 +59,7 @@ bool VoxelPersistThread::process() { qDebug("setChildAtIndexCalls=%llu setChildAtIndexTime=%llu perSet=%lf\n", VoxelNode::getSetChildAtIndexTime(), VoxelNode::getSetChildAtIndexCalls(), usecPerSet); + _initialLoadComplete = true; } uint64_t MSECS_TO_USECS = 1000; @@ -74,9 +69,7 @@ bool VoxelPersistThread::process() { // check the dirty bit and persist here... if (_tree->isDirty()) { qDebug("saving voxels to file %s...\n",_filename); - _tree->lockForRead(); _tree->writeToSVOFile(_filename); - _tree->unlock(); _tree->clearDirtyBit(); // tree is clean after saving qDebug("DONE saving voxels to file...\n"); } diff --git a/libraries/voxel-server-library/src/VoxelPersistThread.h b/libraries/voxel-server-library/src/VoxelPersistThread.h index 3219633702..91e65a2daf 100644 --- a/libraries/voxel-server-library/src/VoxelPersistThread.h +++ b/libraries/voxel-server-library/src/VoxelPersistThread.h @@ -21,6 +21,12 @@ public: static const int DEFAULT_PERSIST_INTERVAL = 1000 * 30; // every 30 seconds VoxelPersistThread(VoxelTree* tree, const char* filename, int persistInterval = DEFAULT_PERSIST_INTERVAL); + + bool isInitialLoadComplete() const { return _initialLoadComplete; } + + time_t* getLoadCompleted() { return &_loadCompleted; } + uint64_t getLoadElapsedTime() const { return _loadTimeUSecs; } + protected: /// Implements generic processing behavior for this thread. virtual bool process(); @@ -28,7 +34,10 @@ private: VoxelTree* _tree; const char* _filename; int _persistInterval; - bool _initialLoad; + bool _initialLoadComplete; + + time_t _loadCompleted; + uint64_t _loadTimeUSecs; }; #endif // __voxel_server__VoxelPersistThread__ diff --git a/libraries/voxel-server-library/src/VoxelSendThread.cpp b/libraries/voxel-server-library/src/VoxelSendThread.cpp index 4455f1e379..4bfed9c9ad 100644 --- a/libraries/voxel-server-library/src/VoxelSendThread.cpp +++ b/libraries/voxel-server-library/src/VoxelSendThread.cpp @@ -26,28 +26,35 @@ VoxelSendThread::VoxelSendThread(const QUuid& nodeUUID, VoxelServer* myServer) : bool VoxelSendThread::process() { uint64_t start = usecTimestampNow(); - Node* node = NodeList::getInstance()->nodeWithUUID(_nodeUUID); + // don't do any send processing until the initial load of the voxels is complete... + if (_myServer->isInitialLoadComplete()) { + Node* node = NodeList::getInstance()->nodeWithUUID(_nodeUUID); - if (node) { - node->lock(); // make sure the node list doesn't kill our node while we're using it - VoxelNodeData* nodeData = NULL; + if (node) { + node->lock(); // make sure the node list doesn't kill our node while we're using it + VoxelNodeData* nodeData = NULL; - nodeData = (VoxelNodeData*) node->getLinkedData(); + nodeData = (VoxelNodeData*) node->getLinkedData(); - int packetsSent = 0; + int packetsSent = 0; - // Sometimes the node data has not yet been linked, in which case we can't really do anything - if (nodeData) { - bool viewFrustumChanged = nodeData->updateCurrentViewFrustum(); - if (_myServer->wantsDebugVoxelSending()) { - printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged)); + // Sometimes the node data has not yet been linked, in which case we can't really do anything + if (nodeData) { + bool viewFrustumChanged = nodeData->updateCurrentViewFrustum(); + if (_myServer->wantsDebugVoxelSending()) { + printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged)); + } + packetsSent = deepestLevelVoxelDistributor(node, nodeData, viewFrustumChanged); } - packetsSent = deepestLevelVoxelDistributor(node, nodeData, viewFrustumChanged); + + node->unlock(); // we're done with this node for now. + } + } else { + if (_myServer->wantsDebugVoxelSending()) { + qDebug("VoxelSendThread::process() waiting for isInitialLoadComplete()\n"); } - - node->unlock(); // we're done with this node for now. } - + // dynamically sleep until we need to fire off the next set of voxels int elapsed = (usecTimestampNow() - start); int usecToSleep = VOXEL_SEND_INTERVAL_USECS - elapsed; @@ -117,8 +124,6 @@ int VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int& /// Version of voxel distributor that sends the deepest LOD level at once int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nodeData, bool viewFrustumChanged) { - _myServer->getServerTree().lockForRead(); - int truePacketsSent = 0; int trueBytesSent = 0; int packetsSentThisInterval = 0; @@ -289,10 +294,13 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod nodeData->getLastTimeBagEmpty(), isFullScene, &nodeData->stats, _myServer->getJurisdiction()); + + _myServer->getServerTree().lockForRead(); nodeData->stats.encodeStarted(); bytesWritten = _myServer->getServerTree().encodeTreeBitstream(subTree, _tempOutputBuffer, MAX_VOXEL_PACKET_SIZE - 1, nodeData->nodeBag, params); nodeData->stats.encodeStopped(); + _myServer->getServerTree().unlock(); if (nodeData->getAvailable() >= bytesWritten) { nodeData->writeToPacket(_tempOutputBuffer, bytesWritten); @@ -360,8 +368,6 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod } // end if bag wasn't empty, and so we sent stuff... - _myServer->getServerTree().unlock(); - return truePacketsSent; } diff --git a/libraries/voxel-server-library/src/VoxelServer.cpp b/libraries/voxel-server-library/src/VoxelServer.cpp index a5cb8f9620..88fed0e921 100644 --- a/libraries/voxel-server-library/src/VoxelServer.cpp +++ b/libraries/voxel-server-library/src/VoxelServer.cpp @@ -175,6 +175,47 @@ int VoxelServer::civetwebRequestHandler(struct mg_connection* connection) { mg_printf(connection, "%s", "\r\n"); mg_printf(connection, "%s", "\r\n"); + + // display voxel file load time + if (GetInstance()->isInitialLoadComplete()) { + tm* voxelsLoadedAtLocal = localtime(GetInstance()->getLoadCompleted()); + const int MAX_TIME_LENGTH = 128; + char buffer[MAX_TIME_LENGTH]; + strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", voxelsLoadedAtLocal); + mg_printf(connection, "Voxels Loaded At: %s", buffer); + + // Convert now to tm struct for UTC + tm* voxelsLoadedAtUTM = gmtime(GetInstance()->getLoadCompleted()); + if (gmtm != NULL) { + strftime(buffer, MAX_TIME_LENGTH, "%m/%d/%Y %X", voxelsLoadedAtUTM); + mg_printf(connection, " [%s UTM] ", buffer); + } + mg_printf(connection, "%s", "\r\n"); + + + uint64_t msecsElapsed = GetInstance()->getLoadElapsedTime() / USECS_PER_MSEC;; + float seconds = (msecsElapsed % MSECS_PER_MIN)/(float)MSECS_PER_SEC; + int minutes = (msecsElapsed/(MSECS_PER_MIN)) % MIN_PER_HOUR; + int hours = (msecsElapsed/(MSECS_PER_MIN * MIN_PER_HOUR)); + + mg_printf(connection, "%s", "Voxels Load Took: "); + if (hours > 0) { + mg_printf(connection, "%d hour%s ", hours, (hours > 1) ? "s" : "" ); + } + if (minutes > 0) { + mg_printf(connection, "%d minute%s ", minutes, (minutes > 1) ? "s" : ""); + } + if (seconds > 0) { + mg_printf(connection, "%.3f seconds", seconds); + } + mg_printf(connection, "%s", "\r\n"); + + } else { + mg_printf(connection, "%s", "Voxels not yet loaded...\r\n"); + } + + mg_printf(connection, "%s", "\r\n"); + mg_printf(connection, "%s", "\r\n"); mg_printf(connection, "%s", "Configuration: \r\n "); for (int i = 1; i < GetInstance()->_argc; i++) { @@ -512,14 +553,15 @@ void VoxelServer::run() { // loop to send to nodes requesting data while (true) { - if (NodeList::getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { + qDebug() << "Exit loop... getInstance()->getNumNoReplyDomainCheckIns() == MAX_SILENT_DOMAIN_SERVER_CHECK_INS\n"; break; } // send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) { gettimeofday(&lastDomainServerCheckIn, NULL); + //qDebug() << "VoxelServer::run()... NodeList::getInstance()->sendDomainServerCheckIn()\n"; NodeList::getInstance()->sendDomainServerCheckIn(); } @@ -569,26 +611,39 @@ void VoxelServer::run() { } } } + qDebug() << "VoxelServer::run()... AFTER loop...\n"; - delete _jurisdiction; + // call NodeList::clear() so that all of our node specific objects, including our sending threads, are + // properly shutdown and cleaned up. + NodeList::getInstance()->clear(); + qDebug() << "VoxelServer::run()... terminating _jurisdictionSender\n"; if (_jurisdictionSender) { _jurisdictionSender->terminate(); delete _jurisdictionSender; } + qDebug() << "VoxelServer::run()... terminating _voxelServerPacketProcessor\n"; if (_voxelServerPacketProcessor) { _voxelServerPacketProcessor->terminate(); delete _voxelServerPacketProcessor; } + qDebug() << "VoxelServer::run()... terminating _voxelPersistThread\n"; if (_voxelPersistThread) { _voxelPersistThread->terminate(); delete _voxelPersistThread; } // tell our NodeList we're done with notifications + qDebug() << "VoxelServer::run()... nodeList->removeHook(&_nodeWatcher)\n"; nodeList->removeHook(&_nodeWatcher); + + qDebug() << "VoxelServer::run()... deleting _jurisdiction\n"; + delete _jurisdiction; + _jurisdiction = NULL; + + qDebug() << "VoxelServer::run()... DONE\n"; } diff --git a/libraries/voxel-server-library/src/VoxelServer.h b/libraries/voxel-server-library/src/VoxelServer.h index f938bebd7e..92590489f8 100644 --- a/libraries/voxel-server-library/src/VoxelServer.h +++ b/libraries/voxel-server-library/src/VoxelServer.h @@ -55,6 +55,10 @@ public: static VoxelServer* GetInstance() { return _theInstance; } + bool isInitialLoadComplete() const { return (_voxelPersistThread) ? _voxelPersistThread->isInitialLoadComplete() : true; } + time_t* getLoadCompleted() { return (_voxelPersistThread) ? _voxelPersistThread->getLoadCompleted() : NULL; } + uint64_t getLoadElapsedTime() const { return (_voxelPersistThread) ? _voxelPersistThread->getLoadElapsedTime() : 0; } + private: int _argc; const char** _argv; diff --git a/libraries/voxel-server-library/src/VoxelServerPacketProcessor.cpp b/libraries/voxel-server-library/src/VoxelServerPacketProcessor.cpp index 5fd2028f50..672c4f0d50 100644 --- a/libraries/voxel-server-library/src/VoxelServerPacketProcessor.cpp +++ b/libraries/voxel-server-library/src/VoxelServerPacketProcessor.cpp @@ -85,7 +85,7 @@ void VoxelServerPacketProcessor::processPacket(sockaddr& senderAddress, unsigned printf("inserting voxel: %f,%f,%f r=%d,g=%d,b=%d\n", vertices[0], vertices[1], vertices[2], red, green, blue); delete[] vertices; } - + _myServer->getServerTree().lockForWrite(); _myServer->getServerTree().readCodeColorBufferToTree(voxelData, destructive); _myServer->getServerTree().unlock(); diff --git a/libraries/voxels/src/VoxelQuery.cpp b/libraries/voxels/src/VoxelQuery.cpp index 463b806eba..9b5d65e0c5 100644 --- a/libraries/voxels/src/VoxelQuery.cpp +++ b/libraries/voxels/src/VoxelQuery.cpp @@ -42,6 +42,7 @@ VoxelQuery::VoxelQuery(Node* owningNode) : } VoxelQuery::~VoxelQuery() { + qDebug("VoxelQuery::~VoxelQuery()\n"); } int VoxelQuery::getBroadcastData(unsigned char* destinationBuffer) { diff --git a/libraries/voxels/src/VoxelQuery.h b/libraries/voxels/src/VoxelQuery.h index bedbfdac4e..c95b632eef 100644 --- a/libraries/voxels/src/VoxelQuery.h +++ b/libraries/voxels/src/VoxelQuery.h @@ -35,7 +35,7 @@ class VoxelQuery : public NodeData { public: VoxelQuery(Node* owningNode = NULL); - ~VoxelQuery(); + virtual ~VoxelQuery(); int getBroadcastData(unsigned char* destinationBuffer); int parseData(unsigned char* sourceBuffer, int numBytes); diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index cfe7828c08..e0e1ac5367 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -1806,9 +1806,10 @@ void VoxelTree::writeToSVOFile(const char* fileName, VoxelNode* node) { while (!nodeBag.isEmpty()) { VoxelNode* subTree = nodeBag.extract(); + lockForRead(); // do tree locking down here so that we have shorter slices and less thread contention EncodeBitstreamParams params(INT_MAX, IGNORE_VIEW_FRUSTUM, WANT_COLOR, NO_EXISTS_BITS); bytesWritten = encodeTreeBitstream(subTree, &outputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1, nodeBag, params); - + unlock(); file.write((const char*)&outputBuffer[0], bytesWritten); } } diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index f9a5c38eb9..f8e0458fa1 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -189,7 +189,9 @@ public: // VoxelTree does not currently handle its own locking, caller must use these to lock/unlock void lockForRead() { lock.lockForRead(); } + void tryLockForRead() { lock.tryLockForRead(); } void lockForWrite() { lock.lockForWrite(); } + void tryLockForWrite() { lock.tryLockForWrite(); } void unlock() { lock.unlock(); } unsigned long getVoxelCount(); From 77ba88e56dbfaae9157e761c4d8bf234de8f9581 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 7 Nov 2013 13:06:57 -0800 Subject: [PATCH 13/17] removed dead comment --- libraries/voxel-server-library/src/VoxelServer.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/voxel-server-library/src/VoxelServer.cpp b/libraries/voxel-server-library/src/VoxelServer.cpp index 88fed0e921..de421341f3 100644 --- a/libraries/voxel-server-library/src/VoxelServer.cpp +++ b/libraries/voxel-server-library/src/VoxelServer.cpp @@ -561,7 +561,6 @@ void VoxelServer::run() { // send a check in packet to the domain server if DOMAIN_SERVER_CHECK_IN_USECS has elapsed if (usecTimestampNow() - usecTimestamp(&lastDomainServerCheckIn) >= DOMAIN_SERVER_CHECK_IN_USECS) { gettimeofday(&lastDomainServerCheckIn, NULL); - //qDebug() << "VoxelServer::run()... NodeList::getInstance()->sendDomainServerCheckIn()\n"; NodeList::getInstance()->sendDomainServerCheckIn(); } From 64d5ccf91e7e29dc1d43a4a7450d8feac9d6f98a Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 7 Nov 2013 14:10:09 -0800 Subject: [PATCH 14/17] add menu option for voxel server fade in/out and dont remove local voxels when server shuts down --- interface/src/Application.cpp | 24 ++++++++++++++---------- interface/src/Menu.cpp | 1 + interface/src/Menu.h | 1 + interface/src/VoxelSystem.cpp | 15 --------------- 4 files changed, 16 insertions(+), 25 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 46be485fc5..fbf383cc6f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4094,11 +4094,13 @@ void Application::nodeKilled(Node* node) { rootDetails.x, rootDetails.y, rootDetails.z, rootDetails.s); // Add the jurisditionDetails object to the list of "fade outs" - VoxelFade fade(VoxelFade::FADE_OUT, NODE_KILLED_RED, NODE_KILLED_GREEN, NODE_KILLED_BLUE); - fade.voxelDetails = rootDetails; - const float slightly_smaller = 0.99; - fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller; - _voxelFades.push_back(fade); + if (!Menu::getInstance()->isOptionChecked(MenuOption::DontFadeOnVoxelServerChanges)) { + VoxelFade fade(VoxelFade::FADE_OUT, NODE_KILLED_RED, NODE_KILLED_GREEN, NODE_KILLED_BLUE); + fade.voxelDetails = rootDetails; + const float slightly_smaller = 0.99; + fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller; + _voxelFades.push_back(fade); + } } } else if (node->getLinkedData() == _lookatTargetAvatar) { _lookatTargetAvatar = NULL; @@ -4127,11 +4129,13 @@ int Application::parseVoxelStats(unsigned char* messageData, ssize_t messageLeng rootDetails.x, rootDetails.y, rootDetails.z, rootDetails.s); // Add the jurisditionDetails object to the list of "fade outs" - VoxelFade fade(VoxelFade::FADE_OUT, NODE_ADDED_RED, NODE_ADDED_GREEN, NODE_ADDED_BLUE); - fade.voxelDetails = rootDetails; - const float slightly_smaller = 0.99; - fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller; - _voxelFades.push_back(fade); + if (!Menu::getInstance()->isOptionChecked(MenuOption::DontFadeOnVoxelServerChanges)) { + VoxelFade fade(VoxelFade::FADE_OUT, NODE_ADDED_RED, NODE_ADDED_GREEN, NODE_ADDED_BLUE); + fade.voxelDetails = rootDetails; + const float slightly_smaller = 0.99; + fade.voxelDetails.s = fade.voxelDetails.s * slightly_smaller; + _voxelFades.push_back(fade); + } } // store jurisdiction details for later use // This is bit of fiddling is because JurisdictionMap assumes it is the owner of the values used to construct it diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 92c6bbf63b..3d1b7b7bf1 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -283,6 +283,7 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::VoxelTextures); addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::AmbientOcclusion); + addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::DontFadeOnVoxelServerChanges); addActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::LodTools, Qt::SHIFT | Qt::Key_L, this, SLOT(lodTools())); QMenu* cullingOptionsMenu = voxelOptionsMenu->addMenu("Culling Options"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 987d7ff452..30ef055447 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -165,6 +165,7 @@ namespace MenuOption { const QString EchoAudio = "Echo Audio"; const QString ExportVoxels = "Export Voxels"; const QString ExtraDebugging = "Extra Debugging"; + const QString DontFadeOnVoxelServerChanges = "Don't Fade In/Out on Voxel Server Changes"; const QString HeadMouse = "Head Mouse"; const QString FaceMode = "Cycle Face Mode"; const QString FaceshiftTCP = "Faceshift (TCP)"; diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 1219548b4d..62419565c6 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -2645,23 +2645,8 @@ bool VoxelSystem::killSourceVoxelsOperation(VoxelNode* node, void* extraData) { void VoxelSystem::nodeKilled(Node* node) { if (node->getType() == NODE_TYPE_VOXEL_SERVER) { _voxelServerCount--; - QUuid nodeUUID = node->getUUID(); - qDebug("VoxelSystem... voxel server %s removed...\n", nodeUUID.toString().toLocal8Bit().constData()); - - if (_voxelServerCount > 0) { - // Kill any voxels from the local tree that match this nodeID - // commenting out for removal of 16 bit node IDs - lockTree(); - _tree->recurseTreeWithOperation(killSourceVoxelsOperation, &nodeUUID); - unlockTree(); - _tree->setDirtyBit(); - setupNewVoxelsForDrawing(); - } else { - // Last server, take the easy way and kill all the local voxels! - killLocalVoxels(); - } } } From 9aacda7d56ce20f2de9ade4532398a1ecdf77726 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 7 Nov 2013 14:34:01 -0800 Subject: [PATCH 15/17] fix for AC lack of QCoreApplication --- assignment-client/src/main.cpp | 2 ++ libraries/shared/src/Node.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/assignment-client/src/main.cpp b/assignment-client/src/main.cpp index 728bcb3c00..10b32f901f 100644 --- a/assignment-client/src/main.cpp +++ b/assignment-client/src/main.cpp @@ -38,6 +38,8 @@ int argc = 0; char** argv = NULL; void childClient() { + QCoreApplication(::argc, ::argv); + // set the logging target to the the CHILD_TARGET_NAME Logging::setTargetName(CHILD_TARGET_NAME); diff --git a/libraries/shared/src/Node.cpp b/libraries/shared/src/Node.cpp index 205d3109a5..dcdc0eacca 100644 --- a/libraries/shared/src/Node.cpp +++ b/libraries/shared/src/Node.cpp @@ -45,6 +45,8 @@ Node::~Node() { delete _localSocket; if (QCoreApplication::instance()) { + // even if we have a QCoreApplication instance we don't get here unless it's been exec'ed + // which is only currently the case for interface if (_linkedData) { _linkedData->deleteLater(); } From ce52395a083c23cb68aa5b642f740f7fd82317ac Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 7 Nov 2013 15:06:11 -0800 Subject: [PATCH 16/17] actually create the QCoreApplication instance --- assignment-client/src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/main.cpp b/assignment-client/src/main.cpp index 10b32f901f..6f8cd83784 100644 --- a/assignment-client/src/main.cpp +++ b/assignment-client/src/main.cpp @@ -38,7 +38,7 @@ int argc = 0; char** argv = NULL; void childClient() { - QCoreApplication(::argc, ::argv); + QCoreApplication application(::argc, ::argv); // set the logging target to the the CHILD_TARGET_NAME Logging::setTargetName(CHILD_TARGET_NAME); From 5889d4122f83396026285536ee316561aa666601 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 7 Nov 2013 15:41:10 -0800 Subject: [PATCH 17/17] use virtual deleteOrDeleteLater in NodeData to handle AvatarVoxelSystem --- interface/src/avatar/Avatar.cpp | 5 +++++ interface/src/avatar/Avatar.h | 1 + libraries/shared/src/Node.cpp | 11 ++--------- libraries/shared/src/NodeData.cpp | 4 ++++ libraries/shared/src/NodeData.h | 2 ++ 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 31e1ee43fb..2359ea2460 100755 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -255,6 +255,11 @@ Avatar::~Avatar() { delete _balls; } +void Avatar::deleteOrDeleteLater() { + this->deleteLater(); +} + + void Avatar::init() { _head.init(); _hand.init(); diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index b3c373009f..4258ce6f14 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -134,6 +134,7 @@ public: Avatar(Node* owningNode = NULL); ~Avatar(); + void deleteOrDeleteLater(); void init(); void simulate(float deltaTime, Transmitter* transmitter); diff --git a/libraries/shared/src/Node.cpp b/libraries/shared/src/Node.cpp index dcdc0eacca..d4bd8a084a 100644 --- a/libraries/shared/src/Node.cpp +++ b/libraries/shared/src/Node.cpp @@ -21,7 +21,6 @@ #include "SharedUtil.h" #include "UDPSocket.h" -#include #include Node::Node(const QUuid& uuid, char type, sockaddr* publicSocket, sockaddr* localSocket) : @@ -44,14 +43,8 @@ Node::~Node() { delete _publicSocket; delete _localSocket; - if (QCoreApplication::instance()) { - // even if we have a QCoreApplication instance we don't get here unless it's been exec'ed - // which is only currently the case for interface - if (_linkedData) { - _linkedData->deleteLater(); - } - } else { - delete _linkedData; + if (_linkedData) { + _linkedData->deleteOrDeleteLater(); } delete _bytesReceivedMovingAverage; diff --git a/libraries/shared/src/NodeData.cpp b/libraries/shared/src/NodeData.cpp index 45e8357d4f..6ab7f2242b 100644 --- a/libraries/shared/src/NodeData.cpp +++ b/libraries/shared/src/NodeData.cpp @@ -16,4 +16,8 @@ NodeData::NodeData(Node* owningNode) : NodeData::~NodeData() { +} + +void NodeData::deleteOrDeleteLater() { + delete this; } \ No newline at end of file diff --git a/libraries/shared/src/NodeData.h b/libraries/shared/src/NodeData.h index e92656f977..0a2ba3b514 100644 --- a/libraries/shared/src/NodeData.h +++ b/libraries/shared/src/NodeData.h @@ -21,6 +21,8 @@ public: virtual ~NodeData() = 0; virtual int parseData(unsigned char* sourceBuffer, int numBytes) = 0; + virtual void deleteOrDeleteLater(); + Node* getOwningNode() { return _owningNode; } protected: Node* _owningNode;