def sync(loop, func, *args, callback_timeout=None, **kwargs): """ Run coroutine in loop running in separate thread. """ callback_timeout = _parse_timedelta(callback_timeout, "s") # Tornado's PollIOLoop doesn't raise when using closed, do it ourselves if PollIOLoop and ( (isinstance(loop, PollIOLoop) and getattr(loop, "_closing", False)) or (hasattr(loop, "asyncio_loop") and loop.asyncio_loop._closed) ): raise RuntimeError("IOLoop is closed") try: if loop.asyncio_loop.is_closed(): # tornado 6 raise RuntimeError("IOLoop is closed") except AttributeError: pass e = threading.Event() main_tid = threading.get_ident() result = [None] error = [False] @gen.coroutine def f(): # We flag the thread state asynchronous, which will make sync() call # within `func` use async semantic. In order to support concurrent # calls to sync(), `asynchronous` is used as a ref counter. thread_state.asynchronous = getattr(thread_state, "asynchronous", 0) thread_state.asynchronous += 1 try: if main_tid == threading.get_ident(): raise RuntimeError("sync() called from thread of running loop") yield gen.moment future = func(*args, **kwargs) if callback_timeout is not None: future = asyncio.wait_for(future, callback_timeout) result[0] = yield future except Exception: error[0] = sys.exc_info() finally: assert thread_state.asynchronous > 0 thread_state.asynchronous -= 1 e.set() loop.add_callback(f) if callback_timeout is not None: if not e.wait(callback_timeout): raise TimeoutError(f"timed out after {callback_timeout} s.") else: while not e.is_set(): e.wait(10) if error[0]: typ, exc, tb = error[0] raise exc.with_traceback(tb) else: return result[0]
def sync(loop, func, *args, callback_timeout=None, **kwargs): """ Run coroutine in loop running in separate thread. """ callback_timeout = _parse_timedelta(callback_timeout, "s") if loop.asyncio_loop.is_closed(): raise RuntimeError("IOLoop is closed") e = threading.Event() main_tid = threading.get_ident() result = error = future = None # set up non-locals @gen.coroutine def f(): nonlocal result, error, future try: if main_tid == threading.get_ident(): raise RuntimeError("sync() called from thread of running loop") yield gen.moment future = func(*args, **kwargs) if callback_timeout is not None: future = asyncio.wait_for(future, callback_timeout) future = asyncio.ensure_future(future) result = yield future except Exception: error = sys.exc_info() finally: e.set() def cancel(): if future is not None: future.cancel() def wait(timeout): try: return e.wait(timeout) except KeyboardInterrupt: loop.add_callback(cancel) raise loop.add_callback(f) if callback_timeout is not None: if not wait(callback_timeout): raise TimeoutError(f"timed out after {callback_timeout} s.") else: while not e.is_set(): wait(10) if error: typ, exc, tb = error raise exc.with_traceback(tb) else: return result
def sync(self, func, *args, asynchronous=None, callback_timeout=None, **kwargs): """Call `func` with `args` synchronously or asynchronously depending on the calling context""" callback_timeout = _parse_timedelta(callback_timeout) if asynchronous is None: asynchronous = self.asynchronous if asynchronous: future = func(*args, **kwargs) if callback_timeout is not None: future = asyncio.wait_for(future, callback_timeout) return future else: return sync( self.loop, func, *args, callback_timeout=callback_timeout, **kwargs )
def sync(loop, func, *args, callback_timeout=None, **kwargs): """ Run coroutine in loop running in separate thread. """ callback_timeout = _parse_timedelta(callback_timeout, "s") if loop.asyncio_loop.is_closed(): raise RuntimeError("IOLoop is closed") e = threading.Event() main_tid = threading.get_ident() result = [None] error = [False] @gen.coroutine def f(): try: if main_tid == threading.get_ident(): raise RuntimeError("sync() called from thread of running loop") yield gen.moment future = func(*args, **kwargs) if callback_timeout is not None: future = asyncio.wait_for(future, callback_timeout) result[0] = yield future except Exception: error[0] = sys.exc_info() finally: e.set() loop.add_callback(f) if callback_timeout is not None: if not e.wait(callback_timeout): raise TimeoutError(f"timed out after {callback_timeout} s.") else: while not e.is_set(): e.wait(10) if error[0]: typ, exc, tb = error[0] raise exc.with_traceback(tb) else: return result[0]
def warn_on_duration(duration, msg): start = time() yield stop = time() if stop - start > _parse_timedelta(duration): warnings.warn(msg, stacklevel=2)