Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Concurrency and asyncio

Use result_async() when an asyncio application needs a Pyroxide result without blocking its event loop. Calling handle.result() on the event-loop thread blocks every other coroutine until the task finishes.

import asyncio
from pyroxide import task

@task
def calculate(value: int) -> int:
    return sum(i * i for i in range(value))

async def main() -> None:
    handle = calculate(1_000_000)
    result = await handle.result_async(timeout_sec=5)
    print(result)

asyncio.run(main())

result_async() preserves the same result and exception behavior as result(). Timeout only stops waiting; it does not cancel the task.

The same pattern works in an asynchronous web handler without coupling the task to a web framework:

async def calculate_route(value: int) -> dict[str, int]:
    result = await calculate(value).result_async(timeout_sec=5)
    return {"result": result}

Completion notification

On Unix, Rust writes to a non-blocking completion pipe. A dedicated Python reader thread scans registered futures and schedules completion on each owning event loop with call_soon_threadsafe. It does not poll task status on a timer.

On Windows, Pyroxide waits on its native condition variable through asyncio’s default executor.

Applications may await different handles from different event loops. A task may have only one active result_async() waiter; a second concurrent call raises RuntimeError. A consuming result releases the task record.

See Getting started for handle lifetime and Task cancellation for the difference between a wait timeout and stopping work.