Hide keyboard shortcuts

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. 

5 

6from queue import Queue 

7from threading import Timer, RLock 

8 

9from xpra.util import AtomicInteger 

10from xpra.log import Logger 

11 

12log = Logger("util") 

13 

14 

15#emulate the glib main loop using a single thread + queue: 

16 

17class QueueScheduler: 

18 

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() 

25 

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() 

32 

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 

40 

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) 

45 

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 

50 

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() 

57 

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, {})) 

64 

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 

80 

81 

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 

101 

102 def stop(self): 

103 self.exit = True 

104 self.stop_main_queue() 

105 

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