123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985 |
- /**
- * @file llapp.cpp
- * @brief Implementation of the LLApp class.
- *
- * $LicenseInfo:firstyear=2003&license=viewergpl$
- *
- * Copyright (c) 2003-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
- #include "linden_common.h"
- #if !LL_WINDOWS
- # include <sys/wait.h> // For waitpid()
- #endif
- #include "llapp.h"
- #include "llcommon.h"
- #include "llerrorcontrol.h"
- #include "lleventtimer.h"
- #include "llevents.h"
- #include "llframetimer.h"
- #include "llmemory.h"
- #include "llthread.h"
- ///////////////////////////////////////////////////////////////////////////////
- // LLErrorThread class declaration (used to be in llerrorthread.h, but only
- // used by and with LLApp, so...)
- ///////////////////////////////////////////////////////////////////////////////
- class LLErrorThread : public LLThread
- {
- protected:
- LOG_CLASS(LLErrorThread);
- public:
- LLErrorThread();
- void run() override;
- void setUserData(void* user_data);
- void* getUserData() const;
- protected:
- void* mUserDatap; // User data associated with this thread
- };
- ///////////////////////////////////////////////////////////////////////////////
- // Signal handling
- ///////////////////////////////////////////////////////////////////////////////
- #if LL_WINDOWS
- // Windows uses structured exceptions, so it is handled a bit differently.
- LONG WINAPI default_windows_exception_handler(struct _EXCEPTION_POINTERS* e);
- BOOL ConsoleCtrlHandler(DWORD fdw_ctrl_type);
- #else
- # include <signal.h>
- # include <unistd.h> // for fork()
- void setup_signals();
- void default_unix_signal_handler(int signum, siginfo_t* info, void*);
- # if LL_DARWIN
- // OS-X does not support SIGRT*
- const S32 LL_SMACKDOWN_SIGNAL = SIGUSR1;
- const S32 LL_HEARTBEAT_SIGNAL = SIGUSR2;
- # else // Linux or (assumed) other similar unixoid
- // Do not catch SIGCHLD in our base application class for the viewer: some of
- // our 3rd party libs may need their *own* SIGCHLD handler to work. The viewer
- // does not need to catch SIGCHLD anyway.
- #define LL_IGNORE_SIGCHLD 1
- // We want reliable delivery of our signals: SIGRT* is it. Old LinuxThreads
- // versions eat SIGRTMIN+0 to SIGRTMIN+2, avoid those. Note that SIGRTMIN/
- // SIGRTMAX may expand to a glibc function call with a non-constant result so
- // these are not consts and cannot be used in constant expressions. SIGRTMAX
- // may return -1 on rare broken setups.
- const S32 LL_SMACKDOWN_SIGNAL = SIGRTMAX >= 0 ? SIGRTMAX - 1 : SIGUSR1;
- const S32 LL_HEARTBEAT_SIGNAL = SIGRTMAX >= 0 ? SIGRTMAX : SIGUSR2;
- # endif // LL_DARWIN
- #endif // LL_WINDOWS
- ///////////////////////////////////////////////////////////////////////////////
- // LLApp class proper
- ///////////////////////////////////////////////////////////////////////////////
- // Static variables
- // The static application instance
- LLApp* LLApp::sApplication = NULL;
- // Local flag for whether or not to do logging in signal handlers.
- bool LLApp::sLogInSignal = false;
- // Keeps track of application status:
- LLAtomicS32 LLApp::sStatus(LLApp::APP_STATUS_STOPPED);
- LLAppErrorHandler LLApp::sErrorHandler = NULL;
- LLAppErrorHandler LLApp::sSyncErrorHandler = NULL;
- bool LLApp::sErrorThreadRunning = false;
- #if !LL_WINDOWS
- LLApp::child_map LLApp::sChildMap;
- LLAtomicU32* LLApp::sSigChildCount = NULL;
- LLAppChildCallback LLApp::sDefaultChildCallback = NULL;
- #endif
- LLApp::LLApp()
- : mThreadErrorp(NULL)
- {
- assert_main_thread(); // Make sure we record the main thread
- commonCtor();
- startErrorThread();
- }
- void LLApp::commonCtor()
- {
- // Set our status to running
- setStatus(APP_STATUS_RUNNING);
- LLCommon::initClass();
- #if !LL_WINDOWS
- // This must be initialized before the error handler.
- sSigChildCount = new LLAtomicU32(0);
- #endif
- // Setup error handling
- setupErrorHandling();
- // Initialize the options structure. We need to make this an array because
- // the structured data will not auto-allocate if we reference an invalid
- // location with the [] operator.
- mOptions = LLSD::emptyArray();
- LLSD sd;
- for (int i = 0; i < PRIORITY_COUNT; ++i)
- {
- mOptions.append(sd);
- }
- // Set the application to this instance.
- sApplication = this;
- }
- LLApp::LLApp(LLErrorThread* error_thread)
- : mThreadErrorp(error_thread)
- {
- commonCtor();
- }
- LLApp::~LLApp()
- {
- #if !LL_WINDOWS
- delete sSigChildCount;
- sSigChildCount = NULL;
- #endif
- setStopped();
- // *HACK: wait for the error thread to clean itself
- ms_sleep(100);
- if (mThreadErrorp)
- {
- delete mThreadErrorp;
- mThreadErrorp = NULL;
- }
- LLCommon::cleanupClass();
- }
- LLSD LLApp::getOption(const std::string& name) const
- {
- LLSD rv;
- for (LLSD::array_const_iterator iter = mOptions.beginArray(),
- end = mOptions.endArray();
- iter != end; ++iter)
- {
- rv = (*iter)[name];
- if (rv.isDefined())
- {
- break;
- }
- }
- return rv;
- }
- bool LLApp::parseCommandOptions(int argc, char** argv)
- {
- LLSD commands;
- std::string name;
- std::string value;
- for (S32 ii = 1; ii < argc; ++ii)
- {
- if (argv[ii][0] != '-')
- {
- llinfos << "Did not find option identifier while parsing token: "
- << argv[ii] << llendl;
- return false;
- }
- int offset = 1;
- if (argv[ii][1] == '-')
- {
- ++offset;
- }
- name.assign(&argv[ii][offset]);
- if (ii + 1 >= argc || argv[ii + 1][0] == '-')
- {
- // we found another option after this one or we have
- // reached the end. simply record that this option was
- // found and continue.
- int flag = name.compare("logfile");
- if (0 == flag)
- {
- commands[name] = "log";
- }
- else
- {
- commands[name] = true;
- }
- continue;
- }
- value.assign(argv[++ii]);
- #if LL_WINDOWS
- // Windows changed command line parsing. Deal with it.
- S32 slen = value.length() - 1;
- S32 start = 0;
- S32 end = slen;
- if (argv[ii][start] == '"')
- {
- ++start;
- }
- if (argv[ii][end] == '"')
- {
- --end;
- }
- if (start != 0 || end != slen)
- {
- value = value.substr(start, end);
- }
- #endif
- commands[name] = value;
- }
- setOptionData(PRIORITY_COMMAND_LINE, commands);
- return true;
- }
- bool LLApp::setOptionData(OptionPriority level, LLSD data)
- {
- if (level < 0 || level >= PRIORITY_COUNT || data.type() != LLSD::TypeMap)
- {
- return false;
- }
- mOptions[level] = data;
- return true;
- }
- LLSD LLApp::getOptionData(OptionPriority level)
- {
- if (level < 0 || level >= PRIORITY_COUNT)
- {
- return LLSD();
- }
- return mOptions[level];
- }
- void LLApp::stepFrame()
- {
- LLFrameTimer::stepFrame();
- LLEventTimer::stepFrame();
- }
- #if LL_WINDOWS
- // The following code is needed for 32-bit apps on 64-bit windows to keep it
- // from eating crashes. It is a lovely undocumented 'feature' in SP1 of
- // Windows 7. An excellent in-depth article on the issue may be found here:
- // http://randomascii.wordpress.com/2012/07/05/when-even-crashing-doesnt-work/
- void EnableCrashingOnCrashes()
- {
- typedef BOOL (WINAPI* tGetPolicy)(LPDWORD lpFlags);
- typedef BOOL (WINAPI* tSetPolicy)(DWORD dwFlags);
- const DWORD EXCEPTION_SWALLOWING = 0x1;
- HMODULE kernel32 = LoadLibraryA("kernel32.dll");
- tGetPolicy pGetPolicy =
- (tGetPolicy)GetProcAddress(kernel32,
- "GetProcessUserModeExceptionPolicy");
- tSetPolicy pSetPolicy =
- (tSetPolicy)GetProcAddress(kernel32,
- "SetProcessUserModeExceptionPolicy");
- if (pGetPolicy && pSetPolicy)
- {
- DWORD dwFlags;
- if (pGetPolicy(&dwFlags))
- {
- // Turn off the filter
- pSetPolicy(dwFlags & ~EXCEPTION_SWALLOWING);
- }
- }
- }
- #endif
- // Error handling is done by starting up an error handling thread, which just
- // sleeps and occasionally checks to see if the app is in an error state, and
- // sees if it needs to be run.
- void LLApp::setupErrorHandling()
- {
- #if LL_WINDOWS
- // Windows does not have the same signal handling mechanisms as UNIX. What
- // we do is install an unhandled exception handler, which will try to do
- // the right thing in the case of an error (generate a minidump).
- EnableCrashingOnCrashes();
- // This sets a callback to handle w32 signals to the console window. The
- // viewer should not be affected, since its a windowed app.
- SetConsoleCtrlHandler((PHANDLER_ROUTINE)ConsoleCtrlHandler, TRUE);
- #else
- // Start up signal handling.
- //
- // There are two different classes of signals. Synchronous signals are
- // delivered to a specific thread, asynchronous signals can be delivered to
- // any thread (in theory)
- setup_signals();
- #endif
- }
- // Starts the error handling thread, which is responsible for taking action
- // when the app goes into the APP_STATUS_ERROR state
- void LLApp::startErrorThread()
- {
- llinfos << "Starting error thread" << llendl;
- mThreadErrorp = new LLErrorThread();
- mThreadErrorp->setUserData((void*)this);
- mThreadErrorp->start();
- }
- //static
- void LLApp::runSyncErrorHandler()
- {
- if (sSyncErrorHandler)
- {
- sSyncErrorHandler();
- }
- }
- //static
- void LLApp::runErrorHandler()
- {
- if (sErrorHandler)
- {
- sErrorHandler();
- }
- setStatus(APP_STATUS_STOPPED);
- }
- //static
- void LLApp::setError()
- {
- if (!isError())
- {
- // Perform any needed synchronous error-handling
- runSyncErrorHandler();
- // Set app status to ERROR so that the LLErrorThread notices
- setStatus(APP_STATUS_ERROR);
- }
- }
- //static
- void LLApp::setQuitting()
- {
- if (!isExiting())
- {
- // If we are already exiting, we do not want to reset our state back to
- // quitting.
- llinfos << "Setting app state to QUITTING" << llendl;
- setStatus(APP_STATUS_QUITTING);
- }
- }
- //static
- void LLApp::setStatus(S32 status)
- {
- sStatus = status;
- // This can also happen very late in the application lifecycle; do not
- // resurrect a deleted LLSingleton...
- if (LLEventPumps::destroyed())
- {
- return;
- }
- // Notify interested parties of status change
- LLSD value;
- switch (status)
- {
- case APP_STATUS_STOPPED:
- value = LLSD::String("stopped");
- break;
- case APP_STATUS_RUNNING:
- value = LLSD::String("running");
- break;
- case APP_STATUS_QUITTING:
- value = LLSD::String("quitting");
- break;
- case APP_STATUS_ERROR:
- value = LLSD::String("error");
- break;
- default:
- value = LLSD::Integer(status);
- }
- LLSD data;
- data["status"] = value;
- gEventPumps.obtain("LLApp").post(data);
- }
- #if !LL_WINDOWS
- //static
- U32 LLApp::getSigChildCount()
- {
- if (sSigChildCount)
- {
- return U32(*sSigChildCount);
- }
- return 0;
- }
- //static
- void LLApp::incSigChildCount()
- {
- if (sSigChildCount)
- {
- ++(*sSigChildCount);
- }
- }
- #endif
- #if LL_WINDOWS
- // Translates the signals/exceptions into cross-platform stuff Windows
- // implementation
- LONG WINAPI default_windows_exception_handler(struct _EXCEPTION_POINTERS*)
- {
- // Make sure the user sees something to indicate that the app crashed.
- LONG retval = EXCEPTION_EXECUTE_HANDLER;
- if (LLApp::isError())
- {
- llwarns << "Got another fatal signal while in the error handler, die now !"
- << llendl;
- return retval;
- }
- // Flag status to error, so thread_error starts its work
- LLApp::setError();
- // Block in the exception handler until the app has stopped. This is pretty
- // sketchy, but appears to work just fine
- while (!LLApp::isStopped())
- {
- ms_sleep(10);
- }
- // *TODO: generate a minidump if we can. This needs to be ported over from
- // the viewer-specific LLWinDebug class
- // At this point, we always want to exit the app. There is no graceful
- // recovery for an unhandled exception. Just kill the process.
- return retval;
- }
- // Win32 does not support signals. This is used instead.
- BOOL ConsoleCtrlHandler(DWORD fdw_ctrl_type)
- {
- switch (fdw_ctrl_type)
- {
- // For these, just set our state to quitting, not error
- case CTRL_BREAK_EVENT:
- case CTRL_LOGOFF_EVENT:
- case CTRL_SHUTDOWN_EVENT:
- case CTRL_CLOSE_EVENT: // From end task or the window close button.
- case CTRL_C_EVENT: // from CTRL-C on the keyboard
- if (LLApp::isExiting())
- {
- // We are already trying to die, just ignore this signal
- if (LLApp::sLogInSignal)
- {
- llinfos << "Already trying to quit, ignoring signal !"
- << llendl;
- }
- return TRUE;
- }
- LLApp::setQuitting();
- return TRUE;
- default:
- return FALSE;
- }
- }
- #else
- void LLApp::setChildCallback(pid_t pid, LLAppChildCallback callback)
- {
- LLChildInfo child_info;
- child_info.mCallback = callback;
- LLApp::sChildMap[pid] = child_info;
- }
- void LLApp::setDefaultChildCallback(LLAppChildCallback callback)
- {
- LLApp::sDefaultChildCallback = callback;
- }
- pid_t LLApp::fork()
- {
- fflush(NULL); // flush all buffers before the child inherits them
- pid_t pid = ::fork();
- if (pid < 0)
- {
- int system_error = errno;
- llwarns << "Unable to fork ! Operating system error code: "
- << system_error << llendl;
- }
- else if (pid == 0)
- {
- // Sleep a bit to allow the parent to set up child callbacks.
- ms_sleep(10);
- // We need to disable signal handling, because we don't have a signal
- // handling thread anymore.
- setupErrorHandling();
- }
- else
- {
- llinfos << "Forked child process " << pid << llendl;
- }
- return pid;
- }
- void setup_signals()
- {
- // Set up signal handlers that may result in program termination
- struct sigaction act;
- act.sa_sigaction = default_unix_signal_handler;
- sigemptyset(&act.sa_mask);
- act.sa_flags = SA_SIGINFO;
- // Synchronous signals
- sigaction(SIGABRT, &act, NULL);
- sigaction(SIGALRM, &act, NULL);
- sigaction(SIGBUS, &act, NULL);
- sigaction(SIGFPE, &act, NULL);
- sigaction(SIGILL, &act, NULL);
- sigaction(SIGPIPE, &act, NULL);
- sigaction(SIGSEGV, &act, NULL);
- sigaction(SIGSYS, &act, NULL);
- sigaction(LL_HEARTBEAT_SIGNAL, &act, NULL);
- sigaction(LL_SMACKDOWN_SIGNAL, &act, NULL);
- // Asynchronous signals that are normally ignored
- #ifndef LL_IGNORE_SIGCHLD
- sigaction(SIGCHLD, &act, NULL);
- #endif // LL_IGNORE_SIGCHLD
- sigaction(SIGUSR2, &act, NULL);
- // Asynchronous signals that result in attempted graceful exit
- sigaction(SIGHUP, &act, NULL);
- sigaction(SIGTERM, &act, NULL);
- sigaction(SIGINT, &act, NULL);
- // Asynchronous signals that result in core
- sigaction(SIGQUIT, &act, NULL);
- }
- void clear_signals()
- {
- struct sigaction act;
- act.sa_handler = SIG_DFL;
- sigemptyset(&act.sa_mask);
- act.sa_flags = SA_SIGINFO;
- // Synchronous signals
- sigaction(SIGABRT, &act, NULL);
- sigaction(SIGALRM, &act, NULL);
- sigaction(SIGBUS, &act, NULL);
- sigaction(SIGFPE, &act, NULL);
- sigaction(SIGILL, &act, NULL);
- sigaction(SIGPIPE, &act, NULL);
- sigaction(SIGSEGV, &act, NULL);
- sigaction(SIGSYS, &act, NULL);
- sigaction(LL_HEARTBEAT_SIGNAL, &act, NULL);
- sigaction(LL_SMACKDOWN_SIGNAL, &act, NULL);
- // Asynchronous signals that are normally ignored
- #ifndef LL_IGNORE_SIGCHLD
- sigaction(SIGCHLD, &act, NULL);
- #endif // LL_IGNORE_SIGCHLD
- // Asynchronous signals that result in attempted graceful exit
- sigaction(SIGHUP, &act, NULL);
- sigaction(SIGTERM, &act, NULL);
- sigaction(SIGINT, &act, NULL);
- // Asynchronous signals that result in core
- sigaction(SIGUSR2, &act, NULL);
- sigaction(SIGQUIT, &act, NULL);
- }
- // Unix implementation of synchronous signal handler. This runs in the thread
- // that threw the signal. We do the somewhat sketchy operation of blocking in
- // here until the error handler has gracefully stopped the app.
- void default_unix_signal_handler(int signum, siginfo_t* info, void*)
- {
- if (LLApp::sLogInSignal)
- {
- llinfos << "Got signal " << signum << llendl;
- }
- switch (signum)
- {
- case SIGCHLD:
- if (LLApp::sLogInSignal)
- {
- llinfos << "Got SIGCHLD from " << info->si_pid << llendl;
- }
- // Check result code for all childs for which we have registered
- // callbacks THIS WILL NOT WORK IF SIGCHLD IS SENT without killing
- // the child.
- // *TODO: now that we are using SIGACTION, we could actually
- // implement the launcher behavior to determine who sent the
- // SIGCHLD even if it does not result in child termination
- if (LLApp::sChildMap.count(info->si_pid))
- {
- LLApp::sChildMap[info->si_pid].mGotSigChild = true;
- }
- LLApp::incSigChildCount();
- return;
- case SIGABRT:
- // Abort just results in termination of the app, no funky error
- // handling.
- if (LLApp::sLogInSignal)
- {
- llwarns << "Got SIGABRT, terminating" << llendl;
- }
- clear_signals();
- raise(signum);
- return;
- case SIGINT:
- #if !LL_DARWIN
- case SIGHUP:
- #endif
- case SIGTERM:
- if (LLApp::sLogInSignal)
- {
- #if LL_DARWIN
- llwarns << "Got SIGINT or SIGTERM, exiting gracefully"
- << llendl;
- #else
- llwarns << "Got SIGINT, SIGHUP or SIGTERM, exiting gracefully"
- << llendl;
- #endif
- }
- // Graceful exit... Just set our state to quitting, not error.
- if (LLApp::isExiting())
- {
- // We are already trying to die, just ignore this signal
- if (LLApp::sLogInSignal)
- {
- llinfos << "Already trying to quit, ignoring signal !"
- << llendl;
- }
- return;
- }
- LLApp::setQuitting();
- return;
- case SIGALRM:
- case SIGPIPE:
- case SIGUSR2:
- default:
- if (signum == LL_SMACKDOWN_SIGNAL || signum == SIGBUS ||
- signum == SIGILL || signum == SIGFPE || signum == SIGSEGV ||
- signum == SIGQUIT)
- {
- if (signum == LL_SMACKDOWN_SIGNAL)
- {
- // Smackdown treated just like any other app termination,
- // for now
- if (LLApp::sLogInSignal)
- {
- llwarns << "Handling smackdown signal !" << llendl;
- }
- else
- {
- // Do not log anything, even errors: this is because
- // this signal could happen anywhere.
- LLError::setDefaultLevel(LLError::LEVEL_NONE);
- }
- // Change the signal that we re-raise to SIGABRT, so we
- // generate a core dump.
- signum = SIGABRT;
- }
- if (LLApp::sLogInSignal)
- {
- llwarns << "Handling fatal signal !" << llendl;
- }
- if (LLApp::isError())
- {
- // Received second fatal signal while handling first, just
- // die right now. Set the signal handlers back to default
- // before handling the signal: this makes the next signal
- // wipe out the app.
- clear_signals();
- if (LLApp::sLogInSignal)
- {
- llwarns << "Got another fatal signal while in the error handler, die now !"
- << llendl;
- }
- raise(signum);
- return;
- }
- if (LLApp::sLogInSignal)
- {
- llwarns << "Flagging error status and waiting for shutdown"
- << llendl;
- }
- // Flag status to ERROR, so thread_error does its work.
- LLApp::setError();
- // Block in the signal handler until somebody says that we are done.
- while (LLApp::sErrorThreadRunning && !LLApp::isStopped())
- {
- ms_sleep(10);
- }
- if (LLApp::sLogInSignal)
- {
- llwarns << "App is stopped, re-raising signal" << llendl;
- }
- clear_signals();
- raise(signum);
- return;
- }
- else if (LLApp::sLogInSignal)
- {
- llinfos << "Unhandled signal " << signum << ", ignoring !"
- << llendl;
- }
- }
- }
- #endif
- ///////////////////////////////////////////////////////////////////////////////
- // LLErrorThread class (used to be in llerrorthread.cpp, but only used by and
- // with LLApp, so...)
- ///////////////////////////////////////////////////////////////////////////////
- LLErrorThread::LLErrorThread()
- : LLThread("Error"),
- mUserDatap(NULL)
- {
- }
- void LLErrorThread::setUserData(void* user_data)
- {
- mUserDatap = user_data;
- }
- void* LLErrorThread::getUserData() const
- {
- return mUserDatap;
- }
- #if !LL_WINDOWS
- //
- // Various signal/error handling functions that can't be put into the class
- //
- void get_child_status(const int waitpid_status, int& process_status,
- bool& exited, bool do_logging)
- {
- exited = false;
- process_status = -1;
- // The child process exited. Call its callback, and then clean it up
- if (WIFEXITED(waitpid_status))
- {
- process_status = WEXITSTATUS(waitpid_status);
- exited = true;
- if (do_logging)
- {
- llinfos << "get_child_status - Child exited cleanly with return of "
- << process_status << llendl;
- }
- return;
- }
- else if (WIFSIGNALED(waitpid_status))
- {
- process_status = WTERMSIG(waitpid_status);
- exited = true;
- if (do_logging)
- {
- llinfos << "get_child_status - Child died because of uncaught signal "
- << process_status << llendl;
- #ifdef WCOREDUMP
- if (WCOREDUMP(waitpid_status))
- {
- llinfos << "get_child_status - Child dumped core" << llendl;
- }
- else
- {
- llinfos << "get_child_status - Child didn't dump core"
- << llendl;
- }
- #endif
- }
- return;
- }
- else if (do_logging)
- {
- // This is weird. I just dump the waitpid status into the status code,
- // not that there's any way of telling what it is...
- llinfos << "get_child_status - Got SIGCHILD but child didn't exit"
- << llendl;
- process_status = waitpid_status;
- }
- }
- #endif
- void LLErrorThread::run()
- {
- LLApp::sErrorThreadRunning = true;
- // This thread sits and waits for the sole purpose
- // of waiting for the signal/exception handlers to flag the
- // application state as APP_STATUS_ERROR.
- llinfos << "thread_error - Waiting for an error" << llendl;
- #if !LL_WINDOWS
- U32 last_sig_child_count = 0;
- #endif
- while (true)
- {
- if (LLApp::isError() || LLApp::isStopped())
- {
- // The application has stopped running, time to take action (maybe)
- break;
- }
- #if !LL_WINDOWS
- // Check whether or not the main thread had a sig child we have not
- // handled.
- U32 current_sig_child_count = LLApp::getSigChildCount();
- if (last_sig_child_count != current_sig_child_count)
- {
- int status = 0;
- pid_t child_pid = 0;
- last_sig_child_count = current_sig_child_count;
- if (LLApp::sLogInSignal)
- {
- llinfos << "thread_error handling SIGCHLD #"
- << current_sig_child_count << llendl;
- }
- for (LLApp::child_map::iterator iter = LLApp::sChildMap.begin();
- iter != LLApp::sChildMap.end(); )
- {
- child_pid = iter->first;
- LLChildInfo &child_info = iter->second;
- // Check the status of *all* children, in case we missed a
- // signal
- if (waitpid(child_pid, &status, WNOHANG) != 0)
- {
- bool exited = false;
- int exit_status = -1;
- get_child_status(status, exit_status, exited,
- LLApp::sLogInSignal);
- if (child_info.mCallback)
- {
- if (LLApp::sLogInSignal)
- {
- llinfos << "Running child callback" << llendl;
- }
- child_info.mCallback(child_pid, exited, status);
- }
- LLApp::sChildMap.erase(iter++);
- }
- else
- {
- // Child did not terminate, yet we got a sigchild somewhere
- if (child_info.mGotSigChild && child_info.mCallback)
- {
- child_info.mCallback(child_pid, false, 0);
- }
- child_info.mGotSigChild = false;
- ++iter;
- }
- }
- // Check the status of *all* children, in case we missed a signal
- // Same as above, but use the default child callback
- while ((child_pid = waitpid(-1, &status, WNOHANG)) > 0)
- {
- if (waitpid(child_pid, &status, WNOHANG) != 0)
- {
- bool exited = false;
- int exit_status = -1;
- get_child_status(status, exit_status, exited, LLApp::sLogInSignal);
- if (LLApp::sDefaultChildCallback)
- {
- if (LLApp::sLogInSignal)
- {
- llinfos << "Running default child callback"
- << llendl;
- }
- LLApp::sDefaultChildCallback(child_pid, true, status);
- }
- }
- }
- }
- #endif
- ms_sleep(10);
- }
- if (LLApp::isError())
- {
- // The app is in an error state, run the application's error handler.
- #if 0
- llinfos << "thread_error - An error has occurred, running error callback !"
- << llendl;
- #endif
- // Run the error handling callback
- LLApp::runErrorHandler();
- }
- #if 0
- else
- {
- // Everything is okay, a clean exit.
- llinfos << "thread_error - Application exited cleanly" << llendl;
- }
- llinfos << "thread_error - Exiting" << llendl;
- #endif
- LLApp::sErrorThreadRunning = false;
- }
|