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

Commit 3f99608

Browse files
Add SIGTERM handling logic that properly shuts down the EE.
1 parent ae2d2f3 commit 3f99608

File tree

7 files changed

+257
-5
lines changed

7 files changed

+257
-5
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
@@ -6487,6 +6488,7 @@ struct PAL_SEHException
64876488
};
64886489

64896490
typedef VOID (PALAPI *PHARDWARE_EXCEPTION_HANDLER)(PAL_SEHException* ex);
6491+
typedef VOID (PALAPI *PTERMINATION_REQUEST_HANDLER)();
64906492
typedef DWORD (PALAPI *PGET_GCMARKER_EXCEPTION_CODE)(LPVOID ip);
64916493

64926494
PALIMPORT
@@ -6508,6 +6510,12 @@ PAL_ThrowExceptionFromContext(
65086510
IN CONTEXT* context,
65096511
IN PAL_SEHException* ex);
65106512

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

src/pal/src/exception/seh.cpp

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,18 @@ const UINT RESERVED_SEH_BIT = 0x800000;
5151

5252
/* Internal variables definitions **********************************************/
5353

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

5767
/* Internal function definitions **********************************************/
5868

@@ -80,6 +90,17 @@ SEHInitialize (CPalThread *pthrCurrent, DWORD flags)
8090
SEHCleanup();
8191
return FALSE;
8292
}
93+
94+
if (flags & PAL_INITIALIZE_SIGNAL_THREAD)
95+
{
96+
PAL_ERROR palError = StartExternalSignalHandlerThread(pthrCurrent);
97+
if (palError != NO_ERROR)
98+
{
99+
ERROR("StartExternalSignalHandlerThread returned %d\n", palError);
100+
SEHCleanup();
101+
return FALSE;
102+
}
103+
}
83104
#endif
84105

85106
return TRUE;
@@ -129,6 +150,26 @@ PAL_SetHardwareExceptionHandler(
129150
g_hardwareExceptionHandler = exceptionHandler;
130151
}
131152

153+
/*++
154+
Function:
155+
PAL_SetTerminationRequestHandler
156+
157+
Register a termination request handler.
158+
159+
Parameters:
160+
terminationHandler - handler for termination request
161+
162+
Return value:
163+
None
164+
--*/
165+
VOID
166+
PALAPI
167+
PAL_SetTerminationRequestHandler(
168+
IN PTERMINATION_REQUEST_HANDLER terminationHandler)
169+
{
170+
g_terminationRequestHandler = terminationHandler;
171+
}
172+
132173
/*++
133174
Function:
134175
PAL_SetGetGcMarkerExceptionCode
@@ -225,6 +266,15 @@ SEHProcessException(PEXCEPTION_POINTERS pointers)
225266
pointers->ExceptionRecord->ExceptionCode, pointers->ExceptionRecord->ExceptionAddress);
226267
}
227268

269+
VOID
270+
SEHHandleTerminationRequest()
271+
{
272+
if (g_terminationRequestHandler != NULL)
273+
{
274+
g_terminationRequestHandler();
275+
}
276+
}
277+
228278
/*++
229279
Function :
230280
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(PEXCEPTION_POINTERS pointers, int code,
7778
native_context_t *ucontext);
@@ -92,7 +93,12 @@ struct sigaction g_previous_sigbus;
9293
struct sigaction g_previous_sigsegv;
9394
struct sigaction g_previous_sigint;
9495
struct sigaction g_previous_sigquit;
96+
struct sigaction g_previous_sigterm;
9597

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

97103
/* public function definitions ************************************************/
98104

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

184197
/* internal function definitions **********************************************/
@@ -494,6 +507,60 @@ static void sigquit_handler(int code, siginfo_t *siginfo, void *context)
494507
kill(gPID, code);
495508
}
496509

510+
/*++
511+
Function :
512+
HandleExternalSignal
513+
514+
Write to a pipe to kick off handling of the signal.
515+
516+
Parameters :
517+
signalCode - code of the external signal
518+
519+
(no return value)
520+
--*/
521+
static void HandleExternalSignal(int signalCode)
522+
{
523+
BYTE signalCodeByte = (BYTE)signalCode;
524+
ssize_t writtenBytes;
525+
do
526+
{
527+
writtenBytes = write(g_signalPipe[1], &signalCodeByte, 1);
528+
}
529+
while ((writtenBytes == -1) && (errno == EINTR));
530+
531+
if (writtenBytes == -1)
532+
{
533+
// Fatal error
534+
PROCAbort();
535+
}
536+
}
537+
538+
/*++
539+
Function :
540+
sigterm_handler
541+
542+
handle SIGTERM signal
543+
544+
Parameters :
545+
POSIX signal handler parameter list ("man sigaction" for details)
546+
547+
(no return value)
548+
--*/
549+
static void sigterm_handler(int code, siginfo_t *siginfo, void *context)
550+
{
551+
if (PALIsInitialized())
552+
{
553+
HandleExternalSignal(code);
554+
}
555+
else
556+
{
557+
if (g_previous_sigterm.sa_sigaction != NULL)
558+
{
559+
g_previous_sigterm.sa_sigaction(code, siginfo, context);
560+
}
561+
}
562+
}
563+
497564
#ifdef INJECT_ACTIVATION_SIGNAL
498565
/*++
499566
Function :
@@ -714,4 +781,111 @@ void restore_signal(int signal_id, struct sigaction *previousAction)
714781
}
715782
}
716783

784+
static
785+
DWORD
786+
PALAPI
787+
ExternalSignalHandlerThreadRoutine(
788+
PVOID
789+
);
790+
791+
PAL_ERROR
792+
StartExternalSignalHandlerThread(
793+
CPalThread *pthr)
794+
{
795+
PAL_ERROR palError = NO_ERROR;
796+
797+
#ifndef DO_NOT_USE_SIGNAL_HANDLING_THREAD
798+
HANDLE hThread;
799+
800+
if (pipe(g_signalPipe) != 0)
801+
{
802+
palError = ERROR_CANNOT_MAKE;
803+
goto done;
804+
}
805+
806+
palError = InternalCreateThread(
807+
pthr,
808+
NULL,
809+
0,
810+
ExternalSignalHandlerThreadRoutine,
811+
NULL,
812+
0,
813+
SignalHandlerThread, // Want no_suspend variant
814+
&g_dwExternalSignalHandlerThreadId,
815+
&hThread
816+
);
817+
818+
if (palError != NO_ERROR)
819+
{
820+
ERROR("Failure creating external signal handler thread (%d)\n", palError);
821+
goto done;
822+
}
823+
824+
InternalCloseHandle(pthr, hThread);
825+
826+
handle_signal(SIGTERM, sigterm_handler, &g_previous_sigterm);
827+
#endif // DO_NOT_USE_SIGNAL_HANDLING_THREAD
828+
829+
done:
830+
831+
return palError;
832+
}
833+
834+
static
835+
DWORD
836+
PALAPI
837+
ExternalSignalHandlerThreadRoutine(
838+
PVOID
839+
)
840+
{
841+
DWORD dwThreadId;
842+
bool fContinue = TRUE;
843+
HANDLE hThread;
844+
PAL_ERROR palError = NO_ERROR;
845+
CPalThread *pthr = InternalGetCurrentThread();
846+
847+
//
848+
// Wait for a signal to occur
849+
//
850+
851+
while (fContinue)
852+
{
853+
BYTE signalCode;
854+
ssize_t bytesRead;
855+
856+
do
857+
{
858+
bytesRead = read(g_signalPipe[0], &signalCode, 1);
859+
}
860+
while ((bytesRead == -1) && (errno == EINTR));
861+
862+
if (bytesRead == -1)
863+
{
864+
// Fatal error
865+
PROCAbort();
866+
}
867+
868+
switch (signalCode)
869+
{
870+
case SIGTERM:
871+
{
872+
SEHHandleTerminationRequest();
873+
}
874+
875+
default:
876+
ASSERT("Unexpected signal %d in signal thread\n", signalCode);
877+
PROCAbort();
878+
break;
879+
}
880+
}
881+
882+
//
883+
// Perform an immediate (non-graceful) shutdown
884+
//
885+
886+
_exit(EXIT_FAILURE);
887+
888+
return 0;
889+
}
890+
717891
#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
@@ -139,6 +139,11 @@ static inline void UpdatePerformanceMetrics(CrawlFrame *pcfThisFrame, BOOL bIsRe
139139
ETW::ExceptionLog::ExceptionThrown(pcfThisFrame, bIsRethrownException, bIsNewException);
140140
}
141141

142+
void ShutdownEEAndExitProcess()
143+
{
144+
ForceEEShutdown(SCA_ExitProcessWhenShutdownComplete);
145+
}
146+
142147
void InitializeExceptionHandling()
143148
{
144149
EH_LOG((LL_INFO100, "InitializeExceptionHandling(): ExceptionTracker size: 0x%x bytes\n", sizeof(ExceptionTracker)));
@@ -158,6 +163,9 @@ void InitializeExceptionHandling()
158163

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

0 commit comments

Comments
 (0)