aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llcommon/llapp.cpp
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:44:46 -0500
committerJacek Antonelli2008-08-15 23:44:46 -0500
commit38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch)
treeadca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/llcommon/llapp.cpp
parentREADME.txt (diff)
downloadmeta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.zip
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.gz
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.bz2
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.xz
Second Life viewer sources 1.13.2.12
Diffstat (limited to '')
-rw-r--r--linden/indra/llcommon/llapp.cpp639
1 files changed, 639 insertions, 0 deletions
diff --git a/linden/indra/llcommon/llapp.cpp b/linden/indra/llcommon/llapp.cpp
new file mode 100644
index 0000000..42d9050
--- /dev/null
+++ b/linden/indra/llcommon/llapp.cpp
@@ -0,0 +1,639 @@
1/**
2 * @file llapp.cpp
3 * @brief Implementation of the LLApp class.
4 *
5 * Copyright (c) 2003-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#include "linden_common.h"
29#include "llapp.h"
30
31#include "llcommon.h"
32#include "llapr.h"
33#include "llerrorthread.h"
34#include "llframetimer.h"
35#include "llmemory.h"
36
37//
38// Signal handling
39//
40// Windows uses structured exceptions, so it's handled a bit differently.
41//
42#if LL_WINDOWS
43LONG WINAPI default_windows_exception_handler(struct _EXCEPTION_POINTERS *exception_infop);
44#else
45#include <unistd.h> // for fork()
46void setup_signals();
47void default_unix_signal_handler(int signum, siginfo_t *info, void *);
48const S32 LL_SMACKDOWN_SIGNAL = SIGUSR1;
49#endif
50
51// the static application instance
52LLApp* LLApp::sApplication = NULL;
53
54// Local flag for whether or not to do logging in signal handlers.
55//static
56BOOL LLApp::sLogInSignal = FALSE;
57
58// static
59LLApp::EAppStatus LLApp::sStatus = LLApp::APP_STATUS_STOPPED; // Keeps track of application status
60LLAppErrorHandler LLApp::sErrorHandler = NULL;
61BOOL LLApp::sErrorThreadRunning = FALSE;
62#if !LL_WINDOWS
63LLApp::child_map LLApp::sChildMap;
64LLAtomicU32* LLApp::sSigChildCount = NULL;
65LLAppChildCallback LLApp::sDefaultChildCallback = NULL;
66#endif
67
68
69LLApp::LLApp() : mThreadErrorp(NULL)
70{
71 // Set our status to running
72 setStatus(APP_STATUS_RUNNING);
73
74 LLCommon::initClass();
75
76#if !LL_WINDOWS
77 // This must be initialized before the error handler.
78 sSigChildCount = new LLAtomicU32(0);
79#endif
80
81 // Setup error handling
82 setupErrorHandling();
83
84 // initialize the options structure. We need to make this an array
85 // because the structured data will not auto-allocate if we
86 // reference an invalid location with the [] operator.
87 mOptions = LLSD::emptyArray();
88 LLSD sd;
89 for(int i = 0; i < PRIORITY_COUNT; ++i)
90 {
91 mOptions.append(sd);
92 }
93
94 // Make sure we clean up APR when we exit
95 // Don't need to do this if we're cleaning up APR in the destructor
96 //atexit(ll_cleanup_apr);
97
98 // Set the application to this instance.
99 sApplication = this;
100}
101
102
103LLApp::~LLApp()
104{
105#if !LL_WINDOWS
106 delete sSigChildCount;
107 sSigChildCount = NULL;
108#endif
109 setStopped();
110 // HACK: wait for the error thread to clean itself
111 ms_sleep(20);
112 if (mThreadErrorp)
113 {
114 delete mThreadErrorp;
115 mThreadErrorp = NULL;
116 }
117
118 LLCommon::cleanupClass();
119}
120
121// static
122LLApp* LLApp::instance()
123{
124 return sApplication;
125}
126
127
128LLSD LLApp::getOption(const std::string& name) const
129{
130 LLSD rv;
131 LLSD::array_const_iterator iter = mOptions.beginArray();
132 LLSD::array_const_iterator end = mOptions.endArray();
133 for(; iter != end; ++iter)
134 {
135 rv = (*iter)[name];
136 if(rv.isDefined()) break;
137 }
138 return rv;
139}
140
141bool LLApp::parseCommandOptions(int argc, char** argv)
142{
143 LLSD commands;
144 std::string name;
145 std::string value;
146 for(int ii = 1; ii < argc; ++ii)
147 {
148 if(argv[ii][0] != '-')
149 {
150 llinfos << "Did not find option identifier while parsing token: "
151 << argv[ii] << llendl;
152 return false;
153 }
154 int offset = 1;
155 if(argv[ii][1] == '-') ++offset;
156 name.assign(&argv[ii][offset]);
157 if(((ii+1) >= argc) || (argv[ii+1][0] == '-'))
158 {
159 // we found another option after this one or we have
160 // reached the end. simply record that this option was
161 // found and continue.
162 commands[name] = true;
163 continue;
164 }
165 ++ii;
166 value.assign(argv[ii]);
167 commands[name] = value;
168 }
169 setOptionData(PRIORITY_COMMAND_LINE, commands);
170 return true;
171}
172
173bool LLApp::setOptionData(OptionPriority level, LLSD data)
174{
175 if((level < 0)
176 || (level >= PRIORITY_COUNT)
177 || (data.type() != LLSD::TypeMap))
178 {
179 return false;
180 }
181 mOptions[level] = data;
182 return true;
183}
184
185LLSD LLApp::getOptionData(OptionPriority level)
186{
187 if((level < 0) || (level >= PRIORITY_COUNT))
188 {
189 return LLSD();
190 }
191 return mOptions[level];
192}
193
194void LLApp::stepFrame()
195{
196 // Update the static frame timer.
197 LLFrameTimer::updateFrameTime();
198
199 // Run ready runnables
200 mRunner.run();
201}
202
203
204void LLApp::setupErrorHandling()
205{
206 // Error handling is done by starting up an error handling thread, which just sleeps and
207 // occasionally checks to see if the app is in an error state, and sees if it needs to be run.
208
209#if LL_WINDOWS
210 // Windows doesn't have the same signal handling mechanisms as UNIX, thus APR doesn't provide
211 // a signal handling thread implementation.
212 // What we do is install an unhandled exception handler, which will try to do the right thing
213 // in the case of an error (generate a minidump)
214
215 // Disable this until the viewer gets ported so server crashes can be JIT debugged.
216 //LPTOP_LEVEL_EXCEPTION_FILTER prev_filter;
217 //prev_filter = SetUnhandledExceptionFilter(default_windows_exception_handler);
218#else
219 //
220 // Start up signal handling.
221 //
222 // There are two different classes of signals. Synchronous signals are delivered to a specific
223 // thread, asynchronous signals can be delivered to any thread (in theory)
224 //
225
226 setup_signals();
227
228#endif
229
230 //
231 // Start the error handling thread, which is responsible for taking action
232 // when the app goes into the APP_STATUS_ERROR state
233 //
234 llinfos << "LLApp::setupErrorHandling - Starting error thread" << llendl;
235 mThreadErrorp = new LLErrorThread();
236 mThreadErrorp->setUserData((void *) this);
237 mThreadErrorp->start();
238}
239
240
241void LLApp::setErrorHandler(LLAppErrorHandler handler)
242{
243 LLApp::sErrorHandler = handler;
244}
245
246// static
247void LLApp::runErrorHandler()
248{
249 if (LLApp::sErrorHandler)
250 {
251 LLApp::sErrorHandler();
252 }
253
254 //llinfos << "App status now STOPPED" << llendl;
255 LLApp::setStopped();
256}
257
258
259// static
260void LLApp::setStatus(EAppStatus status)
261{
262 sStatus = status;
263}
264
265
266// static
267void LLApp::setError()
268{
269 setStatus(APP_STATUS_ERROR);
270}
271
272
273// static
274void LLApp::setQuitting()
275{
276 if (!isExiting())
277 {
278 // If we're already exiting, we don't want to reset our state back to quitting.
279 llinfos << "Setting app state to QUITTING" << llendl;
280 setStatus(APP_STATUS_QUITTING);
281 }
282}
283
284
285// static
286void LLApp::setStopped()
287{
288 setStatus(APP_STATUS_STOPPED);
289}
290
291
292// static
293bool LLApp::isStopped()
294{
295 return (APP_STATUS_STOPPED == sStatus);
296}
297
298
299// static
300bool LLApp::isRunning()
301{
302 return (APP_STATUS_RUNNING == sStatus);
303}
304
305
306// static
307bool LLApp::isError()
308{
309 return (APP_STATUS_ERROR == sStatus);
310}
311
312
313// static
314bool LLApp::isQuitting()
315{
316 return (APP_STATUS_QUITTING == sStatus);
317}
318
319bool LLApp::isExiting()
320{
321 return isQuitting() || isError();
322}
323
324#if !LL_WINDOWS
325// static
326U32 LLApp::getSigChildCount()
327{
328 if (sSigChildCount)
329 {
330 return U32(*sSigChildCount);
331 }
332 return 0;
333}
334
335// static
336void LLApp::incSigChildCount()
337{
338 if (sSigChildCount)
339 {
340 (*sSigChildCount)++;
341 }
342}
343
344#endif
345
346
347// static
348int LLApp::getPid()
349{
350#if LL_WINDOWS
351 return 0;
352#else
353 return getpid();
354#endif
355}
356
357#if LL_WINDOWS
358LONG WINAPI default_windows_exception_handler(struct _EXCEPTION_POINTERS *exception_infop)
359{
360 // Translate the signals/exceptions into cross-platform stuff
361 // Windows implementation
362
363 // Make sure the user sees something to indicate that the app crashed.
364 LONG retval;
365
366 if (LLApp::isError())
367 {
368 llwarns << "Got another fatal signal while in the error handler, die now!" << llendl;
369 retval = EXCEPTION_EXECUTE_HANDLER;
370 return retval;
371 }
372
373 // Flag status to error, so thread_error starts its work
374 LLApp::setError();
375
376 // Block in the exception handler until the app has stopped
377 // This is pretty sketchy, but appears to work just fine
378 while (!LLApp::isStopped())
379 {
380 ms_sleep(10);
381 }
382
383 //
384 // Generate a minidump if we can.
385 //
386 // TODO: This needs to be ported over form the viewer-specific
387 // LLWinDebug class
388
389 //
390 // At this point, we always want to exit the app. There's no graceful
391 // recovery for an unhandled exception.
392 //
393 // Just kill the process.
394 retval = EXCEPTION_EXECUTE_HANDLER;
395 return retval;
396}
397
398#else //!LL_WINDOWS
399void LLApp::setChildCallback(pid_t pid, LLAppChildCallback callback)
400{
401 LLChildInfo child_info;
402 child_info.mCallback = callback;
403 LLApp::sChildMap[pid] = child_info;
404}
405
406void LLApp::setDefaultChildCallback(LLAppChildCallback callback)
407{
408 LLApp::sDefaultChildCallback = callback;
409}
410
411pid_t LLApp::fork()
412{
413 pid_t pid = ::fork();
414 if( pid < 0 )
415 {
416 int system_error = errno;
417 llwarns << "Unable to fork! Operating system error code: "
418 << system_error << llendl;
419 }
420 else if (pid == 0)
421 {
422 // Sleep a bit to allow the parent to set up child callbacks.
423 ms_sleep(10);
424
425 // We need to disable signal handling, because we don't have a
426 // signal handling thread anymore.
427 setupErrorHandling();
428 }
429 else
430 {
431 llinfos << "Forked child process " << pid << llendl;
432 }
433 return pid;
434}
435
436void setup_signals()
437{
438 //
439 // Set up signal handlers that may result in program termination
440 //
441 struct sigaction act;
442 act.sa_sigaction = default_unix_signal_handler;
443 sigemptyset( &act.sa_mask );
444 act.sa_flags = SA_SIGINFO;
445
446 // Synchronous signals
447 sigaction(SIGABRT, &act, NULL);
448 sigaction(SIGALRM, &act, NULL);
449 sigaction(SIGBUS, &act, NULL);
450 sigaction(SIGFPE, &act, NULL);
451 sigaction(SIGHUP, &act, NULL);
452 sigaction(SIGILL, &act, NULL);
453 sigaction(SIGPIPE, &act, NULL);
454 sigaction(SIGSEGV, &act, NULL);
455 sigaction(SIGSYS, &act, NULL);
456
457 // Asynchronous signals that are normally ignored
458 sigaction(SIGCHLD, &act, NULL);
459 sigaction(SIGUSR2, &act, NULL);
460
461 // Asynchronous signals that result in attempted graceful exit
462 sigaction(SIGHUP, &act, NULL);
463 sigaction(SIGTERM, &act, NULL);
464 sigaction(SIGINT, &act, NULL);
465
466 // Asynchronous signals that result in core
467 sigaction(LL_SMACKDOWN_SIGNAL, &act, NULL);
468 sigaction(SIGQUIT, &act, NULL);
469}
470
471void clear_signals()
472{
473 struct sigaction act;
474 act.sa_handler = SIG_DFL;
475 sigemptyset( &act.sa_mask );
476 act.sa_flags = SA_SIGINFO;
477
478 // Synchronous signals
479 sigaction(SIGABRT, &act, NULL);
480 sigaction(SIGALRM, &act, NULL);
481 sigaction(SIGBUS, &act, NULL);
482 sigaction(SIGFPE, &act, NULL);
483 sigaction(SIGHUP, &act, NULL);
484 sigaction(SIGILL, &act, NULL);
485 sigaction(SIGPIPE, &act, NULL);
486 sigaction(SIGSEGV, &act, NULL);
487 sigaction(SIGSYS, &act, NULL);
488
489 // Asynchronous signals that are normally ignored
490 sigaction(SIGCHLD, &act, NULL);
491
492 // Asynchronous signals that result in attempted graceful exit
493 sigaction(SIGHUP, &act, NULL);
494 sigaction(SIGTERM, &act, NULL);
495 sigaction(SIGINT, &act, NULL);
496
497 // Asynchronous signals that result in core
498 sigaction(SIGUSR2, &act, NULL);
499 sigaction(LL_SMACKDOWN_SIGNAL, &act, NULL);
500 sigaction(SIGQUIT, &act, NULL);
501}
502
503
504
505void default_unix_signal_handler(int signum, siginfo_t *info, void *)
506{
507 // Unix implementation of synchronous signal handler
508 // This runs in the thread that threw the signal.
509 // We do the somewhat sketchy operation of blocking in here until the error handler
510 // has gracefully stopped the app.
511
512 if (LLApp::sLogInSignal)
513 {
514 llinfos << "Signal handler - Got signal " << signum << " - " << apr_signal_description_get(signum) << llendl;
515 }
516
517
518 switch (signum)
519 {
520 case SIGALRM:
521 case SIGPIPE:
522 case SIGUSR2:
523 // We don't care about these signals, ignore them
524 if (LLApp::sLogInSignal)
525 {
526 llinfos << "Signal handler - Ignoring this signal" << llendl;
527 }
528 return;
529 case SIGCHLD:
530 if (LLApp::sLogInSignal)
531 {
532 llinfos << "Signal handler - Got SIGCHLD from " << info->si_pid << llendl;
533 }
534
535 // Check result code for all child procs for which we've
536 // registered callbacks THIS WILL NOT WORK IF SIGCHLD IS SENT
537 // w/o killing the child (Go, launcher!)
538 // TODO: Now that we're using SIGACTION, we can actually
539 // implement the launcher behavior to determine who sent the
540 // SIGCHLD even if it doesn't result in child termination
541 if (LLApp::sChildMap.count(info->si_pid))
542 {
543 LLApp::sChildMap[info->si_pid].mGotSigChild = TRUE;
544 }
545
546 LLApp::incSigChildCount();
547
548 return;
549 case SIGABRT:
550 // Abort just results in termination of the app, no funky error handling.
551 if (LLApp::sLogInSignal)
552 {
553 llwarns << "Signal handler - Got SIGABRT, terminating" << llendl;
554 }
555 clear_signals();
556 raise(signum);
557 return;
558 case LL_SMACKDOWN_SIGNAL: // Smackdown treated just like any other app termination, for now
559 if (LLApp::sLogInSignal)
560 {
561 llwarns << "Signal handler - Handling smackdown signal!" << llendl;
562 }
563 else
564 {
565 // Don't log anything, even errors - this is because this signal could happen anywhere.
566 gErrorStream.setLevel(LLErrorStream::NONE);
567 }
568
569 // Change the signal that we reraise to SIGABRT, so we generate a core dump.
570 signum = SIGABRT;
571 case SIGBUS:
572 case SIGSEGV:
573 case SIGQUIT:
574 if (LLApp::sLogInSignal)
575 {
576 llwarns << "Signal handler - Handling fatal signal!" << llendl;
577 }
578 if (LLApp::isError())
579 {
580 // Received second fatal signal while handling first, just die right now
581 // Set the signal handlers back to default before handling the signal - this makes the next signal wipe out the app.
582 clear_signals();
583
584 if (LLApp::sLogInSignal)
585 {
586 llwarns << "Signal handler - Got another fatal signal while in the error handler, die now!" << llendl;
587 }
588 raise(signum);
589 return;
590 }
591
592 if (LLApp::sLogInSignal)
593 {
594 llwarns << "Signal handler - Flagging error status and waiting for shutdown" << llendl;
595 }
596 // Flag status to ERROR, so thread_error does its work.
597 LLApp::setError();
598 // Block in the signal handler until somebody says that we're done.
599 while (LLApp::sErrorThreadRunning && !LLApp::isStopped())
600 {
601 ms_sleep(10);
602 }
603
604 if (LLApp::sLogInSignal)
605 {
606 llwarns << "Signal handler - App is stopped, reraising signal" << llendl;
607 }
608 clear_signals();
609 raise(signum);
610 return;
611 case SIGINT:
612 case SIGHUP:
613 case SIGTERM:
614 if (LLApp::sLogInSignal)
615 {
616 llwarns << "Signal handler - Got SIGINT, HUP, or TERM, exiting gracefully" << llendl;
617 }
618 // Graceful exit
619 // Just set our state to quitting, not error
620 if (LLApp::isQuitting() || LLApp::isError())
621 {
622 // We're already trying to die, just ignore this signal
623 if (LLApp::sLogInSignal)
624 {
625 llinfos << "Signal handler - Already trying to quit, ignoring signal!" << llendl;
626 }
627 return;
628 }
629 LLApp::setQuitting();
630 return;
631 default:
632 if (LLApp::sLogInSignal)
633 {
634 llwarns << "Signal handler - Unhandled signal, ignoring!" << llendl;
635 }
636 }
637}
638
639#endif // !WINDOWS