Add private _asyncioEventLoop module (#19816)
N/A. This is a follow-up from #19122 (DotPad BLE support), splitting out the asyncio event loop module as requested during review.
Summary of the issue:
NVDA currently has no built-in way to run asyncio coroutines. Components that need async functionality (e.g. BLE communication) require a managed asyncio event loop running on a background thread.
Description of user facing changes:
N/A. This is a developer-facing module only.
Description of developer facing changes:
Added a private _asyncioEventLoop module that provides:
initialize() / terminate() — lifecycle management, wired into NVDA core startup/shutdown
runCoroutine(coro) — schedule a coroutine on the event loop (fire-and-forget)
runCoroutineSync(coro, timeout) — schedule a coroutine and block until completion
The module is prefixed with an underscore to keep it internal to NVDA core and not part of the public add-on API.
Description of development approach:
The asyncio event loop runs on a daemon thread, started during NVDA initialization (after appModuleHandler) and terminated during shutdown (after hwIo). The terminate() function cancels all pending tasks before stopping the loop.
runCoroutineSync wraps asyncio.run_coroutine_threadsafe with optional timeout support and converts asyncio.TimeoutError to the built-in TimeoutError for a cleaner API.