DataService: removing threading, using asyncio loop

Start tasks through asyncio loop instead of a separate thread.
Autmatic tasks are not started in the init function. They can be started elsewhere.
This commit is contained in:
Mose Müller 2023-08-02 12:06:19 +02:00
parent ebeb4c1520
commit 0bfb3a44ae

View File

@ -1,8 +1,6 @@
import asyncio
import inspect
import threading
from collections.abc import Callable
from concurrent.futures import Future
from itertools import chain
from typing import Any
@ -31,9 +29,10 @@ class DataService(rpyc.Service):
# Keep track of the root object. This helps to filter the emission of
# notifications
self.__root__: "DataService" = self
self.__loop = asyncio.get_event_loop()
# dictionary to keep track of running tasks
self.__tasks: dict[str, Future[None]] = {}
self.__tasks: dict[str, asyncio.Task[None]] = {}
self._autostart_tasks: dict[str, tuple[Any]]
if "_autostart_tasks" not in self.__dict__:
self._autostart_tasks = {}
@ -41,9 +40,6 @@ class DataService(rpyc.Service):
self._callbacks: set[Callable[[str, Any], None]] = set()
self._set_start_and_stop_for_async_methods()
self._start_async_loop_in_thread()
self._start_autostart_tasks()
self._register_list_change_callbacks(self, f"{self.__class__.__name__}")
self._register_DataService_instance_callbacks(
self, f"{self.__class__.__name__}"
@ -95,24 +91,7 @@ class DataService(rpyc.Service):
f"No start method found for service '{service_name}'"
)
def _start_loop(self) -> None:
asyncio.set_event_loop(self.__loop)
try:
self.__loop.run_forever()
finally:
# cancel all running tasks
for task in self.__tasks.values():
self.__loop.call_soon_threadsafe(task.cancel)
self.__loop.call_soon_threadsafe(self.__loop.stop)
self.__thread.join()
def _start_async_loop_in_thread(self) -> None:
# create a new event loop and run it in a separate thread
self.__loop = asyncio.new_event_loop()
self.__thread = threading.Thread(target=self._start_loop)
self.__thread.start()
def _set_start_and_stop_for_async_methods(self) -> None:
def _set_start_and_stop_for_async_methods(self) -> None: # noqa: C901
# inspect the methods of the class
for name, method in inspect.getmembers(
self, predicate=inspect.iscoroutinefunction
@ -121,17 +100,18 @@ class DataService(rpyc.Service):
def start_task(*args: Any, **kwargs: Any) -> None:
async def task(*args: Any, **kwargs: Any) -> None:
try:
await getattr(self, name)(*args, **kwargs)
await method(*args, **kwargs)
except asyncio.CancelledError:
print(f"Task {name} was cancelled")
self.__tasks[name] = asyncio.run_coroutine_threadsafe(
task(*args, **kwargs), self.__loop
)
if not self.__tasks.get(name):
self.__tasks[name] = self.__loop.create_task(task(*args, **kwargs))
else:
logger.error(f"Task `{name}` is already running!")
def stop_task() -> None:
# cancel the task
task = self.__tasks.get(name)
task = self.__tasks.pop(name)
if task is not None:
self.__loop.call_soon_threadsafe(task.cancel)