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 retrievedError: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: 0Output:frwrqwer None
cccc 0Yes, 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.
