Ticket #13437: trac_13437-sigalrm.patch

File trac_13437-sigalrm.patch, 3.3 KB (added by nbruin, 7 years ago)

First try at cleaner use of signal.alarm

  • sage/parallel/use_fork.py

    # HG changeset patch
    # User Nils Bruin <nbruin@sfu.ca>
    # Date 1346992044 25200
    # Node ID e6beeb3b6be5afe9a479e50881a0e33f671558a8
    # Parent  5dddbca5e354e870cb99edfc3058c3bea88ff5bb
    #13437: Cleaner use of signal.alarm in p_iter_fork
    
    diff --git a/sage/parallel/use_fork.py b/sage/parallel/use_fork.py
    a b class p_iter_fork: 
    9292        sys.stderr.flush()
    9393
    9494        workers = {}
     95
     96        #we install a signal handler that does nothing on a SIGALRM
     97        #the effect is that a SIGALRM happening in a system call will still
     98        #terminate the system call with an EINTR
     99        def my_alrm_handler(a,b): pass
     100        #if you think this is too rique you can also just raise the appropriate error here
     101        #    raise OSError('[Errno 4] Interrupted system call (synthetic version)')
     102
     103        #make sure no alarms are pending
     104        signal.alarm(0)
     105
     106        #and install our handler (saving the old one!)
     107        sigalrm_orig=signal.signal(signal.SIGALRM,my_alrm_handler)
    95108        try:
    96109            while len(v) > 0 or len(workers) > 0:
    97110                # Spawn up to n subprocesses
    class p_iter_fork: 
    112125                    # Now wait for one subprocess to finish and report the result.
    113126                    # However, wait at most the time since the oldest process started.
    114127                    if timeout:
    115                         def mysig(a,b):
    116                             raise RuntimeError, "SIGALRM"
    117128                        oldest = min([X[1] for X in workers.values()])
    118                         signal.signal(signal.SIGALRM, mysig)
    119129                        signal.alarm(max(int(timeout - (walltime()-oldest)), 1))
    120130                    try:
     131                        #the following may be interrupted by an alarm, in which case
     132                        #python raises an OSError (an interrupted read would raise an IOError)
    121133                        pid = os.wait()[0]
    122                         signal.signal(signal.SIGALRM, signal.SIG_IGN)
    123                     except RuntimeError:
    124                         signal.signal(signal.SIGALRM, signal.SIG_IGN)
     134                        signal.alarm(0)        #clear any alarms
     135                    except (OSError, IOError):
     136                        #this should only happen on an alarm, but we clear alarms anyway
     137                        signal.alarm(0)
    125138                        # Kill workers that are too old
    126139                        for pid, X in workers.iteritems():
    127140                            if walltime() - X[1] > timeout:
    class p_iter_fork: 
    132145                                    sys.stdout.flush()
    133146                                os.kill(pid,9)
    134147                                X[-1] = ' (timed out)'
     148                    #Below this point there should be no alarms pending anymore. They have either
     149                    #gone off or have been cleared.
    135150                    else:
    136151                        # If the computation was interrupted the pid
    137152                        # might not be in the workers list, in which
    class p_iter_fork: 
    163178            sys.stdout.flush()
    164179
    165180        finally:
    166 
     181            signal.signal(signal.SIGALRM,sigalrm_orig) #restore original SIGALRM handler
    167182            # Clean up all temporary files.
    168183            try:
    169184                for X in os.listdir(dir):