diff options
Diffstat (limited to 'indra/llplugin/llpluginprocessparent.cpp')
-rw-r--r-- | indra/llplugin/llpluginprocessparent.cpp | 124 |
1 files changed, 87 insertions, 37 deletions
diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp index 41784a713c..895c858979 100644 --- a/indra/llplugin/llpluginprocessparent.cpp +++ b/indra/llplugin/llpluginprocessparent.cpp @@ -2,6 +2,7 @@ * @file llpluginprocessparent.cpp * @brief LLPluginProcessParent handles the parent side of the external-process plugin API. * + * @cond * $LicenseInfo:firstyear=2008&license=viewergpl$ * * Copyright (c) 2008, Linden Research, Inc. @@ -27,6 +28,7 @@ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ + * @endcond */ #include "linden_common.h" @@ -37,12 +39,6 @@ #include "llapr.h" -// If we don't receive a heartbeat in this many seconds, we declare the plugin locked up. -static const F32 PLUGIN_LOCKED_UP_SECONDS = 15.0f; - -// Somewhat longer timeout for initial launch. -static const F32 PLUGIN_LAUNCH_SECONDS = 20.0f; - //virtual LLPluginProcessParentOwner::~LLPluginProcessParentOwner() { @@ -54,7 +50,16 @@ LLPluginProcessParent::LLPluginProcessParent(LLPluginProcessParentOwner *owner) mOwner = owner; mBoundPort = 0; mState = STATE_UNINITIALIZED; + mSleepTime = 0.0; + mCPUUsage = 0.0; mDisableTimeout = false; + mDebug = false; + + mPluginLaunchTimeout = 60.0f; + mPluginLockupTimeout = 15.0f; + + // Don't start the timer here -- start it when we actually launch the plugin process. + mHeartbeat.stop(); } LLPluginProcessParent::~LLPluginProcessParent() @@ -71,8 +76,10 @@ LLPluginProcessParent::~LLPluginProcessParent() // and remove it from our map mSharedMemoryRegions.erase(iter); } - - mProcess.kill(); + + // orphaning the process means it won't be killed when the LLProcessLauncher is destructed. + // This is what we want -- it should exit cleanly once it notices the sockets have been closed. + mProcess.orphan(); killSockets(); } @@ -83,12 +90,20 @@ void LLPluginProcessParent::killSockets(void) mSocket.reset(); } -void LLPluginProcessParent::init(const std::string &launcher_filename, const std::string &plugin_filename) +void LLPluginProcessParent::errorState(void) +{ + if(mState < STATE_RUNNING) + setState(STATE_LAUNCH_FAILURE); + else + setState(STATE_ERROR); +} + +void LLPluginProcessParent::init(const std::string &launcher_filename, const std::string &plugin_filename, bool debug) { mProcess.setExecutable(launcher_filename); mPluginFile = plugin_filename; mCPUUsage = 0.0f; - + mDebug = debug; setState(STATE_INITIALIZED); } @@ -132,7 +147,7 @@ bool LLPluginProcessParent::accept() ll_apr_warn_status(status); // Some other error. - setState(STATE_ERROR); + errorState(); } return result; @@ -150,15 +165,15 @@ void LLPluginProcessParent::idle(void) if(!mMessagePipe->pump()) { // LL_WARNS("Plugin") << "Message pipe hit an error state" << LL_ENDL; - setState(STATE_ERROR); + errorState(); } } - if((mSocketError != APR_SUCCESS) && (mState < STATE_ERROR)) + if((mSocketError != APR_SUCCESS) && (mState <= STATE_RUNNING)) { // The socket is in an error state -- the plugin is gone. LL_WARNS("Plugin") << "Socket hit an error state (" << mSocketError << ")" << LL_ENDL; - setState(STATE_ERROR); + errorState(); } // If a state needs to go directly to another state (as a performance enhancement), it can set idle_again to true after calling setState(). @@ -191,7 +206,7 @@ void LLPluginProcessParent::idle(void) if(ll_apr_warn_status(status)) { killSockets(); - setState(STATE_ERROR); + errorState(); break; } @@ -202,7 +217,7 @@ void LLPluginProcessParent::idle(void) if(ll_apr_warn_status(status)) { killSockets(); - setState(STATE_ERROR); + errorState(); break; } @@ -212,7 +227,7 @@ void LLPluginProcessParent::idle(void) if(ll_apr_warn_status(apr_socket_addr_get(&bound_addr, APR_LOCAL, mListenSocket->getSocket()))) { killSockets(); - setState(STATE_ERROR); + errorState(); break; } mBoundPort = bound_addr->port; @@ -222,7 +237,7 @@ void LLPluginProcessParent::idle(void) LL_WARNS("Plugin") << "Bound port number unknown, bailing out." << LL_ENDL; killSockets(); - setState(STATE_ERROR); + errorState(); break; } } @@ -234,7 +249,7 @@ void LLPluginProcessParent::idle(void) if(ll_apr_warn_status(status)) { killSockets(); - setState(STATE_ERROR); + errorState(); break; } @@ -242,7 +257,7 @@ void LLPluginProcessParent::idle(void) if(ll_apr_warn_status(status)) { killSockets(); - setState(STATE_ERROR); + errorState(); break; } @@ -255,7 +270,7 @@ void LLPluginProcessParent::idle(void) if(ll_apr_warn_status(status)) { killSockets(); - setState(STATE_ERROR); + errorState(); break; } @@ -274,13 +289,38 @@ void LLPluginProcessParent::idle(void) mProcess.addArgument(stream.str()); if(mProcess.launch() != 0) { - setState(STATE_ERROR); + 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' + + std::stringstream cmd; + + mDebugger.setExecutable("/usr/bin/osascript"); + mDebugger.addArgument("-e"); + mDebugger.addArgument("tell application \"Terminal\""); + mDebugger.addArgument("-e"); + cmd << "set win to do script \"gdb -pid " << mProcess.getProcessID() << "\""; + mDebugger.addArgument(cmd.str()); + mDebugger.addArgument("-e"); + mDebugger.addArgument("do script \"continue\" in win"); + mDebugger.addArgument("-e"); + mDebugger.addArgument("end tell"); + mDebugger.launch(); + + #endif + } + // This will allow us to time out if the process never starts. mHeartbeat.start(); - mHeartbeat.setTimerExpirySec(PLUGIN_LAUNCH_SECONDS); + mHeartbeat.setTimerExpirySec(mPluginLaunchTimeout); setState(STATE_LAUNCHED); } } @@ -290,7 +330,7 @@ void LLPluginProcessParent::idle(void) // waiting for the plugin to connect if(pluginLockedUpOrQuit()) { - setState(STATE_ERROR); + errorState(); } else { @@ -309,7 +349,7 @@ void LLPluginProcessParent::idle(void) if(pluginLockedUpOrQuit()) { - setState(STATE_ERROR); + errorState(); } break; @@ -330,14 +370,14 @@ void LLPluginProcessParent::idle(void) // The load_plugin_response message will kick us from here into STATE_RUNNING if(pluginLockedUpOrQuit()) { - setState(STATE_ERROR); + errorState(); } break; case STATE_RUNNING: if(pluginLockedUpOrQuit()) { - setState(STATE_ERROR); + errorState(); } break; @@ -349,8 +389,16 @@ void LLPluginProcessParent::idle(void) else if(pluginLockedUp()) { LL_WARNS("Plugin") << "timeout in exiting state, bailing out" << llendl; - setState(STATE_ERROR); + errorState(); + } + break; + + case STATE_LAUNCH_FAILURE: + if(mOwner != NULL) + { + mOwner->pluginLaunchFailed(); } + setState(STATE_CLEANUP); break; case STATE_ERROR: @@ -362,7 +410,8 @@ void LLPluginProcessParent::idle(void) break; case STATE_CLEANUP: - mProcess.kill(); + // Don't do a kill here anymore -- closing the sockets is the new 'kill'. + mProcess.orphan(); killSockets(); setState(STATE_DONE); break; @@ -467,7 +516,7 @@ void LLPluginProcessParent::receiveMessage(const LLPluginMessage &message) else { LL_WARNS("Plugin") << "received hello message in wrong state -- bailing out" << LL_ENDL; - setState(STATE_ERROR); + errorState(); } } @@ -477,6 +526,9 @@ void LLPluginProcessParent::receiveMessage(const LLPluginMessage &message) { // Plugin has been loaded. + mPluginVersionString = message.getValue("plugin_version"); + LL_INFOS("Plugin") << "plugin version string: " << mPluginVersionString << LL_ENDL; + // Check which message classes/versions the plugin supports. // TODO: check against current versions // TODO: kill plugin on major mismatches? @@ -487,8 +539,6 @@ void LLPluginProcessParent::receiveMessage(const LLPluginMessage &message) LL_INFOS("Plugin") << "message class: " << iter->first << " -> version: " << iter->second.asString() << LL_ENDL; } - mPluginVersionString = message.getValue("plugin_version"); - // Send initial sleep time setSleepTime(mSleepTime, true); @@ -497,13 +547,13 @@ void LLPluginProcessParent::receiveMessage(const LLPluginMessage &message) else { LL_WARNS("Plugin") << "received load_plugin_response message in wrong state -- bailing out" << LL_ENDL; - setState(STATE_ERROR); + errorState(); } } else if(message_name == "heartbeat") { // this resets our timer. - mHeartbeat.setTimerExpirySec(PLUGIN_LOCKED_UP_SECONDS); + mHeartbeat.setTimerExpirySec(mPluginLockupTimeout); mCPUUsage = message.getValueReal("cpu_usage"); @@ -639,7 +689,7 @@ bool LLPluginProcessParent::pluginLockedUpOrQuit() { bool result = false; - if(!mDisableTimeout) + if(!mDisableTimeout && !mDebug) { if(!mProcess.isRunning()) { @@ -658,7 +708,7 @@ bool LLPluginProcessParent::pluginLockedUpOrQuit() bool LLPluginProcessParent::pluginLockedUp() { - // If the timer has expired, the plugin has locked up. - return mHeartbeat.hasExpired(); + // If the timer is running and has expired, the plugin has locked up. + return (mHeartbeat.getStarted() && mHeartbeat.hasExpired()); } |