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

Commit 323d175

Browse files
Add SIGTERM handling logic that properly shuts down the EE.
1 parent c264f88 commit 323d175

File tree

7 files changed

+258
-7
lines changed

7 files changed

+258
-7
lines changed

src/pal/inc/pal.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -480,9 +480,10 @@ typedef long time_t;
480480
#define PAL_INITIALIZE_SYNC_THREAD 0x01
481481
#define PAL_INITIALIZE_EXEC_ALLOCATOR 0x02
482482
#define PAL_INITIALIZE_STD_HANDLES 0x04
483+
#define PAL_INITIALIZE_SIGNAL_THREAD 0x08
483484

484485
// PAL_Initialize() flags
485-
#define PAL_INITIALIZE (PAL_INITIALIZE_SYNC_THREAD | PAL_INITIALIZE_STD_HANDLES)
486+
#define PAL_INITIALIZE (PAL_INITIALIZE_SYNC_THREAD | PAL_INITIALIZE_STD_HANDLES | PAL_INITIALIZE_SIGNAL_THREAD)
486487

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

64896490
typedef VOID (PALAPI *PHARDWARE_EXCEPTION_HANDLER)(PAL_SEHException* ex);
64906491
typedef BOOL (PALAPI *PHARDWARE_EXCEPTION_SAFETY_CHECK_FUNCTION)(PCONTEXT contextRecord, PEXCEPTION_RECORD exceptionRecord);
6492+
typedef VOID (PALAPI *PTERMINATION_REQUEST_HANDLER)();
64916493
typedef DWORD (PALAPI *PGET_GCMARKER_EXCEPTION_CODE)(LPVOID ip);
64926494

64936495
PALIMPORT
@@ -6510,6 +6512,12 @@ PAL_ThrowExceptionFromContext(
65106512
IN CONTEXT* context,
65116513
IN PAL_SEHException* ex);
65126514

6515+
PALIMPORT
6516+
VOID
6517+
PALAPI
6518+
PAL_SetTerminationRequestHandler(
6519+
IN PTERMINATION_REQUEST_HANDLER terminationRequestHandler);
6520+
65136521
//
65146522
// This holder is used to indicate that a hardware
65156523
// exception should be raised as a C++ exception

src/pal/src/exception/seh.cpp

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,22 @@ SET_DEFAULT_DEBUG_CHANNEL(EXCEPT);
4949
/* Bit 28 of exception codes is reserved. */
5050
const UINT RESERVED_SEH_BIT = 0x800000;
5151

52-
/* Internal variables definitions **********************************************/
52+
/* Internal variable definitions **********************************************/
5353

54-
PHARDWARE_EXCEPTION_HANDLER g_hardwareExceptionHandler = NULL;
5554
// Function to check if an activation can be safely injected at a specified context
56-
PHARDWARE_EXCEPTION_SAFETY_CHECK_FUNCTION g_safeExceptionCheckFunction = NULL;
55+
PHARDWARE_EXCEPTION_SAFETY_CHECK_FUNCTION g_safeExceptionCheckFunction = nullptr;
5756

58-
PGET_GCMARKER_EXCEPTION_CODE g_getGcMarkerExceptionCode = NULL;
57+
PHARDWARE_EXCEPTION_HANDLER g_hardwareExceptionHandler = nullptr;
58+
PGET_GCMARKER_EXCEPTION_CODE g_getGcMarkerExceptionCode = nullptr;
59+
PTERMINATION_REQUEST_HANDLER g_terminationRequestHandler = nullptr;
60+
61+
/* Internal function declarations *********************************************/
62+
63+
#if !HAVE_MACH_EXCEPTIONS
64+
PAL_ERROR
65+
StartExternalSignalHandlerThread(
66+
CPalThread *pthr);
67+
#endif // !HAVE_MACH_EXCEPTIONS
5968

6069
/* Internal function definitions **********************************************/
6170

@@ -83,6 +92,17 @@ SEHInitialize (CPalThread *pthrCurrent, DWORD flags)
8392
SEHCleanup();
8493
return FALSE;
8594
}
95+
96+
if (flags & PAL_INITIALIZE_SIGNAL_THREAD)
97+
{
98+
PAL_ERROR palError = StartExternalSignalHandlerThread(pthrCurrent);
99+
if (palError != NO_ERROR)
100+
{
101+
ERROR("StartExternalSignalHandlerThread returned %d\n", palError);
102+
SEHCleanup();
103+
return FALSE;
104+
}
105+
}
86106
#endif
87107

88108
return TRUE;
@@ -134,6 +154,26 @@ PAL_SetHardwareExceptionHandler(
134154
g_safeExceptionCheckFunction = exceptionCheckFunction;
135155
}
136156

157+
/*++
158+
Function:
159+
PAL_SetTerminationRequestHandler
160+
161+
Register a termination request handler.
162+
163+
Parameters:
164+
terminationHandler - handler for termination request
165+
166+
Return value:
167+
None
168+
--*/
169+
VOID
170+
PALAPI
171+
PAL_SetTerminationRequestHandler(
172+
IN PTERMINATION_REQUEST_HANDLER terminationHandler)
173+
{
174+
g_terminationRequestHandler = terminationHandler;
175+
}
176+
137177
/*++
138178
Function:
139179
PAL_SetGetGcMarkerExceptionCode
@@ -253,6 +293,15 @@ SEHProcessException(PEXCEPTION_POINTERS pointers)
253293
// Unhandled hardware exception pointers->ExceptionRecord->ExceptionCode at pointers->ExceptionRecord->ExceptionAddress
254294
}
255295

296+
VOID
297+
SEHHandleTerminationRequest()
298+
{
299+
if (g_terminationRequestHandler != NULL)
300+
{
301+
g_terminationRequestHandler();
302+
}
303+
}
304+
256305
/*++
257306
Function :
258307
SEHEnable

src/pal/src/exception/signal.cpp

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ static void sigtrap_handler(int code, siginfo_t *siginfo, void *context);
7272
static void sigbus_handler(int code, siginfo_t *siginfo, void *context);
7373
static void sigint_handler(int code, siginfo_t *siginfo, void *context);
7474
static void sigquit_handler(int code, siginfo_t *siginfo, void *context);
75+
static void sigterm_handler(int code, siginfo_t *siginfo, void *context);
7576

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

@@ -91,7 +92,12 @@ struct sigaction g_previous_sigbus;
9192
struct sigaction g_previous_sigsegv;
9293
struct sigaction g_previous_sigint;
9394
struct sigaction g_previous_sigquit;
95+
struct sigaction g_previous_sigterm;
9496

97+
// Pipe used for sending signal notifications to a helper thread
98+
int g_signalPipe[2] = { 0, 0 };
99+
100+
DWORD g_dwExternalSignalHandlerThreadId = 0;
95101

96102
/* public function definitions ************************************************/
97103

@@ -178,6 +184,13 @@ void SEHCleanupSignals()
178184
restore_signal(SIGSEGV, &g_previous_sigsegv);
179185
restore_signal(SIGINT, &g_previous_sigint);
180186
restore_signal(SIGQUIT, &g_previous_sigquit);
187+
188+
// Only restore if the signal handler thread was started and
189+
// the previous handler was saved.
190+
if (g_dwExternalSignalHandlerThreadId != 0)
191+
{
192+
restore_signal(SIGTERM, &g_previous_sigterm);
193+
}
181194
}
182195

183196
/* internal function definitions **********************************************/
@@ -384,6 +397,60 @@ static void sigquit_handler(int code, siginfo_t *siginfo, void *context)
384397
kill(gPID, code);
385398
}
386399

400+
/*++
401+
Function :
402+
HandleExternalSignal
403+
404+
Write to a pipe to kick off handling of the signal.
405+
406+
Parameters :
407+
signalCode - code of the external signal
408+
409+
(no return value)
410+
--*/
411+
static void HandleExternalSignal(int signalCode)
412+
{
413+
BYTE signalCodeByte = (BYTE)signalCode;
414+
ssize_t writtenBytes;
415+
do
416+
{
417+
writtenBytes = write(g_signalPipe[1], &signalCodeByte, 1);
418+
}
419+
while ((writtenBytes == -1) && (errno == EINTR));
420+
421+
if (writtenBytes == -1)
422+
{
423+
// Fatal error
424+
PROCAbort();
425+
}
426+
}
427+
428+
/*++
429+
Function :
430+
sigterm_handler
431+
432+
handle SIGTERM signal
433+
434+
Parameters :
435+
POSIX signal handler parameter list ("man sigaction" for details)
436+
437+
(no return value)
438+
--*/
439+
static void sigterm_handler(int code, siginfo_t *siginfo, void *context)
440+
{
441+
if (PALIsInitialized())
442+
{
443+
HandleExternalSignal(code);
444+
}
445+
else
446+
{
447+
if (g_previous_sigterm.sa_sigaction != NULL)
448+
{
449+
g_previous_sigterm.sa_sigaction(code, siginfo, context);
450+
}
451+
}
452+
}
453+
387454
#ifdef INJECT_ACTIVATION_SIGNAL
388455
/*++
389456
Function :
@@ -624,4 +691,111 @@ void restore_signal(int signal_id, struct sigaction *previousAction)
624691
}
625692
}
626693

694+
static
695+
DWORD
696+
PALAPI
697+
ExternalSignalHandlerThreadRoutine(
698+
PVOID
699+
);
700+
701+
PAL_ERROR
702+
StartExternalSignalHandlerThread(
703+
CPalThread *pthr)
704+
{
705+
PAL_ERROR palError = NO_ERROR;
706+
707+
#ifndef DO_NOT_USE_SIGNAL_HANDLING_THREAD
708+
HANDLE hThread;
709+
710+
if (pipe(g_signalPipe) != 0)
711+
{
712+
palError = ERROR_CANNOT_MAKE;
713+
goto done;
714+
}
715+
716+
palError = InternalCreateThread(
717+
pthr,
718+
NULL,
719+
0,
720+
ExternalSignalHandlerThreadRoutine,
721+
NULL,
722+
0,
723+
SignalHandlerThread, // Want no_suspend variant
724+
&g_dwExternalSignalHandlerThreadId,
725+
&hThread
726+
);
727+
728+
if (palError != NO_ERROR)
729+
{
730+
ERROR("Failure creating external signal handler thread (%d)\n", palError);
731+
goto done;
732+
}
733+
734+
InternalCloseHandle(pthr, hThread);
735+
736+
handle_signal(SIGTERM, sigterm_handler, &g_previous_sigterm);
737+
#endif // DO_NOT_USE_SIGNAL_HANDLING_THREAD
738+
739+
done:
740+
741+
return palError;
742+
}
743+
744+
static
745+
DWORD
746+
PALAPI
747+
ExternalSignalHandlerThreadRoutine(
748+
PVOID
749+
)
750+
{
751+
DWORD dwThreadId;
752+
bool fContinue = TRUE;
753+
HANDLE hThread;
754+
PAL_ERROR palError = NO_ERROR;
755+
CPalThread *pthr = InternalGetCurrentThread();
756+
757+
//
758+
// Wait for a signal to occur
759+
//
760+
761+
while (fContinue)
762+
{
763+
BYTE signalCode;
764+
ssize_t bytesRead;
765+
766+
do
767+
{
768+
bytesRead = read(g_signalPipe[0], &signalCode, 1);
769+
}
770+
while ((bytesRead == -1) && (errno == EINTR));
771+
772+
if (bytesRead == -1)
773+
{
774+
// Fatal error
775+
PROCAbort();
776+
}
777+
778+
switch (signalCode)
779+
{
780+
case SIGTERM:
781+
{
782+
SEHHandleTerminationRequest();
783+
}
784+
785+
default:
786+
ASSERT("Unexpected signal %d in signal thread\n", signalCode);
787+
PROCAbort();
788+
break;
789+
}
790+
}
791+
792+
//
793+
// Perform an immediate (non-graceful) shutdown
794+
//
795+
796+
_exit(EXIT_FAILURE);
797+
798+
return 0;
799+
}
800+
627801
#endif // !HAVE_MACH_EXCEPTIONS

src/pal/src/include/pal/process.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ void PROCAbort();
134134
Function:
135135
PROCNotifyProcessShutdown
136136
137-
Calls the abort handler to do any shutdown cleanup. Call be
137+
Calls the abort handler to do any shutdown cleanup. Can be
138138
called from the unhandled native exception handler.
139139
140140
(no return value)

src/pal/src/include/pal/seh.hpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,18 @@ Return value:
7474
VOID
7575
SEHProcessException(PEXCEPTION_POINTERS pointers);
7676

77+
/*++
78+
Function:
79+
SEHHandleTerminationRequest
80+
81+
Send a process termination request to a registered handler.
82+
83+
Parameters:
84+
None
85+
--*/
86+
VOID
87+
SEHHandleTerminationRequest();
88+
7789
#if !HAVE_MACH_EXCEPTIONS
7890
// TODO: Implement for Mach exceptions. Not in CoreCLR surface area.
7991
/*++

src/pal/src/thread/process.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2673,7 +2673,7 @@ DestroyProcessModules(IN ProcessModules *listHead)
26732673
Function:
26742674
PROCNotifyProcessShutdown
26752675
2676-
Calls the abort handler to do any shutdown cleanup. Call be called
2676+
Calls the abort handler to do any shutdown cleanup. Can be called
26772677
from the unhandled native exception handler.
26782678
26792679
(no return value)

src/vm/exceptionhandling.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,11 @@ static inline void UpdatePerformanceMetrics(CrawlFrame *pcfThisFrame, BOOL bIsRe
140140
ETW::ExceptionLog::ExceptionThrown(pcfThisFrame, bIsRethrownException, bIsNewException);
141141
}
142142

143+
void ShutdownEEAndExitProcess()
144+
{
145+
ForceEEShutdown(SCA_ExitProcessWhenShutdownComplete);
146+
}
147+
143148
void InitializeExceptionHandling()
144149
{
145150
EH_LOG((LL_INFO100, "InitializeExceptionHandling(): ExceptionTracker size: 0x%x bytes\n", sizeof(ExceptionTracker)));
@@ -159,6 +164,9 @@ void InitializeExceptionHandling()
159164

160165
// Register handler for determining whether the specified IP has code that is a GC marker for GCCover
161166
PAL_SetGetGcMarkerExceptionCode(GetGcMarkerExceptionCode);
167+
168+
// Register handler for termination requests (e.g. SIGTERM)
169+
PAL_SetTerminationRequestHandler(ShutdownEEAndExitProcess);
162170
#endif // FEATURE_PAL
163171
}
164172

0 commit comments

Comments
 (0)