summaryrefslogtreecommitdiffstats
path: root/src/misc/util/utilSignal.c
diff options
context:
space:
mode:
authorBaruch Sterin <baruchs@gmail.com>2011-02-01 11:13:53 -0800
committerBaruch Sterin <baruchs@gmail.com>2011-02-01 11:13:53 -0800
commitb538a5fad096764a686a68f843f74ee36d3c7ef1 (patch)
treebdeacf8e2427a12ecb5e047bf865c44b6ffc003a /src/misc/util/utilSignal.c
parent624af674a0e7f1a675917afaaf371db6a5588821 (diff)
downloadabc-b538a5fad096764a686a68f843f74ee36d3c7ef1.tar.gz
abc-b538a5fad096764a686a68f843f74ee36d3c7ef1.tar.bz2
abc-b538a5fad096764a686a68f843f74ee36d3c7ef1.zip
1. Replace system() with a function that responds to SIGINT. 2. Add functions to cleanup temporary files on SIGINT. 3. Fix bugs related to signal handling.
Diffstat (limited to 'src/misc/util/utilSignal.c')
-rw-r--r--src/misc/util/utilSignal.c520
1 files changed, 520 insertions, 0 deletions
diff --git a/src/misc/util/utilSignal.c b/src/misc/util/utilSignal.c
new file mode 100644
index 00000000..698ba6d6
--- /dev/null
+++ b/src/misc/util/utilSignal.c
@@ -0,0 +1,520 @@
+/**CFile****************************************************************
+
+ FileName [utilSignal.c]
+
+ SystemName [ABC: Logic synthesis and verification system.]
+
+ PackageName []
+
+ Synopsis []
+
+ Author []
+
+ Affiliation [UC Berkeley]
+
+ Date []
+
+ Revision []
+
+***********************************************************************/
+
+#ifndef _MSC_VER
+
+#include <main.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <hashGen.h>
+#include <errno.h>
+#include <pthread.h>
+
+#include "abc_global.h"
+
+ABC_NAMESPACE_IMPL_START
+
+////////////////////////////////////////////////////////////////////////
+/// DECLARATIONS ///
+////////////////////////////////////////////////////////////////////////
+
+static Hash_Gen_t* watched_pid_hash = NULL;
+static Hash_Gen_t* watched_tmp_files_hash = NULL;
+
+static sigset_t* old_procmask;
+
+////////////////////////////////////////////////////////////////////////
+/// FUNCTION DEFINITIONS ///
+////////////////////////////////////////////////////////////////////////
+
+/**Function*************************************************************
+
+ Synopsis []
+
+ Description [Kills all watched child processes and remove all watched termporary files.]
+
+ SideEffects []
+
+ SeeAlso []
+
+***********************************************************************/
+
+void Util_SignalCleanup()
+{
+ int i;
+ Hash_Gen_Entry_t* pEntry;
+
+ // kill all watched child processes
+ Hash_GenForEachEntry(watched_pid_hash, pEntry, i)
+ {
+ pid_t pid = (pid_t)pEntry->key;
+ pid_t ppid = (pid_t)pEntry->data;
+
+ if (getpid() == ppid)
+ {
+ kill(pid, SIGINT);
+ }
+ }
+
+ // remove watched temporary files
+ Hash_GenForEachEntry(watched_tmp_files_hash, pEntry, i)
+ {
+ int fname = (const char*)pEntry->key;
+ pid_t ppid = (pid_t)pEntry->data;
+
+ if( getpid() == ppid )
+ {
+ remove(fname);
+ }
+ }
+}
+
+/**Function*************************************************************
+
+ Synopsis []
+
+ Description [Sets up data structures needed for cleanup in signal handler.]
+
+ SideEffects []
+
+ SeeAlso []
+
+***********************************************************************/
+
+void Util_SignalStartHandler()
+{
+ watched_pid_hash = Hash_GenAlloc(100, Hash_DefaultHashFuncInt, Hash_DefaultCmpFuncInt, 0);
+ watched_tmp_files_hash = Hash_GenAlloc(100, Hash_DefaultHashFuncStr, strcmp, 1);
+}
+
+/**Function*************************************************************
+
+ Synopsis []
+
+ Description [Frees data structures used for clean up in signal handler.]
+
+ SideEffects []
+
+ SeeAlso []
+
+***********************************************************************/
+
+void Util_SignalResetHandler()
+{
+ int i;
+ Hash_Gen_Entry_t* pEntry;
+
+ sigset_t procmask, old_procmask;
+
+ sigemptyset(&procmask);
+ sigaddset(&procmask, SIGINT);
+
+ sigprocmask(SIG_BLOCK, &procmask, &old_procmask);
+
+ Hash_GenFree(watched_pid_hash);
+ watched_pid_hash = Hash_GenAlloc(100, Hash_DefaultHashFuncInt, Hash_DefaultCmpFuncInt, 0);
+
+ Hash_GenFree(watched_tmp_files_hash);
+ watched_tmp_files_hash = Hash_GenAlloc(100, Hash_DefaultHashFuncStr, strcmp, 1);
+
+ sigprocmask(SIG_SETMASK, &old_procmask, NULL);
+}
+
+void Util_SignalStopHandler()
+{
+ int i;
+ Hash_Gen_Entry_t* pEntry;
+
+ Hash_GenFree(watched_pid_hash);
+ watched_pid_hash = NULL;
+
+ Hash_GenFree(watched_tmp_files_hash);
+ watched_tmp_files_hash = NULL;
+}
+
+
+/**Function*************************************************************
+
+ Synopsis []
+
+ Description [Blocks SIGINT. For use when updating watched processes and temporary files to prevent race conditions with the signal handler.]
+
+ SideEffects []
+
+ SeeAlso []
+
+***********************************************************************/
+
+static int nblocks = 0;
+
+void Util_SignalBlockSignals()
+{
+ sigset_t procmask;
+
+ assert(nblocks==0);
+ nblocks ++ ;
+
+ sigemptyset(&procmask);
+ sigaddset(&procmask, SIGINT);
+
+ sigprocmask(SIG_BLOCK, &procmask, &old_procmask);
+}
+
+/**Function*************************************************************
+
+ Synopsis []
+
+ Description [Unblocks SIGINT after a call to Util_SignalBlockSignals.]
+
+ SideEffects []
+
+ SeeAlso []
+
+***********************************************************************/
+
+void Util_SignalUnblockSignals()
+{
+ assert( nblocks==1);
+ nblocks--;
+
+ sigprocmask(SIG_SETMASK, &old_procmask, NULL);
+}
+
+
+static void watch_tmp_file(const char* fname)
+{
+ if( watched_tmp_files_hash != NULL )
+ {
+ Hash_GenWriteEntry(watched_tmp_files_hash, Extra_UtilStrsav(fname), (void*)getpid() );
+ }
+}
+
+static void unwatch_tmp_file(const char* fname)
+{
+ if ( watched_tmp_files_hash )
+ {
+ assert( Hash_GenExists(watched_tmp_files_hash, fname) );
+ Hash_GenRemove(watched_tmp_files_hash, fname);
+ assert( !Hash_GenExists(watched_tmp_files_hash, fname) );
+ }
+}
+
+/**Function*************************************************************
+
+ Synopsis []
+
+ Description [Adds a process id to the list of processes that should be killed in a signal handler.]
+
+ SideEffects []
+
+ SeeAlso []
+
+***********************************************************************/
+
+void Util_SignalAddChildPid(int pid)
+{
+ if ( watched_pid_hash )
+ {
+ Hash_GenWriteEntry(watched_pid_hash, (void*)pid, (void*)getpid());
+ }
+}
+
+/**Function*************************************************************
+
+ Synopsis []
+
+ Description [Removes a process id from the list of processes that should be killed in a signal handler.]
+
+ SideEffects []
+
+ SeeAlso []
+
+***********************************************************************/
+
+void Util_SignalRemoveChildPid(int pid)
+{
+ if ( watched_pid_hash )
+ {
+ Hash_GenRemove(watched_pid_hash, (void*)pid);
+ }
+}
+
+// a dummy signal hanlder to make sure that SIGCHLD and SIGINT will cause sigsuspend() to return
+static int null_sig_handler(int signum)
+{
+ return 0;
+}
+
+
+// enusre that sigsuspend() returns when signal signum occurs -- sigsuspend() does not return if a signal is ignored
+static void replace_sighandler(int signum, struct sigaction* old_sa, int replace_dfl)
+{
+ sigaction(signum, NULL, old_sa);
+
+ if( old_sa->sa_handler == SIG_IGN || old_sa->sa_handler==SIG_DFL && replace_dfl)
+ {
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+
+ sa.sa_handler = null_sig_handler;
+
+ sigaction(signum, &sa, &old_sa);
+ }
+}
+
+//
+static int do_waitpid(pid_t pid, sigset_t* old_procmask)
+{
+ int status;
+
+ struct sigaction sigint_sa;
+ struct sigaction sigchld_sa;
+ sigset_t waitmask;
+
+ // ensure SIGINT and SIGCHLD are not blocked during sigsuspend()
+ memcpy(&waitmask, old_procmask, sizeof(sigset_t));
+
+ sigdelset(&waitmask, SIGINT);
+ sigdelset(&waitmask, SIGCHLD);
+
+ // ensure sigsuspend() returns if SIGINT or SIGCHLD occur, and save the current settings for SIGCHLD and SIGINT
+
+ replace_sighandler(SIGINT, &sigint_sa, 0);
+ replace_sighandler(SIGCHLD, &sigchld_sa, 1);
+
+ for(;;)
+ {
+ int rc;
+
+ // wait for a signal -- returns if SIGINT or SIGCHLD (or any other signal that is unblocked and not ignored) occur
+ sigsuspend(&waitmask);
+
+ // check if pid has terminated
+ rc = waitpid(pid, &status, WNOHANG);
+
+ // stop if terminated or some other error occurs
+ if( rc > 0 || rc == -1 && errno!=EINTR )
+ {
+ break;
+ }
+ }
+
+ // process is dead, should no longer be watched
+ Util_SignalRemoveChildPid(pid);
+
+ // restore original behavior of SIGINT and SIGCHLD
+ sigaction(SIGINT, &sigint_sa, NULL);
+ sigaction(SIGCHLD, &sigchld_sa, NULL);
+
+ return status;
+}
+
+static int do_system(const char* cmd, sigset_t* old_procmask)
+{
+ int pid;
+
+ pid = fork();
+
+ if (pid == -1)
+ {
+ // fork failed
+ return -1;
+ }
+ else if( pid == 0)
+ {
+ // child process
+ sigprocmask(SIG_SETMASK, old_procmask, NULL);
+ execl("/bin/sh", "sh", "-c", cmd, NULL);
+ _exit(127);
+ }
+
+ Util_SignalAddChildPid(pid);
+
+ return do_waitpid(pid, old_procmask);
+}
+
+/**Function*************************************************************
+
+ Synopsis []
+
+ Description [Replaces system() with a function that allows SIGINT to interrupt.]
+
+ SideEffects []
+
+ SeeAlso []
+
+***********************************************************************/
+
+int Util_SignalSystem(const char* cmd)
+{
+ int status;
+
+ sigset_t procmask;
+ sigset_t old_procmask;
+
+ // if signal handler is not installed, run the original system()
+ if ( ! watched_pid_hash && ! watched_tmp_files_hash )
+ return system(cmd);
+
+ // block SIGINT and SIGCHLD
+ sigemptyset(&procmask);
+
+ sigaddset(&procmask, SIGINT);
+ sigaddset(&procmask, SIGCHLD);
+
+ sigprocmask(SIG_BLOCK, &procmask, &old_procmask);
+
+ // call the actual function
+ status = do_system(cmd, &old_procmask);
+
+ // restore signal block mask
+ sigprocmask(SIG_SETMASK, &old_procmask, NULL);
+ return status;
+}
+
+/**Function*************************************************************
+
+ Synopsis []
+
+ Description []
+
+ SideEffects []
+
+ SeeAlso []
+
+***********************************************************************/
+
+////////////////////////////////////////////////////////////////////////
+/// END OF FILE ///
+////////////////////////////////////////////////////////////////////////
+
+#else /* #ifndef _MSC_VER */
+
+#include "abc_global.h"
+
+ABC_NAMESPACE_IMPL_START
+
+void Util_SignalCleanup()
+{
+}
+
+void Util_SignalStartHandler()
+{
+}
+
+void Util_SignalResetHandler()
+{
+}
+
+void Util_SignalStopHandler()
+{
+}
+
+void Util_SignalBlockSignals()
+{
+}
+
+void Util_SignalUnblockSignals()
+{
+}
+
+void watch_tmp_file(const char* fname)
+{
+}
+
+void unwatch_tmp_file(const char* fname)
+{
+}
+
+void Util_SignalAddChildPid(int pid)
+{
+}
+
+void Util_SignalRemoveChildPid(int pid)
+{
+}
+
+int Util_SignalSystem(const char* cmd)
+{
+ return system(cmd);
+}
+
+#endif /* #ifdef _MSC_VER */
+
+int tmpFile(const char* prefix, const char* suffix, char** out_name);
+
+/**Function*************************************************************
+
+ Synopsis []
+
+ Description [Create a temporary file and add it to the list of files to be cleaned up in the signal handler.]
+
+ SideEffects []
+
+ SeeAlso []
+
+***********************************************************************/
+
+int Util_SignalTmpFile(const char* prefix, const char* suffix, char** out_name)
+{
+ int fd;
+
+ Util_SignalBlockSignals();
+
+ fd = tmpFile(prefix, suffix, out_name);
+
+ if ( fd != -1 )
+ {
+ watch_tmp_file( *out_name );
+ }
+
+ Util_SignalUnblockSignals();
+
+ return fd;
+}
+
+/**Function*************************************************************
+
+ Synopsis []
+
+ Description [Remove a temporary file (and remove it from the watched files list.]
+
+ SideEffects []
+
+ SeeAlso []
+
+***********************************************************************/
+
+void Util_SignalTmpFileRemove(const char* fname, int fLeave)
+{
+ Util_SignalBlockSignals();
+
+ unwatch_tmp_file(fname);
+
+ if (! fLeave)
+ {
+ remove(fname);
+ }
+
+ Util_SignalUnblockSignals();
+}
+
+ABC_NAMESPACE_IMPL_END