mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-01-28 08:03:36 +00:00
- When many tasks are scheduled at once, they will try to write to the database at the same time. This happens prominently in develop mode when multiple notifications are attempted to be shown. - Also other resource contention may happen. - Avoid this by adding or subtracting 5% to the provided task scheduling interval time. Tests: - Print the interval times in the schedule() method and verify that the final interval values are randomized and vary by only 5% from the provided interval. Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org> Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
86 lines
2.3 KiB
Python
86 lines
2.3 KiB
Python
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
"""
|
|
Module to handle glib main loop and provide asynchronous utilities.
|
|
"""
|
|
|
|
import logging
|
|
import random
|
|
import threading
|
|
|
|
from plinth import dbus, network
|
|
from plinth.utils import import_from_gi
|
|
|
|
from . import cfg
|
|
|
|
glib = import_from_gi('GLib', '2.0')
|
|
|
|
_thread = None
|
|
_main_loop = None
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def run():
|
|
"""Run a glib main loop forever in a thread."""
|
|
global _thread
|
|
_thread = threading.Thread(target=_run)
|
|
_thread.start()
|
|
|
|
|
|
def stop():
|
|
"""Exit glib main loop and end the thread."""
|
|
if _main_loop:
|
|
logger.info('Exiting glib main loop')
|
|
_main_loop.quit()
|
|
|
|
|
|
def _run():
|
|
"""Connect to D-Bus and run main loop."""
|
|
logger.info('Started new thread for glib main loop.')
|
|
|
|
# Initialize all modules that use glib main loop
|
|
dbus.init()
|
|
network.init()
|
|
|
|
global _main_loop
|
|
_main_loop = glib.MainLoop()
|
|
_main_loop.run()
|
|
_main_loop = None
|
|
|
|
logger.info('Glib main loop thread exited.')
|
|
|
|
|
|
def schedule(interval, method, data=None, in_thread=True, repeat=True,
|
|
add_jitter=True):
|
|
"""Schedule a recurring call to a method with fixed interval."""
|
|
|
|
def _runner():
|
|
"""Run the target method and log and exceptions."""
|
|
try:
|
|
return method(data)
|
|
except Exception as exception: # pylint: disable=broad-except
|
|
logger.exception('Exception in running scheduled method - %s',
|
|
exception)
|
|
|
|
def _run_bare_or_thread(_user_data):
|
|
"""Run the target method in thread or directly (if async)."""
|
|
if not in_thread:
|
|
return _runner()
|
|
|
|
thread = threading.Thread(target=_runner)
|
|
thread.start()
|
|
return repeat
|
|
|
|
# When running in development mode, reduce the interval for tasks so that
|
|
# they are triggered quickly and frequently to facilitate debugging.
|
|
if cfg.develop and interval > 180:
|
|
interval = 180
|
|
|
|
if add_jitter:
|
|
# Add or subtract 5% random jitter to given interval to avoid many
|
|
# tasks running at exactly the same time (and competing for DB, disk,
|
|
# network, etc.).
|
|
interval *= 0.95 + (random.random() * 0.1)
|
|
|
|
glib.timeout_add(int(interval * 1000), _run_bare_or_thread, None)
|