blob: 1779534379b06470d535a29b01383d5cc724c18e [file] [log] [blame]
# Copyright 2018 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
import multiprocessing
import os
from portage.tests import TestCase
from portage.util._eventloop.global_event_loop import global_event_loop
from portage.util.futures import asyncio
from portage.util.futures.unix_events import DefaultEventLoopPolicy
def fork_main(parent_conn, child_conn):
parent_conn.close()
loop = asyncio._wrap_loop()
# This fails with python's default event loop policy,
# see https://bugs.python.org/issue22087.
loop.run_until_complete(asyncio.sleep(0.1, loop=loop))
loop.close()
def async_main(fork_exitcode, loop=None):
loop = asyncio._wrap_loop(loop)
# Since python2.7 does not support Process.sentinel, use Pipe to
# monitor for process exit.
parent_conn, child_conn = multiprocessing.Pipe()
def eof_callback(proc):
loop.remove_reader(parent_conn.fileno())
parent_conn.close()
proc.join()
fork_exitcode.set_result(proc.exitcode)
proc = multiprocessing.Process(target=fork_main, args=(parent_conn, child_conn))
loop.add_reader(parent_conn.fileno(), eof_callback, proc)
proc.start()
child_conn.close()
class EventLoopInForkTestCase(TestCase):
"""
The default asyncio event loop policy does not support loops
running in forks, see https://bugs.python.org/issue22087.
Portage's DefaultEventLoopPolicy supports forks.
"""
def testEventLoopInForkTestCase(self):
initial_policy = asyncio.get_event_loop_policy()
if not isinstance(initial_policy, DefaultEventLoopPolicy):
asyncio.set_event_loop_policy(DefaultEventLoopPolicy())
loop = None
try:
loop = asyncio._wrap_loop()
fork_exitcode = loop.create_future()
# Make async_main fork while the loop is running, which would
# trigger https://bugs.python.org/issue22087 with asyncio's
# default event loop policy.
loop.call_soon(async_main, fork_exitcode)
assert loop.run_until_complete(fork_exitcode) == os.EX_OK
finally:
asyncio.set_event_loop_policy(initial_policy)
if loop not in (None, global_event_loop()):
loop.close()
self.assertFalse(global_event_loop().is_closed())