summaryrefslogtreecommitdiff
path: root/indra/llplugin/llpluginprocessparent.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llplugin/llpluginprocessparent.cpp')
-rwxr-xr-xindra/llplugin/llpluginprocessparent.cpp308
1 files changed, 196 insertions, 112 deletions
diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp
index b5a2588e1e..0a8e58ac90 100755
--- 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()