Feb-17-2024, 04:10 AM
For the following code:
The following exception is issued and can not be catched:
from asyncio import create_task, run as aiorun, sleep as aiosleep from asyncio import get_event_loop, get_running_loop from os import fork, getpid, pipe, waitpid, WNOHANG from os import close as osclose, read as osread, write as oswrite from time import sleep class Worker(object): class exctype(Exception): pass sleep_seconds = 0.1 async def work(self) -> int | None: try: return await self.fork_and_wait() except Exception as exc: print('413124', exc) #task = create_task(self.fork_and_wait()) #while not task.done() and not task.cancelled(): await aiosleep(0.1) #print('4124', task.exception) #try: return task.result() #except Exception as exc: # print('95425', task, exc) async def fork_and_wait(self) -> int: print('fork_and_wait.0') pipe_fds = pipe() if (subpid := fork()) == 0: self.subprocess(pipe_fds) elif subpid < 0: raise self.exctype('fork failed') osclose(pipe_fds[1]) loop = get_event_loop() self.line_cache = b'' loop.add_reader(pipe_fds[0], self.pipe_reader, pipe_fds[0], subpid) while True: try: subpid0, status = waitpid(subpid, WNOHANG) except ChildProcessError as exc: break if subpid == subpid0: break await aiosleep(self.sleep_seconds) loop.remove_reader(pipe_fds[0]) osclose(pipe_fds[0]) return status def pipe_reader(self, rfd: int, subpid: int) -> None: if not (rbody := osread(rfd, 1024)): return print('xxxx', rbody) def subprocess(self, pipe_fds: tuple[int]) -> int: osclose(pipe_fds[0]) for idx in range(4): oswrite(pipe_fds[1], ('xzy: %u\n' % idx).encode('utf-8')) sleep(0.2) osclose(pipe_fds[1]) exit(0) async def main_worker(): task = create_task(Worker().work()) #task = create_task(Worker().fork_and_wait()) while not task.done() and not task.cancelled(): await aiosleep(0.1) print('frwrqwer', task.exception()) try: result = task.result() except SystemExit as exc: print('kkkk', task, exc) except Worker.exctype as exc: print('zzzz', task, exc) except Exception as exc: print('aaaa', task, exc) print('cccc', result) # To use the following code, works in 3.11.5, but not in 3.12.2 #loop = get_event_loop() #loop.run_until_complete(main_worker()) # To use the following code, do not work in 3.11.5 and 3.12.2 aiorun(main_worker())As the comment in the code, it works for me when run_until_complete is used in 3.11.5, but it does not works anymore in 3.12.2.
The following exception is issued and can not be catched:
Output:fork_and_wait.0
xxxx b'xzy: 0\n'
xxxx b'xzy: 1\n'
xxxx b'xzy: 2\n'
xxxx b'xzy: 3\n'
Task exception was never retrieved
Error:future: <Task finished name='Task-2' coro=<Worker.work() done, defined at /home/wangli/aifire/aifire.draft/aiopy/aioexit.py:10> exception=SystemExit(0)>
Traceback (most recent call last):
File "/home/wangli/aifire/aifire.draft/aiopy/aioexit.py", line 66, in <module>
aiorun(main_worker())
File "/opt/py3.aifire/lib/python3.12/asyncio/runners.py", line 194, in run
return runner.run(main)
^^^^^^^^^^^^^^^^
File "/opt/py3.aifire/lib/python3.12/asyncio/runners.py", line 118, in run
return self._loop.run_until_complete(task)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/py3.aifire/lib/python3.12/asyncio/base_events.py", line 672, in run_until_complete
self.run_forever()
File "/opt/py3.aifire/lib/python3.12/asyncio/base_events.py", line 639, in run_forever
self._run_once()
File "/opt/py3.aifire/lib/python3.12/asyncio/base_events.py", line 1985, in _run_once
handle._run()
File "/opt/py3.aifire/lib/python3.12/asyncio/events.py", line 88, in _run
self._context.run(self._callback, *self._args)
File "/home/wangli/aifire/aifire.draft/aiopy/aioexit.py", line 11, in work
try: return await self.fork_and_wait()
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/wangli/aifire/aifire.draft/aiopy/aioexit.py", line 23, in fork_and_wait
if (subpid := fork()) == 0: self.subprocess(pipe_fds)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/wangli/aifire/aifire.draft/aiopy/aioexit.py", line 46, in subprocess
exit(0)
File "<frozen _sitebuiltins>", line 26, in __call__
SystemExit: 0
Output:frwrqwer None
cccc 0
Yes, I known the dangerous about fork in async functions, but it should be very convenient if something can be passed through fork from parent to subprocess directly.