def fork(nprocs=None): '''continue as ``nprocs`` parallel processes by forking ``nprocs-1`` times If ``nprocs`` exceeds the configured ``maxprocs`` than it will silently be capped. It is up to the user to prepare shared memory and/or locks for inter-process communication. As a safety measure nested forks are blocked by limiting nprocs to 1; all secondary forks will be silently ignored. ''' if nprocs is None or nprocs > _maxprocs.value: nprocs = _maxprocs.value if nprocs <= 1: yield 0 return if not hasattr(os, 'fork'): warnings.warn('fork is unavailable on this platform') yield 0 return amchild = False try: child_pids = [] for procid in builtins.range(1, nprocs): pid = os.fork() if not pid: # pragma: no cover amchild = True signal.signal( signal.SIGINT, signal.SIG_IGN) # disable sigint (ctrl+c) handler treelog.current = treelog.NullLog() # silence treelog break child_pids.append(pid) else: procid = 0 with maxprocs(1): yield procid except BaseException as e: if amchild: # pragma: no cover try: print('[parallel.fork] exception in child process:', e) finally: os._exit(1) # communicate failure to main process for pid in child_pids: # kill all child processes os.kill(pid, signal.SIGKILL) raise else: if amchild: # pragma: no cover os._exit(0) # communicate success to main process with treelog.context('waiting for child processes'): nfails = sum(not _wait(pid) for pid in child_pids) if nfails: # failure in child process: raise exception raise Exception('fork failed in {} out of {} processes'.format( nfails, nprocs)) finally: if amchild: # pragma: no cover os._exit(1) # failsafe
def fork(nprocs=None): '''continue as ``nprocs`` parallel processes by forking ``nprocs-1`` times If ``nprocs`` exceeds the configured ``maxprocs`` than it will silently be capped. It is up to the user to prepare shared memory and/or locks for inter-process communication. As a safety measure nested forks are blocked by limiting nprocs to 1; all secondary forks will be silently ignored. ''' if nprocs is None or nprocs > _maxprocs: nprocs = _maxprocs if nprocs == 1: yield 0 return if not hasattr(os, 'fork'): log.warning('fork is unavailable on this platform') yield 0 return child_pids = [] try: fail = 1 for procid in builtins.range(1, nprocs): pid = os.fork() if not pid: signal.signal( signal.SIGINT, signal.SIG_IGN) # disable sigint (ctrl+c) handler log.current = log.NullLog() break child_pids.append(pid) else: procid = 0 with maxprocs(1): yield procid fail = 0 finally: if procid: # before anything else can fail: os._exit(fail) # communicate exit status to main process nfails = fail + sum(os.waitpid(pid, 0)[1] != 0 for pid in child_pids) if fail: # failure in main process: exception has been reraised log.error( 'fork failed in {} out of {} processes; reraising exception for main process' .format(nfails, nprocs)) elif nfails: # failure in child process: raise exception raise Exception('fork failed in {} out of {} processes'.format( nfails, _maxprocs))
def output_tester(self): with self.assertSilent(): yield treelog.NullLog()