Coverage for /home/antoine/projects/xpra-git/dist/python3/lib64/python/xpra/queue_scheduler.py : 100%
Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is part of Xpra.
2# Copyright (C) 2013-2020 Antoine Martin <antoine@xpra.org>
3# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
4# later version. See the file COPYING for details.
6from queue import Queue
7from threading import Timer, RLock
9from xpra.util import AtomicInteger
10from xpra.log import Logger
12log = Logger("util")
15#emulate the glib main loop using a single thread + queue:
17class QueueScheduler:
19 def __init__(self):
20 self.main_queue = Queue()
21 self.exit = False
22 self.timer_id = AtomicInteger()
23 self.timers = {}
24 self.timer_lock = RLock()
26 def source_remove(self, tid : int):
27 log("source_remove(%i)", tid)
28 with self.timer_lock:
29 timer = self.timers.pop(tid, None)
30 if timer:
31 timer.cancel()
33 def idle_add(self, fn : callable, *args, **kwargs) -> int:
34 tid = self.timer_id.increase()
35 self.main_queue.put((self.idle_repeat_call, (tid, fn, args, kwargs), {}))
36 #add an entry,
37 #but use the value False to stop us from trying to call cancel()
38 self.timers[tid] = False
39 return tid
41 def idle_repeat_call(self, tid : int, fn : callable, args, kwargs):
42 if tid not in self.timers:
43 return False #cancelled
44 return fn(*args, **kwargs)
46 def timeout_add(self, timeout : int, fn : callable, *args, **kwargs):
47 tid = self.timer_id.increase()
48 self.do_timeout_add(tid, timeout, fn, *args, **kwargs)
49 return tid
51 def do_timeout_add(self, tid : int, timeout : int, fn : callable, *args, **kwargs):
52 #emulate glib's timeout_add using Timers
53 args = (tid, timeout, fn, args, kwargs)
54 t = Timer(timeout/1000.0, self.queue_timeout_function, args)
55 self.timers[tid] = t
56 t.start()
58 def queue_timeout_function(self, tid : int, timeout : int, fn : callable, fn_args, fn_kwargs):
59 if tid not in self.timers: # pragma: no cover
60 return #cancelled
61 #add to run queue:
62 mqargs = [tid, timeout, fn, fn_args, fn_kwargs]
63 self.main_queue.put((self.timeout_repeat_call, mqargs, {}))
65 def timeout_repeat_call(self, tid : int, timeout : int, fn : callable, fn_args, fn_kwargs):
66 #executes the function then re-schedules it (if it returns True)
67 if tid not in self.timers: # pragma: no cover
68 return False #cancelled
69 v = fn(*fn_args, **fn_kwargs)
70 if bool(v):
71 #create a new timer with the same tid:
72 with self.timer_lock:
73 if tid in self.timers:
74 self.do_timeout_add(tid, timeout, fn, *fn_args, **fn_kwargs)
75 else:
76 self.timers.pop(tid, None)
77 #we do the scheduling via timers, so always return False here
78 #so that the main queue won't re-schedule this function call itself:
79 return False
82 def run(self):
83 log("run() queue has %s items already in it", self.main_queue.qsize())
84 #process "idle_add"/"timeout_add" events in the main loop:
85 while not self.exit:
86 log("run() size=%s", self.main_queue.qsize())
87 v = self.main_queue.get()
88 if v is None:
89 log("run() None exit marker")
90 break
91 fn, args, kwargs = v
92 log("run() %s%s%s", fn, args, kwargs)
93 try:
94 r = fn(*args, **kwargs)
95 if bool(r):
96 #re-run it
97 self.main_queue.put(v)
98 except Exception:
99 log.error("error during main loop callback %s", fn, exc_info=True)
100 self.exit = True
102 def stop(self):
103 self.exit = True
104 self.stop_main_queue()
106 def stop_main_queue(self):
107 self.main_queue.put(None)
108 #empty the main queue:
109 q = Queue()
110 q.put(None)
111 self.main_queue = q