async def test_threaded_async_run(): """Test threaded async runner.""" runner = ThreadedAsyncRunner() runner.start() async def fn(): return "ok" assert runner.call(fn()).result() == "ok" runner.stop()
async def test_threaded_async_run_cancel_task(): """Test threaded async runner tasks cancelled.""" runner = ThreadedAsyncRunner() runner.start() async def fn(): await asyncio.sleep(1) task = runner.call(fn()) await asyncio.sleep(0.1) task.cancel() await asyncio.sleep(0.1) with pytest.raises(CancelledError): task.result() assert task.done() # cancel before start task = runner.call(fn()) task.cancel() with pytest.raises(CancelledError): task.result() assert task.done() runner.stop()
class Multiplexer(AsyncMultiplexer): """Transit sync multiplexer for compatibility.""" def __init__(self, *args, **kwargs): """ Initialize the connection multiplexer. :param connections: a sequence of connections. :param default_connection_index: the index of the connection to use as default. | this information is used for envelopes which | don't specify any routing context. :param loop: the event loop to run the multiplexer. If None, a new event loop is created. """ super().__init__(*args, **kwargs) self._sync_lock = threading.Lock() self._thread_was_started = False self._is_connected = False def set_loop(self, loop: AbstractEventLoop) -> None: """ Set event loop and all event loopp related objects. :param loop: asyncio event loop. :return: None """ super().set_loop(loop) self._thread_runner = ThreadedAsyncRunner(self._loop) def connect(self) -> None: # type: ignore # cause overrides coroutine # pylint: disable=invalid-overridden-method """ Connect the multiplexer. Synchronously in thread spawned if new loop created. """ with self._sync_lock: if not self._loop.is_running(): self._thread_runner.start() self._thread_was_started = True self._thread_runner.call(super().connect()).result(240) self._is_connected = True def disconnect(self) -> None: # type: ignore # cause overrides coroutine # pylint: disable=invalid-overridden-method """ Disconnect the multiplexer. Also stops a dedicated thread for event loop if spawned on connect. """ self.logger.debug("Disconnect called") with self._sync_lock: if not self._loop.is_running(): return if self._is_connected: self._thread_runner.call(super().disconnect()).result(240) self._is_connected = False self.logger.debug("Disconnect async method executed") if self._thread_runner.is_alive() and self._thread_was_started: self._thread_runner.stop() self.logger.debug("Thread stopped") self.logger.debug("Disconnected") def put( self, envelope: Envelope ) -> None: # type: ignore # cause overrides coroutine """ Schedule an envelope for sending it. Notice that the output queue is an asyncio.Queue which uses an event loop running on a different thread than the one used in this function. :param envelope: the envelope to be sent. :return: None """ self._thread_runner.call(super()._put(envelope)) # .result(240)