@@ -76,6 +76,7 @@ def __init__(self, group=None, target=None, name=None, args=(), kwargs={},
7676 self ._config = _current_process ._config .copy ()
7777 self ._parent_pid = os .getpid ()
7878 self ._popen = None
79+ self ._closed = False
7980 self ._target = target
8081 self ._args = tuple (args )
8182 self ._kwargs = dict (kwargs )
@@ -85,6 +86,10 @@ def __init__(self, group=None, target=None, name=None, args=(), kwargs={},
8586 self .daemon = daemon
8687 _dangling .add (self )
8788
89+ def _check_closed (self ):
90+ if self ._closed :
91+ raise ValueError ("process object is closed" )
92+
8893 def run (self ):
8994 '''
9095 Method to be run in sub-process; can be overridden in sub-class
@@ -96,6 +101,7 @@ def start(self):
96101 '''
97102 Start child process
98103 '''
104+ self ._check_closed ()
99105 assert self ._popen is None , 'cannot start a process twice'
100106 assert self ._parent_pid == os .getpid (), \
101107 'can only start a process object created by current process'
@@ -110,12 +116,14 @@ def terminate(self):
110116 '''
111117 Terminate process; sends SIGTERM signal or uses TerminateProcess()
112118 '''
119+ self ._check_closed ()
113120 self ._popen .terminate ()
114121
115122 def join (self , timeout = None ):
116123 '''
117124 Wait until child process terminates
118125 '''
126+ self ._check_closed ()
119127 assert self ._parent_pid == os .getpid (), 'can only join a child process'
120128 assert self ._popen is not None , 'can only join a started process'
121129 res = self ._popen .wait (timeout )
@@ -126,6 +134,7 @@ def is_alive(self):
126134 '''
127135 Return whether process is alive
128136 '''
137+ self ._check_closed ()
129138 if self is _current_process :
130139 return True
131140 assert self ._parent_pid == os .getpid (), 'can only test a child process'
@@ -134,6 +143,23 @@ def is_alive(self):
134143 self ._popen .poll ()
135144 return self ._popen .returncode is None
136145
146+ def close (self ):
147+ '''
148+ Close the Process object.
149+
150+ This method releases resources held by the Process object. It is
151+ an error to call this method if the child process is still running.
152+ '''
153+ if self ._popen is not None :
154+ if self ._popen .poll () is None :
155+ raise ValueError ("Cannot close a process while it is still running. "
156+ "You should first call join() or terminate()." )
157+ self ._popen .close ()
158+ self ._popen = None
159+ del self ._sentinel
160+ _children .discard (self )
161+ self ._closed = True
162+
137163 @property
138164 def name (self ):
139165 return self ._name
@@ -174,6 +200,7 @@ def exitcode(self):
174200 '''
175201 Return exit code of process or `None` if it has yet to stop
176202 '''
203+ self ._check_closed ()
177204 if self ._popen is None :
178205 return self ._popen
179206 return self ._popen .poll ()
@@ -183,6 +210,7 @@ def ident(self):
183210 '''
184211 Return identifier (PID) of process or `None` if it has yet to start
185212 '''
213+ self ._check_closed ()
186214 if self is _current_process :
187215 return os .getpid ()
188216 else :
@@ -196,6 +224,7 @@ def sentinel(self):
196224 Return a file descriptor (Unix) or handle (Windows) suitable for
197225 waiting for process termination.
198226 '''
227+ self ._check_closed ()
199228 try :
200229 return self ._sentinel
201230 except AttributeError :
@@ -204,6 +233,8 @@ def sentinel(self):
204233 def __repr__ (self ):
205234 if self is _current_process :
206235 status = 'started'
236+ elif self ._closed :
237+ status = 'closed'
207238 elif self ._parent_pid != os .getpid ():
208239 status = 'unknown'
209240 elif self ._popen is None :
@@ -295,6 +326,7 @@ def __init__(self):
295326 self ._name = 'MainProcess'
296327 self ._parent_pid = None
297328 self ._popen = None
329+ self ._closed = False
298330 self ._config = {'authkey' : AuthenticationString (os .urandom (32 )),
299331 'semprefix' : '/mp' }
300332 # Note that some versions of FreeBSD only allow named
@@ -307,6 +339,9 @@ def __init__(self):
307339 # Everything in self._config will be inherited by descendant
308340 # processes.
309341
342+ def close (self ):
343+ pass
344+
310345
311346_current_process = _MainProcess ()
312347_process_counter = itertools .count (1 )
0 commit comments