pyobs / pyobs-core

Core packages for pyobs
Other
10 stars 3 forks source link

Background Tasks Start & Stop #352

Closed GermanHydrogen closed 2 weeks ago

GermanHydrogen commented 5 months ago

I am proposing a extension of the background_thread functionality in pyobs.Object. In many IStartStop modules, the background thread is running regardless of the is_running state. The main loop only is only terminated early:

while True:
    # not running?
    if self._running is False:
        await asyncio.sleep(1)
        continue

This is bad design, as module can consist of many objects which all use this "workaround", leading to many of this loops constantly running. It is also a source of code duplication throughout the code base.

This could be improved by changing the add_background_task method from:

def add_background_task(self, func: Callable[..., Coroutine[Any, Any, None]], restart: bool = True) -> None:

to

def add_background_task(self, 
    func: Callable[..., Coroutine[Any, Any, None]], 
    restart: bool = True, autostart: bool = True) -> BackgroundTask:

with the BackgroundTask implementing a start() and stop() method. If the background task has to be start/stoppable, it can be stored in a module attribute and used in the modules start and stop methods.

GermanHydrogen commented 5 months ago

The introduction of a BackgroundTask class would also simplify the restart functionality:

class BackgroundTask:
    def __init__(self, func, restart):
        self._func = func
        self._restart = restart
        self._task = None

    def start(self):
        self._callback_function()

    def _callback_function(self, corr=None):
        if self._task is None:
            self._start_task()
            return

        try:
            self._task.exception()
        except asyncio.CancelledError:
            return

        if self._restart:
            self._start_task()

    def _start_task(self):
        self._task = asyncio.create_task(self._func())
        self._task.add_done_callback(self._callback_function)

    def stop(self):
        self._task.cancel()

by "recursively" starting the background function in the "done callback".