diff options
author | Oz Linden <oz@lindenlab.com> | 2016-01-15 16:55:04 -0500 |
---|---|---|
committer | Oz Linden <oz@lindenlab.com> | 2016-01-15 16:55:04 -0500 |
commit | bc22e587439a2822e5228508e7a4a1e2ec0121c5 (patch) | |
tree | 442540d0c2760617a2c0cf0cb9ba1973ab6fb298 /indra/llplugin/llpluginprocessparent.cpp | |
parent | 317644f5d575ecd4e734c9a5327f2a062ebf914a (diff) | |
parent | 5a5c023e291990a463b1a91846ce82c70da8daab (diff) |
merge changes for 4.0.1-release
Diffstat (limited to 'indra/llplugin/llpluginprocessparent.cpp')
-rw-r--r-- | indra/llplugin/llpluginprocessparent.cpp | 308 |
1 files changed, 196 insertions, 112 deletions
diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp index b5a2588e1e..0a8e58ac90 100644 --- a/indra/llplugin/llpluginprocessparent.cpp +++ b/indra/llplugin/llpluginprocessparent.cpp @@ -46,7 +46,7 @@ bool LLPluginProcessParent::sUseReadThread = false; apr_pollset_t *LLPluginProcessParent::sPollSet = NULL; bool LLPluginProcessParent::sPollsetNeedsRebuild = false; LLMutex *LLPluginProcessParent::sInstancesMutex; -std::list<LLPluginProcessParent*> LLPluginProcessParent::sInstances; +LLPluginProcessParent::mapInstances_t LLPluginProcessParent::sInstances; LLThread *LLPluginProcessParent::sReadThread = NULL; @@ -104,27 +104,12 @@ LLPluginProcessParent::LLPluginProcessParent(LLPluginProcessParentOwner *owner): // Don't start the timer here -- start it when we actually launch the plugin process. mHeartbeat.stop(); - // Don't add to the global list until fully constructed. - { - LLMutexLock lock(sInstancesMutex); - sInstances.push_back(this); - } } LLPluginProcessParent::~LLPluginProcessParent() { LL_DEBUGS("Plugin") << "destructor" << LL_ENDL; - // Remove from the global list before beginning destruction. - { - // Make sure to get the global mutex _first_ here, to avoid a possible deadlock against LLPluginProcessParent::poll() - LLMutexLock lock(sInstancesMutex); - { - LLMutexLock lock2(&mIncomingQueueMutex); - sInstances.remove(this); - } - } - // Destroy any remaining shared memory regions sharedMemoryRegionsType::iterator iter; while((iter = mSharedMemoryRegions.begin()) != mSharedMemoryRegions.end()) @@ -139,9 +124,109 @@ LLPluginProcessParent::~LLPluginProcessParent() } LLProcess::kill(mProcess); - killSockets(); + if (!LLApp::isQuitting()) + { // If we are quitting, the network sockets will already have been destroyed. + killSockets(); + } +} + +/*static*/ +LLPluginProcessParent::ptr_t LLPluginProcessParent::create(LLPluginProcessParentOwner *owner) +{ + ptr_t that(new LLPluginProcessParent(owner)); + + // Don't add to the global list until fully constructed. + { + LLMutexLock lock(sInstancesMutex); + sInstances.insert(mapInstances_t::value_type(that.get(), that)); + } + + return that; +} + +/*static*/ +void LLPluginProcessParent::shutdown() +{ + LLMutexLock lock(sInstancesMutex); + + mapInstances_t::iterator it; + for (it = sInstances.begin(); it != sInstances.end(); ++it) + { + (*it).second->setState(STATE_GOODBYE); + (*it).second->idle(); + } + sInstances.clear(); +} + + +void LLPluginProcessParent::requestShutdown() +{ + setState(STATE_GOODBYE); + mOwner = NULL; + + if (LLApp::isQuitting()) + { // if we're quitting, run the idle once more + idle(); + removeFromProcessing(); + return; + } + + static uint32_t count = 0; + std::stringstream namestream; + + namestream << "LLPluginProcessParentListener" << ++count; + + //*HACK!*// + // After requestShutdown has been called our previous owner will no longer call + // our idle() method. Tie into the event loop here to do that until we are good + // and finished. + LL_DEBUGS("LLPluginProcessParent") << "listening on \"mainloop\"" << LL_ENDL; + mPolling = LLEventPumps::instance().obtain("mainloop") + .listen(namestream.str(), boost::bind(&LLPluginProcessParent::pollTick, this)); + +} + +bool LLPluginProcessParent::pollTick() +{ + if (isDone()) + { + ptr_t that; + { + // this grabs a copy of the smart pointer to ourselves to ensure that we do not + // get destroyed until after this method returns. + LLMutexLock lock(sInstancesMutex); + mapInstances_t::iterator it = sInstances.find(this); + if (it != sInstances.end()) + that = (*it).second; + } + + removeFromProcessing(); + return true; + } + + idle(); + return false; } +void LLPluginProcessParent::removeFromProcessing() +{ + // Remove from the global list before beginning destruction. + { + // Make sure to get the global mutex _first_ here, to avoid a possible deadlock against LLPluginProcessParent::poll() + LLMutexLock lock(sInstancesMutex); + { + LLMutexLock lock2(&mIncomingQueueMutex); + sInstances.erase(this); + } + } +} + +bool LLPluginProcessParent::wantsPolling() const +{ + return (mPollFD.client_data && (mState != STATE_DONE)); +} + + void LLPluginProcessParent::killSockets(void) { { @@ -371,48 +456,48 @@ void LLPluginProcessParent::idle(void) break; case STATE_LISTENING: - { - // Launch the plugin process. + { + // Launch the plugin process. - // Only argument to the launcher is the port number we're listening on - mProcessParams.args.add(stringize(mBoundPort)); - if (! (mProcess = LLProcess::create(mProcessParams))) - { - errorState(); - } - else - { - if(mDebug) - { - #if LL_DARWIN - // If we're set to debug, start up a gdb instance in a new terminal window and have it attach to the plugin process and continue. + // Only argument to the launcher is the port number we're listening on + mProcessParams.args.add(stringize(mBoundPort)); + if (! (mProcess = LLProcess::create(mProcessParams))) + { + errorState(); + } + else + { + if(mDebug) + { +#if LL_DARWIN + // If we're set to debug, start up a gdb instance in a new terminal window and have it attach to the plugin process and continue. - // The command we're constructing would look like this on the command line: - // osascript -e 'tell application "Terminal"' -e 'set win to do script "gdb -pid 12345"' -e 'do script "continue" in win' -e 'end tell' - - LLProcess::Params params; - params.executable = "/usr/bin/osascript"; - params.args.add("-e"); - params.args.add("tell application \"Terminal\""); - params.args.add("-e"); - params.args.add(STRINGIZE("set win to do script \"gdb -pid " - << mProcess->getProcessID() << "\"")); - params.args.add("-e"); - params.args.add("do script \"continue\" in win"); - params.args.add("-e"); - params.args.add("end tell"); - mDebugger = LLProcess::create(params); - - #endif - } + // The command we're constructing would look like this on the command line: + // osascript -e 'tell application "Terminal"' -e 'set win to do script "gdb -pid 12345"' -e 'do script "continue" in win' -e 'end tell' + + LLProcess::Params params; + params.executable = "/usr/bin/osascript"; + params.args.add("-e"); + params.args.add("tell application \"Terminal\""); + params.args.add("-e"); + params.args.add(STRINGIZE("set win to do script \"gdb -pid " + << mProcess->getProcessID() << "\"")); + params.args.add("-e"); + params.args.add("do script \"continue\" in win"); + params.args.add("-e"); + params.args.add("end tell"); + mDebugger = LLProcess::create(params); + +#endif + } - // This will allow us to time out if the process never starts. - mHeartbeat.start(); - mHeartbeat.setTimerExpirySec(mPluginLaunchTimeout); - setState(STATE_LAUNCHED); - } - } - break; + // This will allow us to time out if the process never starts. + mHeartbeat.start(); + mHeartbeat.setTimerExpirySec(mPluginLaunchTimeout); + setState(STATE_LAUNCHED); + } + } + break; case STATE_LAUNCHED: // waiting for the plugin to connect @@ -430,7 +515,7 @@ void LLPluginProcessParent::idle(void) setState(STATE_CONNECTED); } } - break; + break; case STATE_CONNECTED: // waiting for hello message from the plugin @@ -439,7 +524,7 @@ void LLPluginProcessParent::idle(void) { errorState(); } - break; + break; case STATE_HELLO: LL_DEBUGS("Plugin") << "received hello message" << LL_ENDL; @@ -453,7 +538,7 @@ void LLPluginProcessParent::idle(void) } setState(STATE_LOADING); - break; + break; case STATE_LOADING: // The load_plugin_response message will kick us from here into STATE_RUNNING @@ -461,15 +546,23 @@ void LLPluginProcessParent::idle(void) { errorState(); } - break; + break; case STATE_RUNNING: if(pluginLockedUpOrQuit()) { errorState(); } - break; + break; + case STATE_GOODBYE: + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "shutdown_plugin"); + sendMessage(message); + } + setState(STATE_EXITING); + break; + case STATE_EXITING: if (! LLProcess::isRunning(mProcess)) { @@ -480,7 +573,7 @@ void LLPluginProcessParent::idle(void) LL_WARNS("Plugin") << "timeout in exiting state, bailing out" << LL_ENDL; errorState(); } - break; + break; case STATE_LAUNCH_FAILURE: if(mOwner != NULL) @@ -488,7 +581,7 @@ void LLPluginProcessParent::idle(void) mOwner->pluginLaunchFailed(); } setState(STATE_CLEANUP); - break; + break; case STATE_ERROR: if(mOwner != NULL) @@ -496,19 +589,18 @@ void LLPluginProcessParent::idle(void) mOwner->pluginDied(); } setState(STATE_CLEANUP); - break; + break; case STATE_CLEANUP: LLProcess::kill(mProcess); killSockets(); setState(STATE_DONE); - break; - + dirtyPollSet(); + break; case STATE_DONE: // just sit here. - break; - + break; } } while (idle_again); @@ -651,14 +743,14 @@ void LLPluginProcessParent::updatePollset() sPollSet = NULL; } - std::list<LLPluginProcessParent*>::iterator iter; + mapInstances_t::iterator iter; int count = 0; // Count the number of instances that want to be in the pollset for(iter = sInstances.begin(); iter != sInstances.end(); iter++) { - (*iter)->mPolledInput = false; - if((*iter)->mPollFD.client_data) + (*iter).second->mPolledInput = false; + if ((*iter).second->wantsPolling()) { // This instance has a socket that needs to be polled. ++count; @@ -686,12 +778,12 @@ void LLPluginProcessParent::updatePollset() // Pollset was created, add all instances to it. for(iter = sInstances.begin(); iter != sInstances.end(); iter++) { - if((*iter)->mPollFD.client_data) + if ((*iter).second->wantsPolling()) { - status = apr_pollset_add(sPollSet, &((*iter)->mPollFD)); + status = apr_pollset_add(sPollSet, &((*iter).second->mPollFD)); if(status == APR_SUCCESS) { - (*iter)->mPolledInput = true; + (*iter).second->mPolledInput = true; } else { @@ -756,45 +848,27 @@ void LLPluginProcessParent::poll(F64 timeout) if(status == APR_SUCCESS) { // One or more of the descriptors signalled. Call them. - for(int i = 0; i < count; i++) - { - LLPluginProcessParent *self = (LLPluginProcessParent *)(descriptors[i].client_data); - // NOTE: the descriptor returned here is actually a COPY of the original (even though we create the pollset with APR_POLLSET_NOCOPY). - // This means that even if the parent has set its mPollFD.client_data to NULL, the old pointer may still there in this descriptor. - // It's even possible that the old pointer no longer points to a valid LLPluginProcessParent. - // This means that we can't safely dereference the 'self' pointer here without some extra steps... - if(self) - { - // Make sure this pointer is still in the instances list - bool valid = false; - { - LLMutexLock lock(sInstancesMutex); - for(std::list<LLPluginProcessParent*>::iterator iter = sInstances.begin(); iter != sInstances.end(); ++iter) - { - if(*iter == self) - { - // Lock the instance's mutex before unlocking the global mutex. - // This avoids a possible race condition where the instance gets deleted between this check and the servicePoll() call. - self->mIncomingQueueMutex.lock(); - valid = true; - break; - } - } - } - - if(valid) - { - // The instance is still valid. - // Pull incoming messages off the socket - self->servicePoll(); - self->mIncomingQueueMutex.unlock(); - } - else - { - LL_DEBUGS("PluginPoll") << "detected deleted instance " << self << LL_ENDL; - } + for (int i = 0; i < count; i++) + { + void *thatId = descriptors[i].client_data; + + ptr_t that; + mapInstances_t::iterator it; + + { + LLMutexLock lock(sInstancesMutex); + it = sInstances.find(thatId); + if (it != sInstances.end()) + that = (*it).second; + } + + if (that) + { + that->mIncomingQueueMutex.lock(); + that->servicePoll(); + that->mIncomingQueueMutex.unlock(); + } - } } } else if(APR_STATUS_IS_TIMEUP(status)) @@ -812,6 +886,16 @@ void LLPluginProcessParent::poll(F64 timeout) LL_WARNS("PluginPoll") << "apr_pollset_poll failed with status " << status << LL_ENDL; } } + + // Remove instances in the done state from the sInstances map. + mapInstances_t::iterator itClean = sInstances.begin(); + while (itClean != sInstances.end()) + { + if ((*itClean).second->isDone()) + sInstances.erase(itClean++); + else + ++itClean; + } } void LLPluginProcessParent::servicePoll() |