llapp.cpp

Go to the documentation of this file.
00001 
00032 #include "linden_common.h"
00033 #include "llapp.h"
00034 
00035 #include "llcommon.h"
00036 #include "llapr.h"
00037 #include "llerrorcontrol.h"
00038 #include "llerrorthread.h"
00039 #include "llframetimer.h"
00040 #include "llmemory.h"
00041 #include "lltimer.h"
00042 
00043 //
00044 // Signal handling
00045 //
00046 // Windows uses structured exceptions, so it's handled a bit differently.
00047 //
00048 #if LL_WINDOWS
00049 LONG WINAPI default_windows_exception_handler(struct _EXCEPTION_POINTERS *exception_infop);
00050 #else
00051 #include <unistd.h> // for fork()
00052 void setup_signals();
00053 void default_unix_signal_handler(int signum, siginfo_t *info, void *);
00054 const S32 LL_SMACKDOWN_SIGNAL = SIGUSR1;
00055 #endif
00056 
00057 // the static application instance
00058 LLApp* LLApp::sApplication = NULL;
00059 
00060 // Local flag for whether or not to do logging in signal handlers.
00061 //static
00062 BOOL LLApp::sLogInSignal = FALSE;
00063 
00064 // static
00065 LLApp::EAppStatus LLApp::sStatus = LLApp::APP_STATUS_STOPPED; // Keeps track of application status
00066 LLAppErrorHandler LLApp::sErrorHandler = NULL;
00067 BOOL LLApp::sErrorThreadRunning = FALSE;
00068 #if !LL_WINDOWS
00069 LLApp::child_map LLApp::sChildMap;
00070 LLAtomicU32* LLApp::sSigChildCount = NULL;
00071 LLAppChildCallback LLApp::sDefaultChildCallback = NULL;
00072 #endif
00073 
00074 
00075 LLApp::LLApp() : mThreadErrorp(NULL)
00076 {
00077         // Set our status to running
00078         setStatus(APP_STATUS_RUNNING);
00079 
00080         LLCommon::initClass();
00081 
00082 #if !LL_WINDOWS
00083         // This must be initialized before the error handler.
00084         sSigChildCount = new LLAtomicU32(0);    
00085 #endif
00086         
00087         // Setup error handling
00088         setupErrorHandling();
00089 
00090         // initialize the options structure. We need to make this an array
00091         // because the structured data will not auto-allocate if we
00092         // reference an invalid location with the [] operator.
00093         mOptions = LLSD::emptyArray();
00094         LLSD sd;
00095         for(int i = 0; i < PRIORITY_COUNT; ++i)
00096         {
00097                 mOptions.append(sd);
00098         }
00099 
00100         // Make sure we clean up APR when we exit
00101         // Don't need to do this if we're cleaning up APR in the destructor
00102         //atexit(ll_cleanup_apr);
00103 
00104         // Set the application to this instance.
00105         sApplication = this;
00106 }
00107 
00108 
00109 LLApp::~LLApp()
00110 {
00111 #if !LL_WINDOWS
00112         delete sSigChildCount;
00113         sSigChildCount = NULL;
00114 #endif
00115         setStopped();
00116         // HACK: wait for the error thread to clean itself
00117         ms_sleep(20);
00118         if (mThreadErrorp)
00119         {
00120                 delete mThreadErrorp;
00121                 mThreadErrorp = NULL;
00122         }
00123 
00124         LLCommon::cleanupClass();
00125 }
00126 
00127 // static
00128 LLApp* LLApp::instance()
00129 {
00130         return sApplication;
00131 }
00132 
00133 
00134 LLSD LLApp::getOption(const std::string& name) const
00135 {
00136         LLSD rv;
00137         LLSD::array_const_iterator iter = mOptions.beginArray();
00138         LLSD::array_const_iterator end = mOptions.endArray();
00139         for(; iter != end; ++iter)
00140         {
00141                 rv = (*iter)[name];
00142                 if(rv.isDefined()) break;
00143         }
00144         return rv;
00145 }
00146 
00147 bool LLApp::parseCommandOptions(int argc, char** argv)
00148 {
00149         LLSD commands;
00150         std::string name;
00151         std::string value;
00152         for(int ii = 1; ii < argc; ++ii)
00153         {
00154                 if(argv[ii][0] != '-')
00155                 {
00156                         llinfos << "Did not find option identifier while parsing token: "
00157                                 << argv[ii] << llendl;
00158                         return false;
00159                 }
00160                 int offset = 1;
00161                 if(argv[ii][1] == '-') ++offset;
00162                 name.assign(&argv[ii][offset]);
00163                 if(((ii+1) >= argc) || (argv[ii+1][0] == '-'))
00164                 {
00165                         // we found another option after this one or we have
00166                         // reached the end. simply record that this option was
00167                         // found and continue.
00168                         commands[name] = true;
00169                         continue;
00170                 }
00171                 ++ii;
00172                 value.assign(argv[ii]);
00173                 commands[name] = value;
00174         }
00175         setOptionData(PRIORITY_COMMAND_LINE, commands);
00176         return true;
00177 }
00178 
00179 bool LLApp::setOptionData(OptionPriority level, LLSD data)
00180 {
00181         if((level < 0)
00182            || (level >= PRIORITY_COUNT)
00183            || (data.type() != LLSD::TypeMap))
00184         {
00185                 return false;
00186         }
00187         mOptions[level] = data;
00188         return true;
00189 }
00190 
00191 LLSD LLApp::getOptionData(OptionPriority level)
00192 {
00193         if((level < 0) || (level >= PRIORITY_COUNT))
00194         {
00195                 return LLSD();
00196         }
00197         return mOptions[level];
00198 }
00199 
00200 void LLApp::stepFrame()
00201 {
00202         LLFrameTimer::updateFrameTime();
00203         LLEventTimer::updateClass();
00204         mRunner.run();
00205 }
00206 
00207 
00208 void LLApp::setupErrorHandling()
00209 {
00210         // Error handling is done by starting up an error handling thread, which just sleeps and
00211         // occasionally checks to see if the app is in an error state, and sees if it needs to be run.
00212 
00213 #if LL_WINDOWS
00214         // Windows doesn't have the same signal handling mechanisms as UNIX, thus APR doesn't provide
00215         // a signal handling thread implementation.
00216         // What we do is install an unhandled exception handler, which will try to do the right thing
00217         // in the case of an error (generate a minidump)
00218 
00219         // Disable this until the viewer gets ported so server crashes can be JIT debugged.
00220         //LPTOP_LEVEL_EXCEPTION_FILTER prev_filter;
00221         //prev_filter = SetUnhandledExceptionFilter(default_windows_exception_handler);
00222 #else
00223         //
00224         // Start up signal handling.
00225         //
00226         // There are two different classes of signals.  Synchronous signals are delivered to a specific
00227         // thread, asynchronous signals can be delivered to any thread (in theory)
00228         //
00229 
00230         setup_signals();
00231 
00232 #endif
00233 
00234         //
00235         // Start the error handling thread, which is responsible for taking action
00236         // when the app goes into the APP_STATUS_ERROR state
00237         //
00238         llinfos << "LLApp::setupErrorHandling - Starting error thread" << llendl;
00239         mThreadErrorp = new LLErrorThread();
00240         mThreadErrorp->setUserData((void *) this);
00241         mThreadErrorp->start();
00242 }
00243 
00244 
00245 void LLApp::setErrorHandler(LLAppErrorHandler handler)
00246 {
00247         LLApp::sErrorHandler = handler;
00248 }
00249 
00250 // static
00251 void LLApp::runErrorHandler()
00252 {
00253         if (LLApp::sErrorHandler)
00254         {
00255                 LLApp::sErrorHandler();
00256         }
00257 
00258         //llinfos << "App status now STOPPED" << llendl;
00259         LLApp::setStopped();
00260 }
00261 
00262 
00263 // static
00264 void LLApp::setStatus(EAppStatus status)
00265 {
00266         sStatus = status;
00267 }
00268 
00269 
00270 // static
00271 void LLApp::setError()
00272 {
00273         setStatus(APP_STATUS_ERROR);
00274 }
00275 
00276 
00277 // static
00278 void LLApp::setQuitting()
00279 {
00280         if (!isExiting())
00281         {
00282                 // If we're already exiting, we don't want to reset our state back to quitting.
00283                 llinfos << "Setting app state to QUITTING" << llendl;
00284                 setStatus(APP_STATUS_QUITTING);
00285         }
00286 }
00287 
00288 
00289 // static
00290 void LLApp::setStopped()
00291 {
00292         setStatus(APP_STATUS_STOPPED);
00293 }
00294 
00295 
00296 // static
00297 bool LLApp::isStopped()
00298 {
00299         return (APP_STATUS_STOPPED == sStatus);
00300 }
00301 
00302 
00303 // static
00304 bool LLApp::isRunning()
00305 {
00306         return (APP_STATUS_RUNNING == sStatus);
00307 }
00308 
00309 
00310 // static
00311 bool LLApp::isError()
00312 {
00313         return (APP_STATUS_ERROR == sStatus);
00314 }
00315 
00316 
00317 // static
00318 bool LLApp::isQuitting()
00319 {
00320         return (APP_STATUS_QUITTING == sStatus);
00321 }
00322 
00323 bool LLApp::isExiting()
00324 {
00325         return isQuitting() || isError();
00326 }
00327 
00328 #if !LL_WINDOWS
00329 // static
00330 U32 LLApp::getSigChildCount()
00331 {
00332         if (sSigChildCount)
00333         {
00334                 return U32(*sSigChildCount);
00335         }
00336         return 0;
00337 }
00338 
00339 // static
00340 void LLApp::incSigChildCount()
00341 {
00342         if (sSigChildCount)
00343         {
00344                 (*sSigChildCount)++;
00345         }
00346 }
00347 
00348 #endif
00349 
00350 
00351 // static
00352 int LLApp::getPid()
00353 {
00354 #if LL_WINDOWS
00355         return 0;
00356 #else
00357         return getpid();
00358 #endif
00359 }
00360 
00361 #if LL_WINDOWS
00362 LONG WINAPI default_windows_exception_handler(struct _EXCEPTION_POINTERS *exception_infop)
00363 {
00364         // Translate the signals/exceptions into cross-platform stuff
00365         // Windows implementation
00366 
00367         // Make sure the user sees something to indicate that the app crashed.
00368         LONG retval;
00369 
00370         if (LLApp::isError())
00371         {
00372                 llwarns << "Got another fatal signal while in the error handler, die now!" << llendl;
00373                 retval = EXCEPTION_EXECUTE_HANDLER;
00374                 return retval;
00375         }
00376 
00377         // Flag status to error, so thread_error starts its work
00378         LLApp::setError();
00379 
00380         // Block in the exception handler until the app has stopped
00381         // This is pretty sketchy, but appears to work just fine
00382         while (!LLApp::isStopped())
00383         {
00384                 ms_sleep(10);
00385         }
00386 
00387         //
00388         // Generate a minidump if we can.
00389         //
00390         // TODO: This needs to be ported over form the viewer-specific
00391         // LLWinDebug class
00392 
00393         //
00394         // At this point, we always want to exit the app.  There's no graceful
00395         // recovery for an unhandled exception.
00396         // 
00397         // Just kill the process.
00398         retval = EXCEPTION_EXECUTE_HANDLER;     
00399         return retval;
00400 }
00401 
00402 #else 
00403 void LLApp::setChildCallback(pid_t pid, LLAppChildCallback callback)
00404 {
00405         LLChildInfo child_info;
00406         child_info.mCallback = callback;
00407         LLApp::sChildMap[pid] = child_info;
00408 }
00409 
00410 void LLApp::setDefaultChildCallback(LLAppChildCallback callback)
00411 {
00412         LLApp::sDefaultChildCallback = callback;
00413 }
00414 
00415 pid_t LLApp::fork()
00416 {
00417         pid_t pid = ::fork();
00418         if( pid < 0 )
00419         {
00420                 int system_error = errno;
00421                 llwarns << "Unable to fork! Operating system error code: "
00422                                 << system_error << llendl;
00423         }
00424         else if (pid == 0)
00425         {
00426                 // Sleep a bit to allow the parent to set up child callbacks.
00427                 ms_sleep(10);
00428 
00429                 // We need to disable signal handling, because we don't have a
00430                 // signal handling thread anymore.
00431                 setupErrorHandling();
00432         }
00433         else
00434         {
00435                 llinfos << "Forked child process " << pid << llendl;
00436         }
00437         return pid;
00438 }
00439 
00440 void setup_signals()
00441 {
00442         //
00443         // Set up signal handlers that may result in program termination
00444         //
00445         struct sigaction act;
00446         act.sa_sigaction = default_unix_signal_handler;
00447         sigemptyset( &act.sa_mask );
00448         act.sa_flags = SA_SIGINFO;
00449 
00450         // Synchronous signals
00451         sigaction(SIGABRT, &act, NULL);
00452         sigaction(SIGALRM, &act, NULL);
00453         sigaction(SIGBUS, &act, NULL);
00454         sigaction(SIGFPE, &act, NULL);
00455         sigaction(SIGHUP, &act, NULL); 
00456         sigaction(SIGILL, &act, NULL);
00457         sigaction(SIGPIPE, &act, NULL);
00458         sigaction(SIGSEGV, &act, NULL);
00459         sigaction(SIGSYS, &act, NULL);
00460 
00461         // Asynchronous signals that are normally ignored
00462         sigaction(SIGCHLD, &act, NULL);
00463         sigaction(SIGUSR2, &act, NULL);
00464 
00465         // Asynchronous signals that result in attempted graceful exit
00466         sigaction(SIGHUP, &act, NULL);
00467         sigaction(SIGTERM, &act, NULL);
00468         sigaction(SIGINT, &act, NULL);
00469 
00470         // Asynchronous signals that result in core
00471         sigaction(LL_SMACKDOWN_SIGNAL, &act, NULL);
00472         sigaction(SIGQUIT, &act, NULL);
00473 }
00474 
00475 void clear_signals()
00476 {
00477         struct sigaction act;
00478         act.sa_handler = SIG_DFL;
00479         sigemptyset( &act.sa_mask );
00480         act.sa_flags = SA_SIGINFO;
00481 
00482         // Synchronous signals
00483         sigaction(SIGABRT, &act, NULL);
00484         sigaction(SIGALRM, &act, NULL);
00485         sigaction(SIGBUS, &act, NULL);
00486         sigaction(SIGFPE, &act, NULL);
00487         sigaction(SIGHUP, &act, NULL); 
00488         sigaction(SIGILL, &act, NULL);
00489         sigaction(SIGPIPE, &act, NULL);
00490         sigaction(SIGSEGV, &act, NULL);
00491         sigaction(SIGSYS, &act, NULL);
00492 
00493         // Asynchronous signals that are normally ignored
00494         sigaction(SIGCHLD, &act, NULL);
00495 
00496         // Asynchronous signals that result in attempted graceful exit
00497         sigaction(SIGHUP, &act, NULL);
00498         sigaction(SIGTERM, &act, NULL);
00499         sigaction(SIGINT, &act, NULL);
00500 
00501         // Asynchronous signals that result in core
00502         sigaction(SIGUSR2, &act, NULL);
00503         sigaction(LL_SMACKDOWN_SIGNAL, &act, NULL);
00504         sigaction(SIGQUIT, &act, NULL);
00505 }
00506 
00507 
00508 
00509 void default_unix_signal_handler(int signum, siginfo_t *info, void *)
00510 {
00511         // Unix implementation of synchronous signal handler
00512         // This runs in the thread that threw the signal.
00513         // We do the somewhat sketchy operation of blocking in here until the error handler
00514         // has gracefully stopped the app.
00515 
00516         if (LLApp::sLogInSignal)
00517         {
00518                 llinfos << "Signal handler - Got signal " << signum << " - " << apr_signal_description_get(signum) << llendl;
00519         }
00520 
00521 
00522         switch (signum)
00523         {
00524         case SIGALRM:
00525         case SIGPIPE:
00526         case SIGUSR2:
00527                 // We don't care about these signals, ignore them
00528                 if (LLApp::sLogInSignal)
00529                 {
00530                         llinfos << "Signal handler - Ignoring this signal" << llendl;
00531                 }
00532                 return;
00533     case SIGCHLD:
00534                 if (LLApp::sLogInSignal)
00535                 {
00536                         llinfos << "Signal handler - Got SIGCHLD from " << info->si_pid << llendl;
00537                 }
00538 
00539                 // Check result code for all child procs for which we've
00540                 // registered callbacks THIS WILL NOT WORK IF SIGCHLD IS SENT
00541                 // w/o killing the child (Go, launcher!)
00542                 // TODO: Now that we're using SIGACTION, we can actually
00543                 // implement the launcher behavior to determine who sent the
00544                 // SIGCHLD even if it doesn't result in child termination
00545                 if (LLApp::sChildMap.count(info->si_pid))
00546                 {
00547                         LLApp::sChildMap[info->si_pid].mGotSigChild = TRUE;
00548                 }
00549                 
00550                 LLApp::incSigChildCount();
00551 
00552                 return;
00553         case SIGABRT:
00554                 // Abort just results in termination of the app, no funky error handling.
00555                 if (LLApp::sLogInSignal)
00556                 {
00557                         llwarns << "Signal handler - Got SIGABRT, terminating" << llendl;
00558                 }
00559                 clear_signals();
00560                 raise(signum);
00561                 return;
00562         case LL_SMACKDOWN_SIGNAL: // Smackdown treated just like any other app termination, for now
00563                 if (LLApp::sLogInSignal)
00564                 {
00565                         llwarns << "Signal handler - Handling smackdown signal!" << llendl;
00566                 }
00567                 else
00568                 {
00569                         // Don't log anything, even errors - this is because this signal could happen anywhere.
00570                         LLError::setDefaultLevel(LLError::LEVEL_NONE);
00571                 }
00572                 
00573                 // Change the signal that we reraise to SIGABRT, so we generate a core dump.
00574                 signum = SIGABRT;
00575         case SIGBUS:
00576         case SIGSEGV:
00577         case SIGQUIT:
00578                 if (LLApp::sLogInSignal)
00579                 {
00580                         llwarns << "Signal handler - Handling fatal signal!" << llendl;
00581                 }
00582                 if (LLApp::isError())
00583                 {
00584                         // Received second fatal signal while handling first, just die right now
00585                         // Set the signal handlers back to default before handling the signal - this makes the next signal wipe out the app.
00586                         clear_signals();
00587 
00588                         if (LLApp::sLogInSignal)
00589                         {
00590                                 llwarns << "Signal handler - Got another fatal signal while in the error handler, die now!" << llendl;
00591                         }
00592                         raise(signum);
00593                         return;
00594                 }
00595                         
00596                 if (LLApp::sLogInSignal)
00597                 {
00598                         llwarns << "Signal handler - Flagging error status and waiting for shutdown" << llendl;
00599                 }
00600                 // Flag status to ERROR, so thread_error does its work.
00601                 LLApp::setError();
00602                 // Block in the signal handler until somebody says that we're done.
00603                 while (LLApp::sErrorThreadRunning && !LLApp::isStopped())
00604                 {
00605                         ms_sleep(10);
00606                 }
00607 
00608                 if (LLApp::sLogInSignal)
00609                 {
00610                         llwarns << "Signal handler - App is stopped, reraising signal" << llendl;
00611                 }
00612                 clear_signals();
00613                 raise(signum);
00614                 return;
00615         case SIGINT:
00616         case SIGHUP:
00617         case SIGTERM:
00618                 if (LLApp::sLogInSignal)
00619                 {
00620                         llwarns << "Signal handler - Got SIGINT, HUP, or TERM, exiting gracefully" << llendl;
00621                 }
00622                 // Graceful exit
00623                 // Just set our state to quitting, not error
00624                 if (LLApp::isQuitting() || LLApp::isError())
00625                 {
00626                         // We're already trying to die, just ignore this signal
00627                         if (LLApp::sLogInSignal)
00628                         {
00629                                 llinfos << "Signal handler - Already trying to quit, ignoring signal!" << llendl;
00630                         }
00631                         return;
00632                 }
00633                 LLApp::setQuitting();
00634                 return;
00635         default:
00636                 if (LLApp::sLogInSignal)
00637                 {
00638                         llwarns << "Signal handler - Unhandled signal, ignoring!" << llendl;
00639                 }
00640         }
00641 }
00642 
00643 #endif // !WINDOWS

Generated on Thu Jul 1 06:08:19 2010 for Second Life Viewer by  doxygen 1.4.7