frontends (tools/aio): add tool to run from thread, run maybe async method, or run GLib loop:
- `run_from_thread` can be used to run a method from a thread. - `maybe_async` can be used to run a method if we are not sure if it's async or not. - `install_glib_asyncio_iteration` run the GLib loop from asyncio one's. rel 426
This commit is contained in:
parent
a57624cba6
commit
1ce499a6d2
1 changed files with 93 additions and 1 deletions
|
|
@ -17,7 +17,7 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import asyncio
|
||||
from typing import Any, Coroutine
|
||||
from typing import Any, Awaitable, Callable, Coroutine
|
||||
|
||||
from libervia.backend.core import log as logging
|
||||
|
||||
|
|
@ -54,3 +54,95 @@ def run_async(async_method: Coroutine | asyncio.Future) -> None:
|
|||
|
||||
background_tasks.add(task)
|
||||
task.add_done_callback(_on_task_done)
|
||||
|
||||
|
||||
def run_with_args(
|
||||
async_method: Callable[..., Coroutine[Any, Any, Any]], *args: Any, **kwargs: Any
|
||||
) -> None:
|
||||
"""Schedules and tracks an asynchronous method with arguments.
|
||||
|
||||
This function wraps the provided asynchronous method with its arguments
|
||||
and then schedules it for execution.
|
||||
|
||||
@param async_method: The asynchronous method to be scheduled.
|
||||
@param args: Positional arguments to pass to the async_method.
|
||||
@param kwargs: Keyword arguments to pass to the async_method.
|
||||
"""
|
||||
run_async(async_method(*args, **kwargs))
|
||||
|
||||
|
||||
def run_from_thread(
|
||||
async_method: Coroutine | asyncio.Future,
|
||||
*args,
|
||||
loop: asyncio.AbstractEventLoop | None = None,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
"""Schedules an asynchronous method from another thread.
|
||||
|
||||
@param async_method: The method to be scheduled for execution.
|
||||
"""
|
||||
if loop is None:
|
||||
loop = asyncio.get_event_loop()
|
||||
assert loop is not None
|
||||
loop.call_soon_threadsafe(run_with_args, async_method, *args, **kwargs)
|
||||
|
||||
|
||||
def maybe_async(result: Any | Awaitable[Any]) -> Awaitable[Any]:
|
||||
"""
|
||||
Convert the provided result into an awaitable.
|
||||
|
||||
@param result: the result of a function or coroutine call
|
||||
@return: an awaitable object which can be awaited to get the result
|
||||
"""
|
||||
if asyncio.iscoroutine(result):
|
||||
return result
|
||||
|
||||
future = asyncio.Future()
|
||||
future.set_result(result)
|
||||
return future
|
||||
|
||||
|
||||
def install_glib_asyncio_iteration():
|
||||
"""Import and install GLib context iteration inside our asyncio event loop.
|
||||
|
||||
This is used as soon as GLib is used (like GStreamer).
|
||||
Inspired from Kivy's install_gobject_iteration (in ``kivy.support``), thanks to Kivy's
|
||||
team.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
|
||||
try:
|
||||
from gi.repository import GLib
|
||||
except ImportError:
|
||||
raise ImportError("GLib could not be imported. Ensure it's installed.")
|
||||
|
||||
if hasattr(GLib, "_glib_already_installed"):
|
||||
# already installed, don't do it twice.
|
||||
return
|
||||
|
||||
GLib._glib_already_installed = True
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
# Create a GLib MainContext and make it the default
|
||||
glib_context = GLib.MainContext.default()
|
||||
|
||||
# Function to iterate over the GLib main context
|
||||
def _glib_iteration():
|
||||
# We need to loop over the context to prevent lag
|
||||
iteration_count = 0
|
||||
while glib_context.pending() and iteration_count < 20:
|
||||
glib_context.iteration(False)
|
||||
iteration_count += 1
|
||||
|
||||
# If no work was done in the GLib loop, add a short delay before
|
||||
# scheduling the next iteration, to prevent spinning and high CPU usage.
|
||||
if iteration_count == 0:
|
||||
loop.call_later(0.01, _glib_iteration)
|
||||
else:
|
||||
# Schedule ourselves to run again on the next asyncio loop iteration
|
||||
loop.call_soon(_glib_iteration)
|
||||
|
||||
# Kick off the GLib iteration
|
||||
loop.call_soon(_glib_iteration)
|
||||
|
|
|
|||
Loading…
Reference in a new issue