-
-
Notifications
You must be signed in to change notification settings - Fork 34.7k
bpo-36888: Add multiprocessing.parent_process() #13247
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 23 commits
8839770
bcdba73
98de001
9b67034
23a2f3d
5630de5
be73255
faa3fb8
cd2f241
3d71d3f
0ceffc8
9382659
95dfc61
29898b5
566db0e
a2310a7
b3829f6
133df9c
3c5d241
4fe8467
a9c987c
e23cc08
7293d4c
80dcabf
32aa640
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -66,17 +66,22 @@ def kill(self): | |
| def _launch(self, process_obj): | ||
| code = 1 | ||
| parent_r, child_w = os.pipe() | ||
| child_r, parent_w = os.pipe() | ||
| self.pid = os.fork() | ||
| if self.pid == 0: | ||
| try: | ||
| os.close(parent_r) | ||
| code = process_obj._bootstrap() | ||
| os.close(parent_w) | ||
| code = process_obj._bootstrap(parent_sentinel=child_r) | ||
| finally: | ||
| os._exit(code) | ||
| else: | ||
| os.close(child_w) | ||
| os.close(child_r) | ||
| self.finalizer = util.Finalize(self, os.close, (parent_r,)) | ||
| self.finalizer = util.Finalize(self, os.close, (parent_w,)) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, this is clobbering the previous finalizer. You should use a single callback that closes both fds. |
||
| self.sentinel = parent_r | ||
| self._parent_w = parent_w | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not required (but it doesn't hurt either). |
||
|
|
||
| def close(self): | ||
| if self.finalizer is not None: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -49,7 +49,11 @@ def _launch(self, process_obj): | |
| set_spawning_popen(None) | ||
|
|
||
| self.sentinel, w = forkserver.connect_to_new_process(self._fds) | ||
| # Keep a duplicate of the data pipe's write end as a sentinel of the | ||
| # parent process used by the child process. | ||
| self._parent_w = os.dup(w) | ||
| self.finalizer = util.Finalize(self, os.close, (self.sentinel,)) | ||
| self.finalizer = util.Finalize(self, os.close, (self._parent_w,)) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. However, you're also clobbering the previous finalizer here. |
||
| with open(w, 'wb', closefd=True) as f: | ||
| f.write(buf.getbuffer()) | ||
| self.pid = forkserver.read_signed(self.sentinel) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -269,6 +269,67 @@ def _test(cls, q, *args, **kwds): | |
| q.put(bytes(current.authkey)) | ||
| q.put(current.pid) | ||
|
|
||
| def test_parent_process_attributes(self): | ||
| if self.TYPE == "threads": | ||
| self.skipTest('test not appropriate for {}'.format(self.TYPE)) | ||
|
|
||
| self.assertIsNone(self.parent_process()) | ||
|
|
||
| rconn, wconn = self.Pipe(duplex=False) | ||
| p = self.Process(target=self._test_send_parent_process, args=(wconn,)) | ||
| p.start() | ||
| p.join() | ||
| parent_pid, parent_name = rconn.recv() | ||
| self.assertEqual(parent_pid, self.current_process().pid) | ||
| self.assertEqual(parent_pid, os.getpid()) | ||
| self.assertEqual(parent_name, self.current_process().name) | ||
|
|
||
| @classmethod | ||
| def _test_send_parent_process(cls, wconn): | ||
| from multiprocessing.process import parent_process | ||
| wconn.send([parent_process().pid, parent_process().name]) | ||
|
|
||
| def test_parent_process(self): | ||
| if self.TYPE == "threads": | ||
| self.skipTest('test not appropriate for {}'.format(self.TYPE)) | ||
|
|
||
| # Launch a child process. Make it launch a grandchild process. Kill the | ||
| # child process and make sure that the grandchild notices the death of | ||
| # its parent (a.k.a the child process). | ||
| rconn, wconn = self.Pipe(duplex=False) | ||
| p = self.Process( | ||
| target=self._test_create_grandchild_process, args=(wconn, )) | ||
| p.start() | ||
|
|
||
| if not rconn.poll(timeout=5): | ||
| raise AssertionError("Could not communicate with child process") | ||
| parent_process_status = rconn.recv() | ||
| self.assertEqual(parent_process_status, "alive") | ||
|
|
||
| p.terminate() | ||
| p.join() | ||
|
|
||
| if not rconn.poll(timeout=5): | ||
| raise AssertionError("Could not communicate with child process") | ||
| parent_process_status = rconn.recv() | ||
| self.assertEqual(parent_process_status, "not alive") | ||
|
|
||
| @classmethod | ||
| def _test_create_grandchild_process(cls, wconn): | ||
| p = cls.Process(target=cls._test_report_parent_status, args=(wconn, )) | ||
|
tomMoral marked this conversation as resolved.
|
||
| p.start() | ||
| time.sleep(100) | ||
|
|
||
| @classmethod | ||
| def _test_report_parent_status(cls, wconn): | ||
| from multiprocessing.process import parent_process | ||
| wconn.send("alive" if parent_process().is_alive() else "not alive") | ||
|
|
||
| start_time = time.monotonic() | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't used anymore. |
||
| parent_process().join(timeout=5) | ||
|
|
||
| wconn.send("alive" if parent_process().is_alive() else "not alive") | ||
|
|
||
| def test_process(self): | ||
| q = self.Queue(1) | ||
| e = self.Event() | ||
|
|
@@ -5390,6 +5451,7 @@ class ProcessesMixin(BaseMixin): | |
| Process = multiprocessing.Process | ||
| connection = multiprocessing.connection | ||
| current_process = staticmethod(multiprocessing.current_process) | ||
| parent_process = staticmethod(multiprocessing.parent_process) | ||
| active_children = staticmethod(multiprocessing.active_children) | ||
| Pool = staticmethod(multiprocessing.Pool) | ||
| Pipe = staticmethod(multiprocessing.Pipe) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| Python child processes can now access the status of their parent process | ||
| using multiprocessing.process.parent_process |
Uh oh!
There was an error while loading. Please reload this page.