Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Commit

Permalink
Add SIGTERM handling logic that properly shuts down the EE.
Browse files Browse the repository at this point in the history
  • Loading branch information
adityamandaleeka committed Apr 14, 2016
1 parent c264f88 commit 323d175
Show file tree
Hide file tree
Showing 7 changed files with 258 additions and 7 deletions.
10 changes: 9 additions & 1 deletion src/pal/inc/pal.h
Original file line number Diff line number Diff line change
Expand Up @@ -480,9 +480,10 @@ typedef long time_t;
#define PAL_INITIALIZE_SYNC_THREAD 0x01
#define PAL_INITIALIZE_EXEC_ALLOCATOR 0x02
#define PAL_INITIALIZE_STD_HANDLES 0x04
#define PAL_INITIALIZE_SIGNAL_THREAD 0x08

// PAL_Initialize() flags
#define PAL_INITIALIZE (PAL_INITIALIZE_SYNC_THREAD | PAL_INITIALIZE_STD_HANDLES)
#define PAL_INITIALIZE (PAL_INITIALIZE_SYNC_THREAD | PAL_INITIALIZE_STD_HANDLES | PAL_INITIALIZE_SIGNAL_THREAD)

// PAL_InitializeDLL() flags - don't start any of the helper threads
#define PAL_INITIALIZE_DLL PAL_INITIALIZE_NONE
Expand Down Expand Up @@ -6488,6 +6489,7 @@ struct PAL_SEHException

typedef VOID (PALAPI *PHARDWARE_EXCEPTION_HANDLER)(PAL_SEHException* ex);
typedef BOOL (PALAPI *PHARDWARE_EXCEPTION_SAFETY_CHECK_FUNCTION)(PCONTEXT contextRecord, PEXCEPTION_RECORD exceptionRecord);
typedef VOID (PALAPI *PTERMINATION_REQUEST_HANDLER)();
typedef DWORD (PALAPI *PGET_GCMARKER_EXCEPTION_CODE)(LPVOID ip);

PALIMPORT
Expand All @@ -6510,6 +6512,12 @@ PAL_ThrowExceptionFromContext(
IN CONTEXT* context,
IN PAL_SEHException* ex);

PALIMPORT
VOID
PALAPI
PAL_SetTerminationRequestHandler(
IN PTERMINATION_REQUEST_HANDLER terminationRequestHandler);

//
// This holder is used to indicate that a hardware
// exception should be raised as a C++ exception
Expand Down
57 changes: 53 additions & 4 deletions src/pal/src/exception/seh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,22 @@ SET_DEFAULT_DEBUG_CHANNEL(EXCEPT);
/* Bit 28 of exception codes is reserved. */
const UINT RESERVED_SEH_BIT = 0x800000;

/* Internal variables definitions **********************************************/
/* Internal variable definitions **********************************************/

PHARDWARE_EXCEPTION_HANDLER g_hardwareExceptionHandler = NULL;
// Function to check if an activation can be safely injected at a specified context
PHARDWARE_EXCEPTION_SAFETY_CHECK_FUNCTION g_safeExceptionCheckFunction = NULL;
PHARDWARE_EXCEPTION_SAFETY_CHECK_FUNCTION g_safeExceptionCheckFunction = nullptr;

PGET_GCMARKER_EXCEPTION_CODE g_getGcMarkerExceptionCode = NULL;
PHARDWARE_EXCEPTION_HANDLER g_hardwareExceptionHandler = nullptr;
PGET_GCMARKER_EXCEPTION_CODE g_getGcMarkerExceptionCode = nullptr;
PTERMINATION_REQUEST_HANDLER g_terminationRequestHandler = nullptr;

/* Internal function declarations *********************************************/

#if !HAVE_MACH_EXCEPTIONS
PAL_ERROR
StartExternalSignalHandlerThread(
CPalThread *pthr);
#endif // !HAVE_MACH_EXCEPTIONS

/* Internal function definitions **********************************************/

Expand Down Expand Up @@ -83,6 +92,17 @@ SEHInitialize (CPalThread *pthrCurrent, DWORD flags)
SEHCleanup();
return FALSE;
}

if (flags & PAL_INITIALIZE_SIGNAL_THREAD)
{
PAL_ERROR palError = StartExternalSignalHandlerThread(pthrCurrent);
if (palError != NO_ERROR)
{
ERROR("StartExternalSignalHandlerThread returned %d\n", palError);
SEHCleanup();
return FALSE;
}
}
#endif

return TRUE;
Expand Down Expand Up @@ -134,6 +154,26 @@ PAL_SetHardwareExceptionHandler(
g_safeExceptionCheckFunction = exceptionCheckFunction;
}

/*++
Function:
PAL_SetTerminationRequestHandler
Register a termination request handler.
Parameters:
terminationHandler - handler for termination request
Return value:
None
--*/
VOID
PALAPI
PAL_SetTerminationRequestHandler(
IN PTERMINATION_REQUEST_HANDLER terminationHandler)
{
g_terminationRequestHandler = terminationHandler;
}

/*++
Function:
PAL_SetGetGcMarkerExceptionCode
Expand Down Expand Up @@ -253,6 +293,15 @@ SEHProcessException(PEXCEPTION_POINTERS pointers)
// Unhandled hardware exception pointers->ExceptionRecord->ExceptionCode at pointers->ExceptionRecord->ExceptionAddress
}

VOID
SEHHandleTerminationRequest()
{
if (g_terminationRequestHandler != NULL)
{
g_terminationRequestHandler();
}
}

/*++
Function :
SEHEnable
Expand Down
174 changes: 174 additions & 0 deletions src/pal/src/exception/signal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ static void sigtrap_handler(int code, siginfo_t *siginfo, void *context);
static void sigbus_handler(int code, siginfo_t *siginfo, void *context);
static void sigint_handler(int code, siginfo_t *siginfo, void *context);
static void sigquit_handler(int code, siginfo_t *siginfo, void *context);
static void sigterm_handler(int code, siginfo_t *siginfo, void *context);

static void common_signal_handler(int code, siginfo_t *siginfo, void *sigcontext, int numParams, ...);

Expand All @@ -91,7 +92,12 @@ struct sigaction g_previous_sigbus;
struct sigaction g_previous_sigsegv;
struct sigaction g_previous_sigint;
struct sigaction g_previous_sigquit;
struct sigaction g_previous_sigterm;

// Pipe used for sending signal notifications to a helper thread
int g_signalPipe[2] = { 0, 0 };

DWORD g_dwExternalSignalHandlerThreadId = 0;

/* public function definitions ************************************************/

Expand Down Expand Up @@ -178,6 +184,13 @@ void SEHCleanupSignals()
restore_signal(SIGSEGV, &g_previous_sigsegv);
restore_signal(SIGINT, &g_previous_sigint);
restore_signal(SIGQUIT, &g_previous_sigquit);

// Only restore if the signal handler thread was started and
// the previous handler was saved.
if (g_dwExternalSignalHandlerThreadId != 0)
{
restore_signal(SIGTERM, &g_previous_sigterm);
}
}

/* internal function definitions **********************************************/
Expand Down Expand Up @@ -384,6 +397,60 @@ static void sigquit_handler(int code, siginfo_t *siginfo, void *context)
kill(gPID, code);
}

/*++
Function :
HandleExternalSignal
Write to a pipe to kick off handling of the signal.
Parameters :
signalCode - code of the external signal
(no return value)
--*/
static void HandleExternalSignal(int signalCode)
{
BYTE signalCodeByte = (BYTE)signalCode;
ssize_t writtenBytes;
do
{
writtenBytes = write(g_signalPipe[1], &signalCodeByte, 1);
}
while ((writtenBytes == -1) && (errno == EINTR));

if (writtenBytes == -1)
{
// Fatal error
PROCAbort();
}
}

/*++
Function :
sigterm_handler
handle SIGTERM signal
Parameters :
POSIX signal handler parameter list ("man sigaction" for details)
(no return value)
--*/
static void sigterm_handler(int code, siginfo_t *siginfo, void *context)
{
if (PALIsInitialized())
{
HandleExternalSignal(code);
}
else
{
if (g_previous_sigterm.sa_sigaction != NULL)
{
g_previous_sigterm.sa_sigaction(code, siginfo, context);
}
}
}

#ifdef INJECT_ACTIVATION_SIGNAL
/*++
Function :
Expand Down Expand Up @@ -624,4 +691,111 @@ void restore_signal(int signal_id, struct sigaction *previousAction)
}
}

static
DWORD
PALAPI
ExternalSignalHandlerThreadRoutine(
PVOID
);

PAL_ERROR
StartExternalSignalHandlerThread(
CPalThread *pthr)
{
PAL_ERROR palError = NO_ERROR;

#ifndef DO_NOT_USE_SIGNAL_HANDLING_THREAD
HANDLE hThread;

if (pipe(g_signalPipe) != 0)
{
palError = ERROR_CANNOT_MAKE;
goto done;
}

palError = InternalCreateThread(
pthr,
NULL,
0,
ExternalSignalHandlerThreadRoutine,
NULL,
0,
SignalHandlerThread, // Want no_suspend variant
&g_dwExternalSignalHandlerThreadId,
&hThread
);

if (palError != NO_ERROR)
{
ERROR("Failure creating external signal handler thread (%d)\n", palError);
goto done;
}

InternalCloseHandle(pthr, hThread);

handle_signal(SIGTERM, sigterm_handler, &g_previous_sigterm);
#endif // DO_NOT_USE_SIGNAL_HANDLING_THREAD

done:

return palError;
}

static
DWORD
PALAPI
ExternalSignalHandlerThreadRoutine(
PVOID
)
{
DWORD dwThreadId;
bool fContinue = TRUE;
HANDLE hThread;
PAL_ERROR palError = NO_ERROR;
CPalThread *pthr = InternalGetCurrentThread();

//
// Wait for a signal to occur
//

while (fContinue)
{
BYTE signalCode;
ssize_t bytesRead;

do
{
bytesRead = read(g_signalPipe[0], &signalCode, 1);
}
while ((bytesRead == -1) && (errno == EINTR));

if (bytesRead == -1)
{
// Fatal error
PROCAbort();
}

switch (signalCode)
{
case SIGTERM:
{
SEHHandleTerminationRequest();
}

default:
ASSERT("Unexpected signal %d in signal thread\n", signalCode);
PROCAbort();
break;
}
}

//
// Perform an immediate (non-graceful) shutdown
//

_exit(EXIT_FAILURE);

return 0;
}

#endif // !HAVE_MACH_EXCEPTIONS
2 changes: 1 addition & 1 deletion src/pal/src/include/pal/process.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ void PROCAbort();
Function:
PROCNotifyProcessShutdown
Calls the abort handler to do any shutdown cleanup. Call be
Calls the abort handler to do any shutdown cleanup. Can be
called from the unhandled native exception handler.
(no return value)
Expand Down
12 changes: 12 additions & 0 deletions src/pal/src/include/pal/seh.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,18 @@ Return value:
VOID
SEHProcessException(PEXCEPTION_POINTERS pointers);

/*++
Function:
SEHHandleTerminationRequest
Send a process termination request to a registered handler.
Parameters:
None
--*/
VOID
SEHHandleTerminationRequest();

#if !HAVE_MACH_EXCEPTIONS
// TODO: Implement for Mach exceptions. Not in CoreCLR surface area.
/*++
Expand Down
2 changes: 1 addition & 1 deletion src/pal/src/thread/process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2673,7 +2673,7 @@ DestroyProcessModules(IN ProcessModules *listHead)
Function:
PROCNotifyProcessShutdown
Calls the abort handler to do any shutdown cleanup. Call be called
Calls the abort handler to do any shutdown cleanup. Can be called
from the unhandled native exception handler.
(no return value)
Expand Down
8 changes: 8 additions & 0 deletions src/vm/exceptionhandling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,11 @@ static inline void UpdatePerformanceMetrics(CrawlFrame *pcfThisFrame, BOOL bIsRe
ETW::ExceptionLog::ExceptionThrown(pcfThisFrame, bIsRethrownException, bIsNewException);
}

void ShutdownEEAndExitProcess()
{
ForceEEShutdown(SCA_ExitProcessWhenShutdownComplete);
}

void InitializeExceptionHandling()
{
EH_LOG((LL_INFO100, "InitializeExceptionHandling(): ExceptionTracker size: 0x%x bytes\n", sizeof(ExceptionTracker)));
Expand All @@ -159,6 +164,9 @@ void InitializeExceptionHandling()

// Register handler for determining whether the specified IP has code that is a GC marker for GCCover
PAL_SetGetGcMarkerExceptionCode(GetGcMarkerExceptionCode);

// Register handler for termination requests (e.g. SIGTERM)
PAL_SetTerminationRequestHandler(ShutdownEEAndExitProcess);
#endif // FEATURE_PAL
}

Expand Down

0 comments on commit 323d175

Please sign in to comment.