added exception/signal/exit handlers to properly restore modified system settings like gamma ramps

This commit is contained in:
myT 2017-03-05 20:10:48 +01:00
parent d8bf2665f5
commit 39ee0db5b7
11 changed files with 280 additions and 81 deletions

View file

@ -698,8 +698,6 @@ static void CL_FirstSnapshot()
Cbuf_AddText( cl_activeAction->string );
Cvar_Set( "activeAction", "" );
}
Sys_BeginProfiling();
}

View file

@ -1892,6 +1892,9 @@ int Com_Milliseconds()
///////////////////////////////////////////////////////////////
#if defined(DEBUG)
// throw a fatal error to test error shutdown procedures
static void Com_Error_f( void )
@ -1921,6 +1924,12 @@ static void Com_Freeze_f( void )
}
static void Com_Exit_f( void )
{
exit( 666 );
}
// force a bus error for development reasons
static void Com_Crash_f( void )
@ -1929,6 +1938,9 @@ static void Com_Crash_f( void )
}
#endif
// TTimo: centralizing the cl_cdkey stuff after I discovered a buffer overflow problem with the dedicated server version
// not sure it's necessary to have different defaults for regular and dedicated, but I don't want to risk it
// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=470
@ -2211,12 +2223,12 @@ void Com_Init( char *commandLine )
}
}
if ( com_developer && com_developer->integer ) {
Cmd_AddCommand( "error", Com_Error_f );
Cmd_AddCommand( "crash", Com_Crash_f );
Cmd_AddCommand( "freeze", Com_Freeze_f );
//Cmd_AddCommand( "changeVectors", MSG_ReportChangeVectors_f );
}
#if defined(DEBUG)
Cmd_AddCommand( "error", Com_Error_f );
Cmd_AddCommand( "crash", Com_Crash_f );
Cmd_AddCommand( "freeze", Com_Freeze_f );
Cmd_AddCommand( "exit", Com_Exit_f );
#endif
Cmd_AddCommand( "quit", Com_Quit_f );
Cmd_AddCommand( "writeconfig", Com_WriteConfig_f );

View file

@ -976,9 +976,6 @@ const char* Sys_DefaultHomePath();
char** Sys_ListFiles( const char *directory, const char *extension, const char *filter, int *numfiles, qbool wantsubs );
void Sys_FreeFileList( char **list );
void Sys_BeginProfiling( void );
void Sys_EndProfiling( void );
qbool Sys_LowPhysicalMemory( void );

View file

@ -867,6 +867,7 @@ static int vidmode_MajorVersion = 0, vidmode_MinorVersion = 0; // major and mino
// gamma value of the X display before we start playing with it
static XF86VidModeGamma vidmode_InitialGamma = { -1,-1,-1 }; // drakkar - initialized to nonvalid values
static qbool vidmode_GammaSet = qfalse;
#endif /* HAVE_XF86DGA */
#ifdef HAVE_XF86DGA
@ -900,10 +901,26 @@ void GLimp_SetGamma( unsigned char red[256], unsigned char green[256], unsigned
gamma.red = g;
gamma.green = g;
gamma.blue = g;
XF86VidModeSetGamma(dpy, scrnum, &gamma);
if (XF86VidModeSetGamma(dpy, scrnum, &gamma))
{
vidmode_GammaSet = qtrue;
}
#endif /* HAVE_XF86DGA */
}
void LIN_RestoreGamma( void )
{
#ifdef HAVE_XF86DGA
if (dpy && glConfig.deviceSupportsGamma && vidmode_GammaSet)
{
if (XF86VidModeSetGamma(dpy, scrnum, &vidmode_InitialGamma) == True)
{
vidmode_GammaSet = qfalse;
}
}
#endif
}
/*
** GLimp_Shutdown
**
@ -933,10 +950,7 @@ void GLimp_Shutdown( void )
#ifdef HAVE_XF86DGA
if (vidmode_active)
XF86VidModeSwitchToMode(dpy, scrnum, vidmodes[0]);
if (glConfig.deviceSupportsGamma)
{
XF86VidModeSetGamma(dpy, scrnum, &vidmode_InitialGamma);
}
LIN_RestoreGamma();
#endif /* HAVE_XF86DGA */
// NOTE TTimo opening/closing the display should be necessary only once per run
@ -1401,8 +1415,6 @@ void GLimp_Init( void )
}
#endif
InitSig();
// set up our custom error handler for X failures
XSetErrorHandler(&qXErrorHandler);
@ -1429,8 +1441,6 @@ void GLimp_Init( void )
QGL_InitARB(); // compiles the shaders etc
QGL_SwapInterval( dpy, win, r_swapInterval->integer );
InitSig(); // not clear why this is at begin & end of function
IN_Init();
}

View file

@ -23,6 +23,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
void Sys_QueEvent( int time, sysEventType_t type, int value, int value2, int ptrLength, void *ptr );
void Sys_SendKeyEvents (void);
void Sys_Exit(int);
// Input subsystem
void IN_Init (void);
@ -37,4 +38,7 @@ void QGL_EnableLogging( qboolean enable );
void QGL_Shutdown( void );
// signals.c
void InitSig(void);
void SIG_Init(void);
// hardware gamma ramp
void LIN_RestoreGamma(void);

View file

@ -26,39 +26,133 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#ifndef DEDICATED
#include "../renderer/tr_local.h"
#endif
#include "linux_local.h"
static qboolean signalcaught = qfalse;;
void Sys_Exit(int); // bk010104 - abstraction
// columns: Symbol, IsCrash, Desc
#define SIGNAL_LIST(X) \
X(SIGHUP, qfalse, "hangup detected on controlling terminal or death of controlling process") \
X(SIGQUIT, qfalse, "quit from keyboard") \
X(SIGILL, qtrue, "illegal instruction") \
X(SIGTRAP, qfalse, "trace/breakpoint trap") \
X(SIGIOT, qtrue, "IOT trap (a synonym for SIGABRT)") \
X(SIGBUS, qtrue, "bus error (bad memory access)") \
X(SIGFPE, qtrue, "fatal arithmetic error") \
X(SIGSEGV, qtrue, "invalid memory reference") \
X(SIGTERM, qfalse, "termination signal") \
X(SIGINT, qfalse, "interrupt")
static void signal_handler(int sig) // bk010104 - replace this... (NOTE TTimo huh?)
static qboolean Sig_IsCrashSignal(int sig)
{
if (signalcaught)
{
printf("DOUBLE SIGNAL FAULT: Received signal %d, exiting...\n", sig);
Sys_Exit(1); // bk010104 - abstraction
}
#define SIGNAL_ITEM(Symbol, IsCrash, Desc) case Symbol: return IsCrash;
switch (sig) {
SIGNAL_LIST(SIGNAL_ITEM)
default: return qfalse;
}
#undef SIGNAL_ITEM
}
signalcaught = qtrue;
printf("Received signal %d, exiting...\n", sig);
static const char* Sig_GetDescription(int sig)
{
#define SIGNAL_ITEM(Symbol, IsCrash, Desc) case Symbol: return Desc;
switch (sig) {
SIGNAL_LIST(SIGNAL_ITEM)
default: return "unhandled signal";
}
#undef SIGNAL_ITEM
}
static const char* Sig_GetName(int sig)
{
#define SIGNAL_ITEM(Symbol, IsCrash, Desc) case Symbol: return #Symbol;
switch (sig) {
SIGNAL_LIST(SIGNAL_ITEM)
default: return "unhandled signal";
}
#undef SIGNAL_ITEM
}
// Every call in there needs to be safe when called more than once.
static void SIG_HandleCrash()
{
// We crashed and only care about restoring system settings
// that the process clean-up won't handle for us.
#ifndef DEDICATED
//GLimp_Shutdown(); // bk010104 - shouldn't this be CL_Shutdown
// rcg08312005 Agreed: changed to CL_Shutdown... --ryan.
CL_Shutdown();
LIN_RestoreGamma();
#endif
SV_Shutdown("Signal caught");
Sys_Exit(0); // bk010104 - abstraction NOTE TTimo send a 0 to avoid DOUBLE SIGNAL FAULT
}
void InitSig(void)
static void Sig_HandleSignal(int sig)
{
signal(SIGHUP, signal_handler);
signal(SIGQUIT, signal_handler);
signal(SIGILL, signal_handler);
signal(SIGTRAP, signal_handler);
signal(SIGIOT, signal_handler);
signal(SIGBUS, signal_handler);
signal(SIGFPE, signal_handler);
signal(SIGSEGV, signal_handler);
signal(SIGTERM, signal_handler);
static int faultCounter = 0;
static qbool crashHandled = qfalse;
faultCounter++;
if (faultCounter >= 3)
{
// We're here because the double fault handler failed.
// We take no chances this time and exit right away to avoid
// calling this function many more times.
exit(3);
}
const qbool crashed = Sig_IsCrashSignal(sig);
if (faultCounter == 2)
{
// The termination handler failed which means that if we exit right now,
// some system settings might still be in a bad state.
printf("DOUBLE SIGNAL FAULT: Received signal %d (%s), exiting...\n", sig, Sig_GetName(sig));
if (crashed && !crashHandled)
{
SIG_HandleCrash();
}
exit(2);
}
fprintf(crashed ? stderr : stdout, "Received %s signal %d: %s (%s), exiting...\n",
crashed ? "crash" : "termination", sig, Sig_GetName(sig), Sig_GetDescription(sig));
if (crashed)
{
SIG_HandleCrash();
crashHandled = qtrue;
exit(1);
}
else
{
// attempt a proper shutdown sequence
#ifndef DEDICATED
CL_Shutdown();
#endif
SV_Shutdown("Signal caught");
Sys_Exit(0);
}
}
void SIG_Init(void)
{
// This is unfortunately needed because some code might
// call exit and bypass all the clean-up work without
// there ever being a real crash.
// This happens for instance with the "fatal IO error"
// of the X server.
atexit(SIG_HandleCrash);
signal(SIGILL, Sig_HandleSignal);
signal(SIGIOT, Sig_HandleSignal);
signal(SIGBUS, Sig_HandleSignal);
signal(SIGFPE, Sig_HandleSignal);
signal(SIGSEGV, Sig_HandleSignal);
signal(SIGHUP, Sig_HandleSignal);
signal(SIGQUIT, Sig_HandleSignal);
signal(SIGTRAP, Sig_HandleSignal);
signal(SIGTERM, Sig_HandleSignal);
signal(SIGINT, Sig_HandleSignal);
}

View file

@ -436,24 +436,11 @@ qboolean Sys_LowPhysicalMemory()
}
void Sys_BeginProfiling( void ) {
}
// single exit point (regular exit or in case of signal fault)
void Sys_Exit( int ex )
{
Sys_ConsoleInputShutdown();
#ifdef NDEBUG // regular behavior
// We can't do this as long as GL DLL's keep installing with atexit...
//exit(ex);
_exit(ex);
#else
// Give me a backtrace on error exits.
assert( ex == 0 );
exit(ex);
#endif
}
@ -823,6 +810,8 @@ void Sys_Init()
int main( int argc, char* argv[] )
{
SIG_Init();
// merge the command line: we need it in a single chunk
int len = 1, i;
for (i = 1; i < argc; ++i)
@ -844,11 +833,6 @@ int main( int argc, char* argv[] )
fcntl( 0, F_SETFL, fcntl(0, F_GETFL, 0) | FNDELAY );
#ifdef DEDICATED
// init here for dedicated, as we don't have GLimp_Init
InitSig();
#endif
while (qtrue) {
// if running as a client but not focused, sleep a bit
// (servers have their own sleep path)

View file

@ -41,6 +41,8 @@ typedef struct
qbool cdsFullscreen;
qbool pixelFormatSet;
int nPendingPF;
qbool gammaRampSet; // qtrue if our custom ramp is active
} glwstate_t;
extern glwstate_t glw_state;

View file

@ -842,13 +842,14 @@ static void GLW_CheckHardwareGamma()
}
static void GLW_RestoreGamma()
void GLW_RestoreGamma()
{
if (!glConfig.deviceSupportsGamma)
if (!glConfig.deviceSupportsGamma || !glw_state.gammaRampSet)
return;
HDC hDC = GetDC( GetDesktopWindow() );
SetDeviceGammaRamp( hDC, s_oldHardwareGamma );
if ( SetDeviceGammaRamp( hDC, s_oldHardwareGamma ) )
glw_state.gammaRampSet = qfalse;
ReleaseDC( GetDesktopWindow(), hDC );
}
@ -878,7 +879,9 @@ void GLimp_SetGamma( unsigned char red[256], unsigned char green[256], unsigned
}
}
if ( !SetDeviceGammaRamp( glw_state.hDC, table ) ) {
if ( SetDeviceGammaRamp( glw_state.hDC, table ) ) {
glw_state.gammaRampSet = qtrue;
} else {
Com_Printf( "SetDeviceGammaRamp failed.\n" );
}
}

View file

@ -43,13 +43,14 @@ void SNDDMA_Activate();
LRESULT CALLBACK MainWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
void GLW_RestoreGamma();
typedef struct
{
HWND hWnd;
HINSTANCE hInstance;
qbool activeApp;
qbool isMinimized;
OSVERSIONINFO osversion;
// when we get a windows message, we store the time off
// so keyboard processing can know the exact time of an event

View file

@ -36,6 +36,29 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
WinVars_t g_wv;
static qbool win_timePeriodActive = qfalse;
static void Win_BeginTimePeriod()
{
if ( win_timePeriodActive )
return;
timeBeginPeriod( 1 );
win_timePeriodActive = qtrue;
}
static void Win_EndTimePeriod()
{
if ( !win_timePeriodActive )
return;
timeEndPeriod( 1 );
win_timePeriodActive = qfalse;
}
#define MEM_THRESHOLD 96*1024*1024
@ -48,11 +71,6 @@ qbool Sys_LowPhysicalMemory()
}
void Sys_BeginProfiling( void ) {
// this is just used on the mac build
}
// show the early console as an error dialog
void QDECL Sys_Error( const char *error, ... )
@ -70,7 +88,7 @@ void QDECL Sys_Error( const char *error, ... )
Sys_SetErrorText( text );
Sys_ShowConsole( 1, qtrue );
timeEndPeriod( 1 );
Win_EndTimePeriod();
#ifndef DEDICATED
IN_Shutdown();
@ -93,7 +111,7 @@ void QDECL Sys_Error( const char *error, ... )
void Sys_Quit()
{
timeEndPeriod( 1 );
Win_EndTimePeriod();
#ifndef DEDICATED
IN_Shutdown();
#endif
@ -520,7 +538,7 @@ static void Sys_Net_Restart_f( void )
void Sys_Init()
{
// make sure the timer is high precision, otherwise NT gets 18ms resolution
timeBeginPeriod( 1 );
Win_BeginTimePeriod();
#ifndef DEDICATED
Cmd_AddCommand( "in_restart", Sys_In_Restart_f );
@ -542,7 +560,7 @@ void Sys_Init()
///////////////////////////////////////////////////////////////
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
static int WinMainImpl( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
// should never get a previous instance in Win32
if ( hPrevInstance )
@ -553,9 +571,6 @@ int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin
// done before Com/Sys_Init since we need this for error output
Sys_CreateConsole();
// no abort/retry/fail errors
SetErrorMode( SEM_FAILCRITICALERRORS );
// get the initial time base
Sys_Milliseconds();
@ -605,3 +620,82 @@ int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin
// never gets here
}
//
// The exception handler's job is to reset system settings that won't get reset
// as part of the normal process clean-up by the OS.
// It can't do any memory allocation or use any synchronization objects.
// Ideally, we want it to be called before every abrupt application exit
// and right after any legitimate crash.
//
// There are 2 cases where the function won't be called:
//
// 1. Termination through the debugger.
// Our atexit handler never gets called.
//
// Work-around: Quit normally.
//
// 2. Breakpoints. The debugger has first-chance access and handles them.
// Our exception handler doesn't get called.
//
// Work-around: None for debugging. Quit normally.
//
static qbool exitCalled = qfalse;
LONG CALLBACK Win_HandleException( EXCEPTION_POINTERS* ep )
{
static const char* mbMsg = "CNQ3 crashed!\n\nOK to continue after attaching a debugger\nCancel to quit";
#if !DEDICATED
__try {
GLW_RestoreGamma();
} __except( EXCEPTION_EXECUTE_HANDLER ) {}
#endif
__try {
Win_EndTimePeriod();
} __except( EXCEPTION_EXECUTE_HANDLER ) {}
if ( exitCalled || IsDebuggerPresent() )
return EXCEPTION_CONTINUE_SEARCH;
#if defined(_DEBUG)
// ask if we want to debug the app
if ( MessageBoxA( NULL, mbMsg, "Crash", MB_OKCANCEL | MB_ICONERROR ) == IDOK &&
IsDebuggerPresent() )
return EXCEPTION_CONTINUE_SEARCH;
#endif
ExitProcess( 666 );
}
static void Win_HandleExit( void )
{
exitCalled = qtrue;
Win_HandleException( NULL );
}
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
// Register the exception handler for all threads present and future in this process.
// 1 means we're inserting the handler at the front of the queue.
// The debugger does still get first-chance access though.
// The handler is always called in the context of the thread raising the exception.
AddVectoredExceptionHandler( 1, Win_HandleException );
// Make sure we reset system settings even when someone calls exit.
atexit( Win_HandleExit );
// SetErrorMode(0) gets the current flags
// SEM_FAILCRITICALERRORS -> no abort/retry/fail errors
// SEM_NOGPFAULTERRORBOX -> the Windows Error Reporting dialog will not be shown
SetErrorMode( SetErrorMode(0) | SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX );
return WinMainImpl( hInstance, hPrevInstance, lpCmdLine, nCmdShow );
}