Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitc09a9f5

Browse files
tomMoralpitrou
authored andcommitted
bpo-36888: Add multiprocessing.parent_process() (GH-13247)
1 parent5ae1c84 commitc09a9f5

File tree

12 files changed

+155
-20
lines changed

12 files changed

+155
-20
lines changed

‎Doc/library/multiprocessing.rst‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -944,6 +944,14 @@ Miscellaneous
944944

945945
An analogue of:func:`threading.current_thread`.
946946

947+
..function::parent_process()
948+
949+
Return the:class:`Process` object corresponding to the parent process of
950+
the:func:`current_process`. For the main process, ``parent_process`` will
951+
be ``None``.
952+
953+
..versionadded::3.8
954+
947955
..function::freeze_support()
948956

949957
Add support for when a program which uses:mod:`multiprocessing` has been

‎Lib/multiprocessing/context.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class BaseContext(object):
3535
AuthenticationError=AuthenticationError
3636

3737
current_process=staticmethod(process.current_process)
38+
parent_process=staticmethod(process.parent_process)
3839
active_children=staticmethod(process.active_children)
3940

4041
defcpu_count(self):

‎Lib/multiprocessing/forkserver.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,8 @@ def _serve_one(child_r, fds, unused_fds, handlers):
294294
*_forkserver._inherited_fds)=fds
295295

296296
# Run process object received over pipe
297-
code=spawn._main(child_r)
297+
parent_sentinel=os.dup(child_r)
298+
code=spawn._main(child_r,parent_sentinel)
298299

299300
returncode
300301

‎Lib/multiprocessing/popen_fork.py‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,20 @@ def kill(self):
6666
def_launch(self,process_obj):
6767
code=1
6868
parent_r,child_w=os.pipe()
69+
child_r,parent_w=os.pipe()
6970
self.pid=os.fork()
7071
ifself.pid==0:
7172
try:
7273
os.close(parent_r)
73-
code=process_obj._bootstrap()
74+
os.close(parent_w)
75+
code=process_obj._bootstrap(parent_sentinel=child_r)
7476
finally:
7577
os._exit(code)
7678
else:
7779
os.close(child_w)
78-
self.finalizer=util.Finalize(self,os.close, (parent_r,))
80+
os.close(child_r)
81+
self.finalizer=util.Finalize(self,util.close_fds,
82+
(parent_r,parent_w,))
7983
self.sentinel=parent_r
8084

8185
defclose(self):

‎Lib/multiprocessing/popen_forkserver.py‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,11 @@ def _launch(self, process_obj):
4949
set_spawning_popen(None)
5050

5151
self.sentinel,w=forkserver.connect_to_new_process(self._fds)
52-
self.finalizer=util.Finalize(self,os.close, (self.sentinel,))
52+
# Keep a duplicate of the data pipe's write end as a sentinel of the
53+
# parent process used by the child process.
54+
_parent_w=os.dup(w)
55+
self.finalizer=util.Finalize(self,util.close_fds,
56+
(_parent_w,self.sentinel))
5357
withopen(w,'wb',closefd=True)asf:
5458
f.write(buf.getbuffer())
5559
self.pid=forkserver.read_signed(self.sentinel)

‎Lib/multiprocessing/popen_spawn_posix.py‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,12 @@ def _launch(self, process_obj):
6161
withopen(parent_w,'wb',closefd=False)asf:
6262
f.write(fp.getbuffer())
6363
finally:
64-
ifparent_risnotNone:
65-
self.finalizer=util.Finalize(self,os.close, (parent_r,))
66-
forfdin (child_r,child_w,parent_w):
64+
fds_to_close= []
65+
forfdin (parent_r,parent_w):
66+
iffdisnotNone:
67+
fds_to_close.append(fd)
68+
self.finalizer=util.Finalize(self,util.close_fds,fds_to_close)
69+
70+
forfdin (child_r,child_w):
6771
iffdisnotNone:
6872
os.close(fd)

‎Lib/multiprocessing/process.py‎

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
# Licensed to PSF under a Contributor Agreement.
88
#
99

10-
__all__= ['BaseProcess','current_process','active_children']
10+
__all__= ['BaseProcess','current_process','active_children',
11+
'parent_process']
1112

1213
#
1314
# Imports
@@ -46,6 +47,13 @@ def active_children():
4647
_cleanup()
4748
returnlist(_children)
4849

50+
51+
defparent_process():
52+
'''
53+
Return process object representing the parent process
54+
'''
55+
return_parent_process
56+
4957
#
5058
#
5159
#
@@ -76,6 +84,7 @@ def __init__(self, group=None, target=None, name=None, args=(), kwargs={},
7684
self._identity=_current_process._identity+ (count,)
7785
self._config=_current_process._config.copy()
7886
self._parent_pid=os.getpid()
87+
self._parent_name=_current_process.name
7988
self._popen=None
8089
self._closed=False
8190
self._target=target
@@ -278,9 +287,9 @@ def __repr__(self):
278287

279288
##
280289

281-
def_bootstrap(self):
290+
def_bootstrap(self,parent_sentinel=None):
282291
from .importutil,context
283-
global_current_process,_process_counter,_children
292+
global_current_process,_parent_process,_process_counter,_children
284293

285294
try:
286295
ifself._start_methodisnotNone:
@@ -290,6 +299,8 @@ def _bootstrap(self):
290299
util._close_stdin()
291300
old_process=_current_process
292301
_current_process=self
302+
_parent_process=_ParentProcess(
303+
self._parent_name,self._parent_pid,parent_sentinel)
293304
try:
294305
util._finalizer_registry.clear()
295306
util._run_after_forkers()
@@ -337,6 +348,40 @@ def __reduce__(self):
337348
)
338349
returnAuthenticationString, (bytes(self),)
339350

351+
352+
#
353+
# Create object representing the parent process
354+
#
355+
356+
class_ParentProcess(BaseProcess):
357+
358+
def__init__(self,name,pid,sentinel):
359+
self._identity= ()
360+
self._name=name
361+
self._pid=pid
362+
self._parent_pid=None
363+
self._popen=None
364+
self._closed=False
365+
self._sentinel=sentinel
366+
self._config= {}
367+
368+
defis_alive(self):
369+
frommultiprocessing.connectionimportwait
370+
returnnotwait([self._sentinel],timeout=0)
371+
372+
@property
373+
defident(self):
374+
returnself._pid
375+
376+
defjoin(self,timeout=None):
377+
'''
378+
Wait until parent process terminates
379+
'''
380+
frommultiprocessing.connectionimportwait
381+
wait([self._sentinel],timeout=timeout)
382+
383+
pid=ident
384+
340385
#
341386
# Create object representing the main process
342387
#
@@ -365,6 +410,7 @@ def close(self):
365410
pass
366411

367412

413+
_parent_process=None
368414
_current_process=_MainProcess()
369415
_process_counter=itertools.count(1)
370416
_children=set()

‎Lib/multiprocessing/spawn.py‎

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -100,25 +100,24 @@ def spawn_main(pipe_handle, parent_pid=None, tracker_fd=None):
100100

101101
ifparent_pidisnotNone:
102102
source_process=_winapi.OpenProcess(
103-
_winapi.PROCESS_DUP_HANDLE,False,parent_pid)
103+
_winapi.SYNCHRONIZE|_winapi.PROCESS_DUP_HANDLE,
104+
False,parent_pid)
104105
else:
105106
source_process=None
106-
try:
107-
new_handle=reduction.duplicate(pipe_handle,
108-
source_process=source_process)
109-
finally:
110-
ifsource_processisnotNone:
111-
_winapi.CloseHandle(source_process)
107+
new_handle=reduction.duplicate(pipe_handle,
108+
source_process=source_process)
112109
fd=msvcrt.open_osfhandle(new_handle,os.O_RDONLY)
110+
parent_sentinel=source_process
113111
else:
114112
from .importresource_tracker
115113
resource_tracker._resource_tracker._fd=tracker_fd
116114
fd=pipe_handle
117-
exitcode=_main(fd)
115+
parent_sentinel=os.dup(pipe_handle)
116+
exitcode=_main(fd,parent_sentinel)
118117
sys.exit(exitcode)
119118

120119

121-
def_main(fd):
120+
def_main(fd,parent_sentinel):
122121
withos.fdopen(fd,'rb',closefd=True)asfrom_parent:
123122
process.current_process()._inheriting=True
124123
try:
@@ -127,7 +126,7 @@ def _main(fd):
127126
self=reduction.pickle.load(from_parent)
128127
finally:
129128
delprocess.current_process()._inheriting
130-
returnself._bootstrap()
129+
returnself._bootstrap(parent_sentinel)
131130

132131

133132
def_check_not_importing_main():

‎Lib/multiprocessing/util.py‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,3 +421,9 @@ def spawnv_passfds(path, args, passfds):
421421
finally:
422422
os.close(errpipe_read)
423423
os.close(errpipe_write)
424+
425+
426+
defclose_fds(*fds):
427+
"""Close each file descriptor given as an argument"""
428+
forfdinfds:
429+
os.close(fd)

‎Lib/test/_test_multiprocessing.py‎

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,64 @@ def _test(cls, q, *args, **kwds):
269269
q.put(bytes(current.authkey))
270270
q.put(current.pid)
271271

272+
deftest_parent_process_attributes(self):
273+
ifself.TYPE=="threads":
274+
self.skipTest('test not appropriate for {}'.format(self.TYPE))
275+
276+
self.assertIsNone(self.parent_process())
277+
278+
rconn,wconn=self.Pipe(duplex=False)
279+
p=self.Process(target=self._test_send_parent_process,args=(wconn,))
280+
p.start()
281+
p.join()
282+
parent_pid,parent_name=rconn.recv()
283+
self.assertEqual(parent_pid,self.current_process().pid)
284+
self.assertEqual(parent_pid,os.getpid())
285+
self.assertEqual(parent_name,self.current_process().name)
286+
287+
@classmethod
288+
def_test_send_parent_process(cls,wconn):
289+
frommultiprocessing.processimportparent_process
290+
wconn.send([parent_process().pid,parent_process().name])
291+
292+
deftest_parent_process(self):
293+
ifself.TYPE=="threads":
294+
self.skipTest('test not appropriate for {}'.format(self.TYPE))
295+
296+
# Launch a child process. Make it launch a grandchild process. Kill the
297+
# child process and make sure that the grandchild notices the death of
298+
# its parent (a.k.a the child process).
299+
rconn,wconn=self.Pipe(duplex=False)
300+
p=self.Process(
301+
target=self._test_create_grandchild_process,args=(wconn, ))
302+
p.start()
303+
304+
ifnotrconn.poll(timeout=5):
305+
raiseAssertionError("Could not communicate with child process")
306+
parent_process_status=rconn.recv()
307+
self.assertEqual(parent_process_status,"alive")
308+
309+
p.terminate()
310+
p.join()
311+
312+
ifnotrconn.poll(timeout=5):
313+
raiseAssertionError("Could not communicate with child process")
314+
parent_process_status=rconn.recv()
315+
self.assertEqual(parent_process_status,"not alive")
316+
317+
@classmethod
318+
def_test_create_grandchild_process(cls,wconn):
319+
p=cls.Process(target=cls._test_report_parent_status,args=(wconn, ))
320+
p.start()
321+
time.sleep(100)
322+
323+
@classmethod
324+
def_test_report_parent_status(cls,wconn):
325+
frommultiprocessing.processimportparent_process
326+
wconn.send("alive"ifparent_process().is_alive()else"not alive")
327+
parent_process().join(timeout=5)
328+
wconn.send("alive"ifparent_process().is_alive()else"not alive")
329+
272330
deftest_process(self):
273331
q=self.Queue(1)
274332
e=self.Event()
@@ -5398,6 +5456,7 @@ class ProcessesMixin(BaseMixin):
53985456
Process=multiprocessing.Process
53995457
connection=multiprocessing.connection
54005458
current_process=staticmethod(multiprocessing.current_process)
5459+
parent_process=staticmethod(multiprocessing.parent_process)
54015460
active_children=staticmethod(multiprocessing.active_children)
54025461
Pool=staticmethod(multiprocessing.Pool)
54035462
Pipe=staticmethod(multiprocessing.Pipe)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2026 Movatter.jp