def run_sync_function_on_asyncio_thread(func: Callable, *, block: bool) -> None:
    """Run a non-async fn on the asyncio thread. Can be called from any thread.

    If the current thread is already the asyncio thread, func is guaranteed
    to have been completed when this method returns.

    For any other thread, we only wait for completion if `block` is True.
    """
    assert not asyncio.iscoroutinefunction(func), "func must be a non-async function"
    asyncio_loop = get_asyncio_loop()
    if get_running_loop() == asyncio_loop:  # we are running on the asyncio thread
        func()
    else:  # non-asyncio thread
        async def wrapper():
            return func()
        fut = asyncio.run_coroutine_threadsafe(wrapper(), loop=asyncio_loop)
        if block:
            fut.result()
        else:
            # add explicit logging of exceptions, otherwise they might get lost
            tb1 = traceback.format_stack()[:-1]
            tb1_str = "".join(tb1)
            def on_done(fut_: concurrent.futures.Future):
                assert fut_.done()
                if fut_.cancelled():
                    _logger.debug(f"func cancelled. {func=}.")
                elif exc := fut_.exception():
                    # note: We explicitly log the first part of the traceback, tb1_str.
                    #       The second part gets logged by setting "exc_info".
                    _logger.error(
                        f"func errored. {func=}. {exc=}"
                        f"\n{tb1_str}", exc_info=exc)
            fut.add_done_callback(on_done)
Run a non-async fn on the asyncio thread. Can be called from any thread.

    If the current thread is already the asyncio thread, func is guaranteed
    to have been completed when this method returns.

    For any other thread, we only wait for completion if `block` is True.
    
electrum/util.py