# Does PythonCall block all \`@async\` routines?

**URL:** <https://discourse.julialang.org/t/does-pythoncall-block-all-async-routines/114722>\
**Category:** General Usage\
**Tags:** question, pythoncall, async\
**Created:** [May 25, 2024, 3:03pm UTC](https://discourse.julialang.org/t/does-pythoncall-block-all-async-routines/114722 "2024-05-25T15:03:59Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![aryavorskiy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aryavorskiy/32/43466_2.png) [@aryavorskiy](https://discourse.julialang.org/u/aryavorskiy)\
**Post date:** [May 25, 2024, 3:03pm UTC](https://discourse.julialang.org/t/does-pythoncall-block-all-async-routines/114722/1 "2024-05-25T15:03:59Z")

</div>

I am trying to write a simple timing function. To avoid waiting for too long, I made it stop waiting and return `Inf` at some point. I achieved it this way:

```julia
function time_function(f, input...; maxtime=1)
    c = Channel{Float64}(2) do ch # Watchdog
        sleep(maxtime)
        put!(ch, Inf)
    end
    @async begin # Worker
        dt = @elapsed f(input...)
        isopen(c) && put!(c, dt)
    end
    return take!(c)
end

```

The point being, it doesn’t work with Python functions for some reason:

```julia
using PythonCall
t = pyimport("time")
time_function(sleep, 0.1) # 0.1
time_function(sleep, 10) # Inf
time_function(t.sleep, 0.1) # 0.1
time_function(t.sleep, 10) # 10 - why?

```

Seems that the Python functions are tunning in the main thread and are `await`-ed regardless of `@async`.

How can I avoid this? I’m aware of [this problem](https://discourse.julialang.org/t/non-blocking-asyncio-calls-with-pycall/36685), but in my case I do not use Python’s asyncio at all.

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [May 25, 2024, 4:38pm UTC](https://discourse.julialang.org/t/does-pythoncall-block-all-async-routines/114722/2 "2024-05-25T16:38:31Z")

</div>

With `@async` you are tryjng to share a thread with Python, but Python does not know that. Consider using `@spawn` instead while making sure `Threads.nthreads()` is greater than 1.

---

<div class="post-metadata">

**Author:** ![aryavorskiy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aryavorskiy/32/43466_2.png) [@aryavorskiy](https://discourse.julialang.org/u/aryavorskiy)\
**Post date:** [May 25, 2024, 5:05pm UTC](https://discourse.julialang.org/t/does-pythoncall-block-all-async-routines/114722/3 "2024-05-25T17:05:14Z")

</div>

Unfortunately, this didn’t help. However, I eventually created a separate implementation for Python functions:

````julia
pyexec("""
import threading
import time
import ctypes

def _async_raise(tid, exctype):
    '''Abort the thread'''
    if not isinstance(tid, int):
        tid = tid.ident
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid), ctypes.py_object(exctype))
    if res == 0:
        raise ValueError("Invalid thread id")
    elif res > 1:
        # If it returns a number greater than one
        # We're in trouble, and we try to revert the effect
        ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid), None)
        raise SystemError("PyThreadState_SetAsyncExc failed")""", Main)

@pyexec ```
def py_measure_time(func, maxtime, *args):
    def wrapper():
        func(*args)

    thread = threading.Thread(target=wrapper)
    thread.start()
    start_time = time.time()
    thread.join(timeout=maxtime)
    if thread.is_alive():
        _async_raise(thread, SystemExit)
        return float("Inf")
    else:
        return time.time() - start_time``` => py_measure_time
time_function(f::Py, args...; maxtime=1) = 
    pyconvert(Number, py_measure_time(f, maxtime, args...))

````
