Skip to content

Commit ce1ef65

Browse files
authored
Merge pull request #2771 from effigies/fix/picklable_procs
RF: Subclass non-daemon variants of all multiprocessing contexts
2 parents 9eee003 + f1f3845 commit ce1ef65

File tree

1 file changed

+59
-17
lines changed

1 file changed

+59
-17
lines changed

nipype/pipeline/plugins/legacymultiproc.py

+59-17
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
# Import packages
1313
import os
14+
import multiprocessing as mp
1415
from multiprocessing import Pool, cpu_count, pool
1516
from traceback import format_exception
1617
import sys
@@ -73,24 +74,65 @@ def run_node(node, updatehash, taskid):
7374
# Return the result dictionary
7475
return result
7576

77+
# Pythons 2.7, 3.4-3.7.0, and 3.7.1 have three different implementations of
78+
# pool.Pool().Process(), and the type of the result varies based on the default
79+
# multiprocessing context, so we need to dynamically patch the daemon property
80+
class NonDaemonMixin(object):
81+
@property
82+
def daemon(self):
83+
return False
84+
85+
@daemon.setter
86+
def daemon(self, val):
87+
pass
7688

77-
class NonDaemonPool(pool.Pool):
78-
"""A process pool with non-daemon processes.
79-
"""
80-
def Process(self, *args, **kwds):
81-
proc = super(NonDaemonPool, self).Process(*args, **kwds)
82-
83-
class NonDaemonProcess(proc.__class__):
84-
"""Monkey-patch process to ensure it is never daemonized"""
85-
@property
86-
def daemon(self):
87-
return False
88-
89-
@daemon.setter
90-
def daemon(self, val):
91-
pass
92-
proc.__class__ = NonDaemonProcess
93-
return proc
89+
try:
90+
from multiprocessing import context
91+
# Exists on all platforms
92+
class NonDaemonSpawnProcess(NonDaemonMixin, context.SpawnProcess):
93+
pass
94+
class NonDaemonSpawnContext(context.SpawnContext):
95+
Process = NonDaemonSpawnProcess
96+
_nondaemon_context_mapper = {
97+
'spawn': NonDaemonSpawnContext()
98+
}
99+
100+
# POSIX only
101+
try:
102+
class NonDaemonForkProcess(NonDaemonMixin, context.ForkProcess):
103+
pass
104+
class NonDaemonForkContext(context.ForkContext):
105+
Process = NonDaemonForkProcess
106+
_nondaemon_context_mapper['fork'] = NonDaemonForkContext()
107+
except AttributeError:
108+
pass
109+
# POSIX only
110+
try:
111+
class NonDaemonForkServerProcess(NonDaemonMixin, context.ForkServerProcess):
112+
pass
113+
class NonDaemonForkServerContext(context.ForkServerContext):
114+
Process = NonDaemonForkServerProcess
115+
_nondaemon_context_mapper['forkserver'] = NonDaemonForkServerContext()
116+
except AttributeError:
117+
pass
118+
119+
class NonDaemonPool(pool.Pool):
120+
def __init__(self, processes=None, initializer=None, initargs=(),
121+
maxtasksperchild=None, context=None):
122+
if context is None:
123+
context = mp.get_context()
124+
context = _nondaemon_context_mapper[context._name]
125+
super(NonDaemonPool, self).__init__(processes=processes,
126+
initializer=initializer,
127+
initargs=initargs,
128+
maxtasksperchild=maxtasksperchild,
129+
context=context)
130+
131+
except ImportError:
132+
class NonDaemonProcess(NonDaemonMixin, mp.Process):
133+
pass
134+
class NonDaemonPool(pool.Pool):
135+
Process = NonDaemonProcess
94136

95137

96138
class LegacyMultiProcPlugin(DistributedPluginBase):

0 commit comments

Comments
 (0)