diff options
author | Baruch Sterin <baruchs@gmail.com> | 2011-02-27 18:33:56 -0800 |
---|---|---|
committer | Baruch Sterin <baruchs@gmail.com> | 2011-02-27 18:33:56 -0800 |
commit | 34d59b0b91f09b35b7c3b4e74411f11b8b3994cc (patch) | |
tree | a721b34849df3243b145ba62b504ed41ab618dbc /src/python/pyabc.i | |
parent | 02081dba6779fbd247d1e62d5dc6db2097acc8e7 (diff) | |
download | abc-34d59b0b91f09b35b7c3b4e74411f11b8b3994cc.tar.gz abc-34d59b0b91f09b35b7c3b4e74411f11b8b3994cc.tar.bz2 abc-34d59b0b91f09b35b7c3b4e74411f11b8b3994cc.zip |
fixes to pyabc kill mechanism
Diffstat (limited to 'src/python/pyabc.i')
-rw-r--r-- | src/python/pyabc.i | 719 |
1 files changed, 691 insertions, 28 deletions
diff --git a/src/python/pyabc.i b/src/python/pyabc.i index 0bf58288..e258575e 100644 --- a/src/python/pyabc.i +++ b/src/python/pyabc.i @@ -23,9 +23,18 @@ %{ #include <main.h> +#include <utilCex.h> + #include <stdlib.h> #include <signal.h> -#include "utilSignal.h" + +#include <sys/prctl.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <fcntl.h> int n_ands() { @@ -160,6 +169,56 @@ int n_phases() return pNtk ? Abc_NtkPhaseFrameNum(pNtk) : 1; } +Abc_Cex_t* _cex_get() +{ + Abc_Frame_t* pAbc = Abc_FrameGetGlobalFrame(); + Abc_Cex_t* pCex = Abc_FrameReadCex(pAbc); + + if ( ! pCex ) + { + return NULL; + } + + return Abc_CexDup( pCex, -1 ); +} + +void _cex_put(Abc_Cex_t* pCex) +{ + Abc_Frame_t* pAbc = Abc_FrameGetGlobalFrame(); + + if ( pCex ) + { + pCex = Abc_CexDup(pCex, -1); + } + + Abc_FrameSetCex( Abc_CexDup(pCex, -1) ); +} + +void _cex_free(Abc_Cex_t* pCex) +{ + Abc_CexFree(pCex); +} + +int _cex_n_regs(Abc_Cex_t* pCex) +{ + return pCex->nRegs; +} + +int _cex_n_pis(Abc_Cex_t* pCex) +{ + return pCex->nPis; +} + +int _cex_get_po(Abc_Cex_t* pCex) +{ + return pCex->iPo; +} + +int _cex_get_frame(Abc_Cex_t* pCex) +{ + return pCex->iFrame; +} + static PyObject* pyabc_internal_python_command_callback = 0; void pyabc_internal_set_command_callback( PyObject* callback ) @@ -170,6 +229,8 @@ void pyabc_internal_set_command_callback( PyObject* callback ) pyabc_internal_python_command_callback = callback; } +PyThreadState *_save; + static int pyabc_internal_abc_command_callback(Abc_Frame_t * pAbc, int argc, char ** argv) { int i; @@ -183,6 +244,8 @@ static int pyabc_internal_abc_command_callback(Abc_Frame_t * pAbc, int argc, cha if ( !pyabc_internal_python_command_callback ) return 0; + Py_BLOCK_THREADS + args = PyList_New(argc); for( i=0 ; i<argc ; i++ ) @@ -196,19 +259,30 @@ static int pyabc_internal_abc_command_callback(Abc_Frame_t * pAbc, int argc, cha if ( !res ) { + Py_UNBLOCK_THREADS return -1; } lres = PyInt_AsLong(res); Py_DECREF(res); + Py_UNBLOCK_THREADS + return lres; } int run_command(char* cmd) { Abc_Frame_t* pAbc = Abc_FrameGetGlobalFrame(); - return Cmd_CommandExecute(pAbc, cmd); + int rc; + + Py_UNBLOCK_THREADS + + rc = Cmd_CommandExecute(pAbc, cmd); + + Py_BLOCK_THREADS + + return rc; } void pyabc_internal_register_command( char * sGroup, char * sName, int fChanges ) @@ -218,45 +292,212 @@ void pyabc_internal_register_command( char * sGroup, char * sName, int fChanges Cmd_CommandAdd( pAbc, sGroup, sName, (void*)pyabc_internal_abc_command_callback, fChanges); } -static void sigint_handler(int signum) +static int sigchld_pipe_fd = -1; + +static void sigchld_handler(int signum) +{ + while( write(sigchld_pipe_fd, "", 1) == -1 && errno==EINTR ) + ; +} + +static void install_sigchld_handler(int sigchld_fd) { - Util_SignalCleanup(); - _exit(1); + sigchld_pipe_fd = sigchld_fd; + signal(SIGCHLD, sigchld_handler); } -void add_child_pid(int pid) +static int sigint_pipe_fd = -1; + +static void sigint_handler(int signum) { - Util_SignalAddChildPid(pid); + unsigned char tmp = (unsigned char)signum; + while( write(sigint_pipe_fd, &tmp, 1) == -1 && errno==EINTR ) + ; } -void remove_child_pid(int pid) +static void install_sigint_handler(int sigint_fd) { - Util_SignalRemoveChildPid(pid); + sigint_pipe_fd = sigint_fd; + + signal(SIGINT, sigint_handler); + + // try to catch other signals that ask the process to terminate + signal(SIGABRT, sigint_handler); + signal(SIGQUIT, sigint_handler); + signal(SIGTERM, sigint_handler); + + // try to ensure cleanup on exceptional conditions + signal(SIGBUS, sigint_handler); + signal(SIGILL, sigint_handler); + signal(SIGSEGV, sigint_handler); + + // try to ensure cleanup before being killed due to resource limit + signal(SIGXCPU, sigint_handler); + signal(SIGXFSZ, sigint_handler); } +sigset_t old_procmask; +static int nblocks = 0; + void block_sigint() { - Util_SignalBlockSignals(); + sigset_t procmask; + + assert(nblocks==0); + nblocks ++ ; + + sigemptyset(&procmask); + sigaddset(&procmask, SIGINT); + + sigprocmask(SIG_BLOCK, &procmask, &old_procmask); } -void restore_sigint_block() +void unblock_sigint() { - Util_SignalUnblockSignals(); + assert( nblocks==1); + nblocks--; + + sigprocmask(SIG_SETMASK, &old_procmask, NULL); } -void reset_sigint_handler() +static PyObject* pyabc_internal_system_callback = 0; +static PyObject* pyabc_internal_tmpfile_callback = 0; +static PyObject* pyabc_internal_tmpfile_remove_callback = 0; + +int Util_SignalSystem(const char* cmd) { - Util_SignalResetHandler(); + PyObject* arglist; + PyObject* res; + + long lres; + + if ( !pyabc_internal_system_callback ) + return -1; + + Py_BLOCK_THREADS + + arglist = Py_BuildValue("(O)", PyString_FromString(cmd)); + Py_INCREF(arglist); + + res = PyEval_CallObject( pyabc_internal_system_callback, arglist ); + Py_DECREF(arglist); + + if ( !res ) + { + Py_UNBLOCK_THREADS + return -1; + } + + lres = PyInt_AsLong(res); + Py_DECREF(res); + + Py_UNBLOCK_THREADS + + return lres; +} + +int Util_SignalTmpFile(const char* prefix, const char* suffix, char** out_name) +{ + char* str; + Py_ssize_t size; + + PyObject* arglist; + PyObject* res; + + *out_name = NULL; + + if ( !pyabc_internal_tmpfile_callback ) + return 0; + + Py_BLOCK_THREADS + + arglist = Py_BuildValue("(ss)", prefix, suffix); + Py_INCREF(arglist); + + res = PyEval_CallObject( pyabc_internal_tmpfile_callback, arglist ); + Py_DECREF(arglist); + + if ( !res ) + { + Py_UNBLOCK_THREADS + return -1; + } + + PyString_AsStringAndSize(res, &str, &size); + + *out_name = ABC_ALLOC(char, size+1); + strcpy(*out_name, str); + + Py_DECREF(res); + + Py_UNBLOCK_THREADS + + return open(*out_name, O_WRONLY); +} + +void Util_SignalTmpFileRemove(const char* fname, int fLeave) +{ + PyObject* arglist; + PyObject* res; + + if ( !pyabc_internal_tmpfile_remove_callback ) + return; + + Py_BLOCK_THREADS + + arglist = Py_BuildValue("(si)", fname, fLeave); + Py_INCREF(arglist); + + res = PyEval_CallObject( pyabc_internal_tmpfile_remove_callback, arglist ); + Py_DECREF(arglist); + Py_XDECREF(res); + + Py_UNBLOCK_THREADS +} + +void pyabc_internal_set_util_callbacks( PyObject* system_callback, PyObject* tmpfile_callback, PyObject* tmpfile_remove_callback ) +{ + Py_XINCREF(system_callback); + Py_XDECREF(pyabc_internal_system_callback); + + pyabc_internal_system_callback = system_callback; + + Py_XINCREF(tmpfile_callback); + Py_XDECREF(pyabc_internal_tmpfile_callback); + + pyabc_internal_tmpfile_callback = tmpfile_callback; + + Py_XINCREF(tmpfile_remove_callback); + Py_XDECREF(pyabc_internal_tmpfile_remove_callback); + + pyabc_internal_tmpfile_remove_callback = tmpfile_remove_callback; } +PyObject* _wait_no_hang() +{ + int status; + int pid; + + pid = wait3(&status, WNOHANG, NULL); + + return Py_BuildValue("(iii)", pid, status, errno); +} + +int _posix_kill(int pid, int signum) +{ + return kill(pid, signum); +} + +void _set_death_signal() +{ + prctl(PR_SET_PDEATHSIG, SIGINT); +} %} %init %{ Abc_Start(); - Util_SignalStartHandler(); - signal(SIGINT, sigint_handler); %} int n_ands(); @@ -281,18 +522,442 @@ int cex_frame(); int n_phases(); +Abc_Cex_t* _cex_get(); +void _cex_put(Abc_Cex_t* pCex); +void _cex_free(Abc_Cex_t* pCex); +int _cex_n_regs(Abc_Cex_t* pCex); +int _cex_n_pis(Abc_Cex_t* pCex); +int _cex_get_po(Abc_Cex_t* pCex); +int _cex_get_frame(Abc_Cex_t* pCex); + void pyabc_internal_set_command_callback( PyObject* callback ); void pyabc_internal_register_command( char * sGroup, char * sName, int fChanges ); +void install_sigchld_handler(int sigint_fd); +void install_sigint_handler(int sigint_fd); + void block_sigint(); -void restore_sigint_block(); -void add_child_pid(int pid); -void remove_child_pid(int pid); -void reset_sigint_handler(); +void unblock_sigint(); + +void pyabc_internal_set_util_callbacks( PyObject* system_callback, PyObject* tmpfile_callback, PyObject* tmpfile_remove_callback ); + +PyObject* _wait_no_hang(); + +void _set_death_signal(); + +int _posix_kill(int pid, int signum); +void _set_death_signal(); %pythoncode %{ +class _Cex(object): + + def __init__(self, pCex): + self.pCex = pCex + + def __del__(self): + _cex_free(self.pCex) + + def n_regs(self): + return _cex_n_regs(self.pCex) + + def n_pis(self): + return _cex_n_pis(self.pCex) + + def get_po(self): + return _cex_get_po(self.pCex) + + def get_frame(self): + return _cex_get_frame(self.pCex) + +def cex_get(): + + cex = _cex_get() + + if cex is None: + return None + + return _Cex(_cex_get()) + +def cex_put(cex): + + assert cex is not None + assert cex.pCex is not None + + return _cex_put(cex.pCex) + +import threading +import select +import signal +import tempfile +import os +import errno +import sys, traceback +import subprocess + +_active_lock = threading.Lock() +_die_flag = False + +_active_pids = set() +_active_temp_files = set() + +_terminated_pids_cond = threading.Condition(_active_lock) +_terminated_pids = {} + +def add_temp_file(fname): + with _active_lock: + _active_temp_files.add(fname) + +def remove_temp_file(fname): + with _active_lock: + _active_temp_files.remove(fname) + +_old_os_wait3 = os.wait3 +_select_select = select.select + +def _retry_select(fd): + while True: + try: + rrdy,_,_ = _select_select([fd],[],[]) + if fd in rrdy: + return + except select.error as e: + if e[0] == errno.EINTR: + continue + raise + +def _retry_read(fd): + + while True: + try: + return fd.read(1) + except OSError as e: + if e.errno == errno.EINTR: + continue + raise + +def _retry_wait(): + + while True: + + pid, status, e = _wait_no_hang() + + if pid>0: + return pid, status + + elif pid==0: + return 0,0 + + elif pid == -1 and e == errno.ECHILD: + return 0,0 + + elif pid==-1 and e != errno.EINTR: + raise OSError(e, 'unknown error in wait3()') + +def _sigint_wait_thread_func(fd): + + global _die_flag + + while True: + + _retry_select(fd) + _retry_read(fd) + + with _active_lock: + + if _die_flag: + os._exit(-1) + + _die_flag = True + + for pid in _active_pids: + rc = _posix_kill(pid, signal.SIGINT) + + for fname in _active_temp_files: + os.remove(fname) + + os._exit(-1) + +def _child_wait_thread_func(fd): + + while True: + + _retry_select(fd) + rc = _retry_read(fd) + + with _active_lock: + + while True: + + pid, status = _retry_wait() + + if pid==0: + break + + if pid in _active_pids: + _active_pids.remove(pid) + + _terminated_pids[pid] = status + _terminated_pids_cond.notifyAll() + +_sigint_pipe_read_fd = -1 +_sigint_pipe_write_fd = -1 + +_sigchld_pipe_read_fd = -1 +_sigchld_pipe_write_fd = -1 + +def _start_threads(): + + global _sigint_pipe_read_fd, _sigint_pipe_write_fd + + _sigint_pipe_read_fd, _sigint_pipe_write_fd = os.pipe() + sigint_read = os.fdopen(_sigint_pipe_read_fd, "r", 0 ) + + sigint_wait_thread = threading.Thread(target=_sigint_wait_thread_func, name="SIGINT wait thread", args=(sigint_read,)) + sigint_wait_thread.setDaemon(True) + sigint_wait_thread.start() + + install_sigint_handler(_sigint_pipe_write_fd) + + global _sigchld_pipe_read_fd, _sigchld_pipe_write_fd + + _sigchld_pipe_read_fd, _sigchld_pipe_write_fd = os.pipe() + sigchld_read = os.fdopen(_sigchld_pipe_read_fd, "r", 0 ) + + child_wait_thread = threading.Thread(target=_child_wait_thread_func, name="child process wait thread", args=(sigchld_read,)) + child_wait_thread.setDaemon(True) + child_wait_thread.start() + + install_sigchld_handler(_sigchld_pipe_write_fd) + +_close_on_fork = [] + +def close_on_fork(fd): + _close_on_fork.append(fd) + +def after_fork(): + + _set_death_signal() + + global _close_on_fork + + for fd in _close_on_fork: + os.close(fd) + + _close_on_fork = [] + + os.close(_sigint_pipe_read_fd) + os.close(_sigint_pipe_write_fd) + + os.close(_sigchld_pipe_read_fd) + os.close(_sigchld_pipe_write_fd) + + global _active_lock + _active_lock = threading.Lock() + + global _terminated_pids_cond + _terminated_pids_cond = threading.Condition(_active_lock) + + global _terminated_pids + _terminated_pids = {} + + global _active_pids + _active_pids = set() + + global _active_temp_files + _active_temp_files = set() + + _start_threads() + +class _sigint_block_section(object): + def __init__(self): + self.blocked = False + + def __enter__(self): + block_sigint() + self.blocked = True + + def __exit__(self, type, value, traceback): + self.release() + + def release(self): + if self.blocked: + self.blocked = False + unblock_sigint() + +_old_os_fork = os.fork + +def _fork(): + + ppid = os.getpid() + + with _sigint_block_section() as cs: + + with _active_lock: + + if _die_flag: + os._exit(-1) + + pid = _old_os_fork() + + if pid == 0: + after_fork() + + if pid > 0: + _active_pids.add(pid) + + return pid + +def _waitpid(pid, options=0): + + while True: + + with _active_lock: + + if pid in _terminated_pids: + status = _terminated_pids[pid] + del _terminated_pids[pid] + return pid, status + + if options==os.WNOHANG: + return 0, 0 + + _terminated_pids_cond.wait() + +def _wait(options=0): + + while True: + + with _active_lock: + + for pid, status in _terminated_pids.iteritems(): + del _terminated_pids[pid] + return pid, status + + if options==os.WNOHANG: + return 0, 0 + + _terminated_pids_cond.wait() + +_old_os_kill = os.kill + +def _kill(pid, sig): + + with _active_lock: + + if pid in _terminated_pids: + return None + + return _old_os_kill(pid,sig) + +os.kill = _kill +os.fork = _fork +os.wait = _wait +os.waitpid = _waitpid + +def _split_command_line(cmd): + + args = [] + + i=0 + + while i<len(cmd): + + while i<len(cmd) and cmd[i] in [' ','\t','\f']: + i += 1 + + if i >= len(cmd): + break + + arg = [] + + in_quotes = None + + while i<len(cmd): + + if not in_quotes and cmd[i] in ['\'','\"','\'']: + in_quotes = cmd[i] + + elif in_quotes and cmd[i]==in_quotes: + in_quotes = None + + elif cmd[i] == '\\' and i<(len(cmd)+1): + + i += 1 + + if cmd[i]=='\\': + arg.append('\\') + elif cmd[i]=='\'': + arg.append('\'') + elif cmd[i]=='\"': + arg.append('\'') + elif cmd[i]=='\"': + arg.append('\"') + elif cmd[i]=='a': + arg.append('\a') + elif cmd[i]=='b': + arg.append('\b') + elif cmd[i]=='n': + arg.append('\n') + elif cmd[i]=='f': + arg.append('\f') + elif cmd[i]=='r': + arg.append('\r') + elif cmd[i]=='t': + arg.append('\t') + elif cmd[i]=='v': + arg.append('\v') + else: + arg.append(cmd[i]) + + elif not in_quotes and cmd[i] in [' ','\t','\f']: + break + + else: + arg.append(cmd[i]) + + i += 1 + + args.append( "".join(arg) ) + + return args + + +def system(cmd): + + args = _split_command_line(cmd) + + if args[-2] == '>': + + with open(args[-1],'w') as fout: + p = subprocess.Popen(args[:-2], stdout=fout) + rc = p.wait() + return rc + + else: + p = subprocess.Popen(args) + return p.wait() + +def tmpfile(prefix, suffix): + + with _active_lock: + with tempfile.NamedTemporaryFile(delete=False, prefix=prefix, suffix=suffix) as file: + _active_temp_files.add(file.name) + return file.name + +def tmpfile_remove(fname, leave): + + with _active_lock: + os.remove(fname) + _active_temp_files.remove(fname) + +pyabc_internal_set_util_callbacks( system, tmpfile,tmpfile_remove ) + + +_start_threads() + + _registered_commands = {} def _cmd_callback(args): @@ -309,7 +974,8 @@ def _cmd_callback(args): return res except Exception, e: - print "Python error: ", e + import traceback + traceback.print_exc() except SystemExit, se: pass @@ -322,14 +988,11 @@ def add_abc_command(fcmd, group, cmd, change): _registered_commands[ cmd ] = fcmd pyabc_internal_register_command( group, cmd, change) -import sys import optparse -import os.path -import __main__ +xxx = {} def cmd_python(cmd_args): - global __main__ usage = "usage: %prog [options] <Python files>" @@ -345,7 +1008,7 @@ def cmd_python(cmd_args): return 0 if options.cmd: - exec options.cmd in __main__.__dict__ + exec options.cmd in xxx return 0 scripts_dir = os.getenv('ABC_PYTHON_SCRIPTS', ".") @@ -353,12 +1016,12 @@ def cmd_python(cmd_args): for fname in args[1:]: if os.path.isabs(fname): - execfile(fname, __main__.__dict__) + execfile(fname, xxx) else: for d in scripts_dirs: fname = os.path.join(scripts_dir, fname) if os.path.exists(fname): - execfile(fname, __main__.__dict__) + execfile(fname, xxx) break return 0 |