/***************************************************************************** * * * DH_SIG.C * * * * Freely redistributable and modifiable. Use at your own risk. * * * * Copyright 1994 The Downhill Project * * * *****************************************************************************/ #ifdef HAVE_CONFIG_H #include #endif #define WIN32_LEAN_AND_MEAN 1 #include #include "psignal.h" extern int UserBreak; /* Define stuff ************************************************************ */ #ifndef TRUE # define TRUE 1 #endif #ifndef FALSE # define FALSE 0 #endif /* Signal groupings ======================================================== */ #define SIGNAL_DEFAULT_EXIT case SIGABRT: \ case SIGFPE: \ case SIGILL: \ case SIGINT: \ case SIGSEGV: \ case SIGTERM: #define IS_SIGNAL(a) ( ((a) > 0 ) && ((a) < NSIG)) /* Struct stuff **************************************************************/ struct downhill_Signal_Struct { void (*signal_Handler)(int); int signal_Count; int signal_Extra; sigset_t signal_Mask; int signal_Flags; }; /* Static stuff **************************************************************/ static struct downhill_Signal_Struct* downhill_Signal_Info = NULL; static sigset_t downhill_Sigset_Mask = 0; static HANDLE IGotASignal; /* Function stuff ************************************************************/ /* Handle a signal ========================================================= */ void raise(int signal_Number) { if (!IS_SIGNAL(signal_Number)) { errno = EINVAL; return; } /* Check to see if we're masking this signal */ if (sigismember(&downhill_Sigset_Mask,signal_Number)) { downhill_Signal_Info[signal_Number].signal_Count++; } else { /* Do a signal's action */ if (downhill_Signal_Info[signal_Number].signal_Handler == SIG_DFL) { switch (signal_Number) { SIGNAL_DEFAULT_EXIT exit(3); default: break; } } else if ( downhill_Signal_Info[signal_Number].signal_Handler == SIG_IGN ) {/* IGNORE */} else { void (*signal_HandlerOld)(int); /* Do we reset the handler? */ signal_HandlerOld = downhill_Signal_Info[signal_Number].signal_Handler; if (downhill_Signal_Info[signal_Number].signal_Flags& SA_RESETHAND) { downhill_Signal_Info[signal_Number]. signal_Handler = SIG_DFL; } /* Do the action */ if ((signal_Number == SIGCHLD) && (downhill_Signal_Info[signal_Number].signal_Flags& SA_NOCLDSTOP)) { /* Ignore SIGCHLD */ } else { sigset_t sigset_MaskOriginal = downhill_Sigset_Mask; sigset_t sigset_MaskNew = downhill_Signal_Info[ signal_Number].signal_Mask; /* Set the new signal mask */ sigaddset(&sigset_MaskNew,signal_Number); sigprocmask(SIG_BLOCK,&sigset_MaskNew,NULL); /* Execute the handler */ signal_HandlerOld(signal_Number); /* Restore the signal mask */ sigprocmask(SIG_SETMASK,&sigset_MaskOriginal, NULL); } } PulseEvent(IGotASignal); } } /* Init the signal re-direction ============================================ */ /* Hardware interrupt handler. */ static BOOL CALLBACK hwIntrHandler (DWORD type) { int ret; switch (type) { case CTRL_C_EVENT : case CTRL_BREAK_EVENT : /* Why SIGBREAK? SIGINT is used internally by R and the signal handler for SIGINT ends with a 'longjmp'. But, under Windows, hardware interrupt handler runs in a different thread. So, longjmp fails (tipically it will crash R). So, we raise a SIGBREAK to record that the user want to stop. This is then processed at due time. Drawback of this approach: if R is lost inside a C or Fortran routine we don't break it. I (g.m.) have tried to raise a signal in the appropriate thread using SuspendThread/GetThreadContext/setting Eip to the signal handler/ SetThreadContext/ResumeThread but I had success only under NT. */ raise(SIGBREAK); /* Seems that SIGBREAK is not working under 1,4,0, so do it via a semaphore, as RGui does */ UserBreak = 1; ret = TRUE; break; default: ret = FALSE; } return ret ; } static int downhill_Signal_Init(void) { /* Skip this if we've already done it */ if (downhill_Signal_Info == NULL) { if (!(downhill_Signal_Info = calloc(sizeof(struct downhill_Signal_Struct),NSIG)) || !(IGotASignal=CreateEvent(NULL,FALSE,FALSE,NULL)) || !SetConsoleCtrlHandler (hwIntrHandler, TRUE)) { if (downhill_Signal_Info) free(downhill_Signal_Info); if (IGotASignal) CloseHandle(IGotASignal); errno = ENOMEM; return FALSE; } } return TRUE; } /* Set a signal action ===================================================== */ int sigaction(int signal_Number,struct sigaction* sigaction_Info, struct sigaction* sigaction_InfoOld) { /* Make sure we're init'd */ if (!downhill_Signal_Init()) { return -1; } /* Set the signal */ if (IS_SIGNAL(signal_Number)) { if (sigaction_InfoOld != NULL) { sigaction_InfoOld->sa_handler = downhill_Signal_Info[signal_Number]. signal_Handler; sigaction_InfoOld->sa_mask = downhill_Signal_Info[signal_Number]. signal_Mask; sigaction_InfoOld->sa_flags = downhill_Signal_Info[signal_Number]. signal_Flags; } if (sigaction_Info != NULL) { downhill_Signal_Info[signal_Number]. signal_Handler = sigaction_Info->sa_handler; downhill_Signal_Info[signal_Number]. signal_Count = 0; downhill_Signal_Info[signal_Number]. signal_Extra = 0; downhill_Signal_Info[signal_Number]. signal_Mask = sigaction_Info->sa_mask; downhill_Signal_Info[signal_Number]. signal_Flags = sigaction_Info->sa_flags; } } else { errno = EINVAL; return -1; } return 0; } /* Set the action of a signal ============================================== */ sighandler_t signal(int signal_Number, sighandler_t signal_Handler) { sighandler_t signal_HandlerOld; /* Make sure we're init'd */ if (!IS_SIGNAL(signal_Number) || !downhill_Signal_Init() ) { return SIG_ERR; } signal_HandlerOld = downhill_Signal_Info[signal_Number].signal_Handler; downhill_Signal_Info[signal_Number].signal_Handler = signal_Handler; downhill_Signal_Info[signal_Number].signal_Count = 0; downhill_Signal_Info[signal_Number].signal_Extra = 0; downhill_Signal_Info[signal_Number].signal_Mask = 0; downhill_Signal_Info[signal_Number].signal_Flags = 0; return signal_HandlerOld; } /* Add a signal to a set =================================================== */ int sigaddset(sigset_t* sigset_Info,int signal_Number) { if (IS_SIGNAL(signal_Number)) { (*sigset_Info) |= (1 << (signal_Number - 1)); return 0; } else { errno = EINVAL; return -1; } return 0; } /* Remove a signal from a set ============================================== */ int sigdelset(sigset_t* sigset_Info,int signal_Number) { if (IS_SIGNAL(signal_Number)) { *sigset_Info &= ~(1<< (signal_Number - 1)); return 0; } else { errno = EINVAL; return -1; } return 0; } /* Empty a set ============================================================= */ int sigemptyset(sigset_t* sigset_Info) { *sigset_Info = 0; return 0; } /* Fill a set ============================================================== */ int sigfillset(sigset_t* sigset_Info) { *sigset_Info = (sigset_t)-1; return 0; } /* Checks if a signal is in a set ========================================== */ int sigismember(sigset_t* sigset_Info,int signal_Number) { if (IS_SIGNAL(signal_Number)) { if ( *sigset_Info & (1 << (signal_Number-1))) return 1; else return 0; } errno = EINVAL; return -1; } /* Returns the signals pending ============================================= */ int sigpending(sigset_t* sigset_Info) { int signal_Index; /* Make sure we're init'd */ if (!downhill_Signal_Init()) { return -1; } /* Check all the pending signals */ sigemptyset(sigset_Info); for (signal_Index = 1;signal_Index < NSIG;signal_Index++) { if (downhill_Signal_Info[signal_Index].signal_Count > 0) { sigaddset(sigset_Info,signal_Index); } } return 0; } /* Change the blocked signals ============================================== */ int sigprocmask(int mask_Function,sigset_t* sigset_Info, sigset_t* sigset_InfoOld) { int signal_Index; sigset_t sigset_MaskOld = downhill_Sigset_Mask; /* Make sure we're init'd */ if (!downhill_Signal_Init()) { return -1; } /* Return the current value */ if (sigset_InfoOld != NULL) { *sigset_InfoOld = sigset_MaskOld; } /* Set the new mask */ if (sigset_Info) { switch (mask_Function) { case SIG_BLOCK: downhill_Sigset_Mask |= (*sigset_Info); break; case SIG_UNBLOCK: downhill_Sigset_Mask &= ~(*sigset_Info); break; case SIG_SETMASK: downhill_Sigset_Mask = *sigset_Info; break; default: errno = EINVAL; return -1; } } /* And release any signals that were pending */ for (signal_Index = 1;signal_Index < NSIG;signal_Index++) { if ((!sigismember(&downhill_Sigset_Mask,signal_Index)) && (sigismember(&sigset_MaskOld,signal_Index)) && (downhill_Signal_Info[signal_Index].signal_Count > 0)) { downhill_Signal_Info[signal_Index].signal_Count = 0; raise(signal_Index); } } return 0; } /* Set signal mask ========================================================= */ int sigsetmask(int signal_MaskNew) { int signal_MaskOld = downhill_Sigset_Mask; if (sigprocmask(SIG_SETMASK, &signal_MaskNew, NULL) == -1) return (int)-1; return signal_MaskOld; } /* Add signals to mask ===================================================== */ int sigblock(int signal_MaskNew) { /* Block a specific group of signals */ return sigsetmask(downhill_Sigset_Mask|signal_MaskNew); } /* Hold a signal =========================================================== */ int sighold(int signal_Number) { /* Block a specific signal */ if (sigblock(sigmask(signal_Number)) == -1) { return -1; } return 0; } /* Release a signal ======================================================== */ int sigrelse(int signal_Number) { /* Release a specific signal */ if (sigsetmask(downhill_Sigset_Mask&(~sigmask(signal_Number))) == -1) { return -1; } return 0; } /* Pause until a signal ==================================================== */ int pause(void) { /* Wait for a signal */ WaitForSingleObject(IGotASignal,INFINITE); /* And return if we were interrupted */ errno = EINTR; return -1; } /* Suspend the process until a signal ====================================== */ int sigsuspend(sigset_t* sigset_Info) { sigset_t sigset_MaskOriginal = downhill_Sigset_Mask; /* Set the new mask */ sigprocmask(SIG_SETMASK,sigset_Info,NULL); /* Wait for the signal */ pause(); /* Reset the old mask */ sigprocmask(SIG_SETMASK,&sigset_MaskOriginal,NULL); return -1; }