Add support for injecting activation functions into threads.
authorAditya Mandaleeka <adityam@microsoft.com>
Fri, 7 Aug 2015 01:43:43 +0000 (18:43 -0700)
committerAditya Mandaleeka <adityam@microsoft.com>
Thu, 13 Aug 2015 19:24:59 +0000 (12:24 -0700)
Use signals to interrupt the specified thread and have it
call the activation function passed in.

src/pal/inc/pal.h
src/pal/src/exception/signal.cpp

index 9c310a5..deae54b 100644 (file)
@@ -5413,6 +5413,16 @@ VOID
 PALAPI 
 FlushProcessWriteBuffers();
 
+typedef void (*PAL_ActivationFunction)(CONTEXT *context);
+
+PALIMPORT
+BOOL
+PALAPI
+PAL_InjectActivation(
+    IN HANDLE hThread,
+    IN PAL_ActivationFunction pActivationFunction
+);
+
 #define VER_PLATFORM_WIN32_WINDOWS        1
 #define VER_PLATFORM_WIN32_NT        2
 #define VER_PLATFORM_UNIX            10
index a2e2762..8ece9ee 100644 (file)
@@ -46,6 +46,8 @@ using namespace CorUnix;
 
 SET_DEFAULT_DEBUG_CHANNEL(EXCEPT);
 
+#define INJECT_ACTIVATION_SIGNAL SIGRTMIN
+
 /* local type definitions *****************************************************/
 
 #if !HAVE_SIGINFO_T
@@ -73,6 +75,8 @@ void CorUnix::resume_handler(int code, siginfo_t *siginfo, void *context);
 static void common_signal_handler(PEXCEPTION_POINTERS pointers, int code, 
                                   native_context_t *ucontext);
 
+static void inject_activation_handler(int code, siginfo_t *siginfo, void *context);
+
 static void handle_signal(int signal_id, SIGFUNC sigfunc, struct sigaction *previousAction);
 static void restore_signal(int signal_id, struct sigaction *previousAction);
 
@@ -138,6 +142,8 @@ BOOL SEHInitializeSignals()
     handle_signal(SIGUSR2, resume_handler, &g_previous_sigusr2);
 #endif
 
+    handle_signal(INJECT_ACTIVATION_SIGNAL, inject_activation_handler, NULL);
+
     /* The default action for SIGPIPE is process termination.
        Since SIGPIPE can be signaled when trying to write on a socket for which
        the connection has been dropped, we need to tell the system we want
@@ -561,6 +567,125 @@ static void sigbus_handler(int code, siginfo_t *siginfo, void *context)
 
 /*++
 Function :
+    inject_activation_handler
+
+    Handle the INJECT_ACTIVATION_SIGNAL signal. This signal interrupts a running thread
+    so it can call the activation function that was specified when sending the signal.
+
+Parameters :
+    POSIX signal handler parameter list ("man sigaction" for details)
+
+(no return value)
+--*/
+static void inject_activation_handler(int code, siginfo_t *siginfo, void *context)
+{
+    // Only accept activations from the current process
+    if (siginfo->si_pid == getpid())
+    {
+        PAL_ActivationFunction activation = (PAL_ActivationFunction)siginfo->si_value.sival_ptr;
+        if (activation != NULL)
+        {
+            native_context_t *ucontext = (native_context_t *)context;
+
+            CONTEXT winContext;
+            CONTEXTFromNativeContext(
+                ucontext, 
+                &winContext, 
+                CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT);
+
+            activation(&winContext);
+
+            // Activation function may have modified the context, so update it.
+            CONTEXTToNativeContext(&winContext, ucontext);
+        }
+    }
+}
+
+/*++
+Function :
+    InjectActivationInternal
+
+    Interrupt the specified thread and have it call the activationFunction passed in
+
+Parameters :
+    pThread            - target PAL thread
+    activationFunction - function to call 
+
+(no return value)
+--*/
+void InjectActivationInternal(CorUnix::CPalThread* pThread, PAL_ActivationFunction activationFunction)
+{
+    sigval value;
+    value.sival_ptr = (void*)activationFunction;
+    int status = pthread_sigqueue(pThread->GetPThreadSelf(), INJECT_ACTIVATION_SIGNAL, value);
+    if (status != 0)
+    {
+        // Failure to send the signal is fatal. There are only two cases when sending
+        // the signal can fail. First, if the signal ID is invalid and second, 
+        // if the thread doesn't exist anymore.
+        abort();
+    }
+}
+
+/*++
+Function:
+PAL_InjectActivation
+
+Interrupt the specified thread and have it call the activation function passed in
+
+Parameters:
+hThread            - handle of the target thread
+activationFunction - function to call 
+
+Return: 
+TRUE if it succeeded, FALSE otherwise.
+--*/
+BOOL
+PALAPI
+PAL_InjectActivation(
+    IN HANDLE hThread,
+    IN PAL_ActivationFunction pActivationFunction)
+{
+    PERF_ENTRY(PAL_InjectActivation);
+    ENTRY("PAL_InjectActivation(hThread=%p, pActivationFunction=%p)\n", hThread, pActivationFunction);
+
+    CPalThread *pCurrentThread;
+    CPalThread *pTargetThread;
+    IPalObject *pobjThread = NULL;
+
+    pCurrentThread = InternalGetCurrentThread();
+
+    PAL_ERROR palError = InternalGetThreadDataFromHandle(
+        pCurrentThread,
+        hThread,
+        0,
+        &pTargetThread,
+        &pobjThread
+        );
+
+    if (palError == NO_ERROR)
+    {
+        InjectActivationInternal(pTargetThread, pActivationFunction);
+    }
+    else
+    {
+        pCurrentThread->SetLastError(palError);
+    }
+
+    if (pobjThread != NULL)
+    {
+        pobjThread->ReleaseReference(pCurrentThread);
+    }
+
+    BOOL success = (palError == NO_ERROR);
+    LOGEXIT("PAL_InjectActivation returns:d\n", success);
+    PERF_EXIT(PAL_InjectActivation);
+
+    return success;
+}
+
+/*++
+Function :
     SEHSetSafeState
 
     specify whether the current thread is in a state where exception handling