diff --git a/gajim/application.py b/gajim/application.py index f7ed67c73ef1d2b9fd66d3b4926d1b4a05399a46..4c0f516ce0c28c6cf367c4e06eb275c69bbd06b7 100644 --- a/gajim/application.py +++ b/gajim/application.py @@ -55,6 +55,7 @@ from gajim.common import logger from gajim.common.i18n import _ from gajim.common.contacts import LegacyContactsAPI +from gajim.common.task_manager import TaskManager class GajimApplication(Gtk.Application): @@ -218,6 +219,7 @@ def _startup(self, _application): from gajim.common.cert_store import CertificateStore app.cert_store = CertificateStore() + app.task_manager = TaskManager() # Set Application Menu app.app = self diff --git a/gajim/common/app.py b/gajim/common/app.py index 9175467eaf8769ff6786c9a3ff3eff72780a6557..62cc57d1c68440e0f70779ad9a60913ac56d6a1d 100644 --- a/gajim/common/app.py +++ b/gajim/common/app.py @@ -131,6 +131,8 @@ cert_store = None +task_manager = None + # zeroconf account name ZEROCONF_ACC_NAME = 'Local' diff --git a/gajim/common/task_manager.py b/gajim/common/task_manager.py new file mode 100644 index 0000000000000000000000000000000000000000..11d2dec0baeace3a3199cdd986af0d3f8ac9cb90 --- /dev/null +++ b/gajim/common/task_manager.py @@ -0,0 +1,104 @@ +# This file is part of Gajim. +# +# Gajim is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published +# by the Free Software Foundation; version 3 only. +# +# Gajim is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Gajim. If not, see <http://www.gnu.org/licenses/>. + +from __future__ import annotations + +from typing import List + +import functools +import queue +import logging + +from gi.repository import GLib + +log = logging.getLogger('gajim.c.m.task_manager') + + +class TaskManager: + def __init__(self): + self._timeout = None + self._queue = queue.PriorityQueue() + + def _start_worker(self): + self._timeout = GLib.timeout_add_seconds(2, self._process_queue) + + def _process_queue(self): + log.info('%s tasks queued', self._queue.qsize()) + requeue = [] + while not self._queue.empty(): + task = self._queue.get_nowait() + if task.is_obsolete(): + log.info('Task obsolete: %r', task) + continue + + if not task.preconditions_met(): + if task.is_obsolete(): + log.info('Task obsolete: %r', task) + else: + requeue.append(task) + continue + + log.info('Execute task %r', task) + task.execute() + self._requeue_tasks(requeue) + return True + + if self._requeue_tasks(requeue): + # Queue is empty, but there are tasks to requeue + # don't stop worker + return True + + # Queue is empty, stop worker + self._timeout = None + return False + + def _requeue_tasks(self, tasks: List[Task]): + if not tasks: + return False + + for task in tasks: + log.info('Requeue task (preconditions not met): %r', task) + self._queue.put_nowait(task) + return True + + def add_task(self, task: Task): + log.info('Adding task: %r', task) + self._queue.put_nowait(task) + if self._timeout is None: + self._start_worker() + + +@functools.total_ordering +class Task: + def __init__(self, priority: int = 0): + self.priority = priority + self._obsolete = False + + def is_obsolete(self): + return self._obsolete + + def set_obsolete(self): + self._obsolete = True + + def __lt__(self, task: Task): + return self.priority < task.priority + + def __eq__(self, task: Task): + return task.priority == self.priority + + def execute(self): + raise NotImplementedError + + def preconditions_met(self): + raise NotImplementedError