浏览代码

Daemon manager: fix signal management

Bastien Sevajol (Algoo) 9 年前
父节点
当前提交
0735440805
共有 2 个文件被更改,包括 55 次插入15 次删除
  1. 32 15
      tracim/tracim/lib/daemons.py
  2. 23 0
      tracim/tracim/lib/utils.py

+ 32 - 15
tracim/tracim/lib/daemons.py 查看文件

10
 from radicale import config as radicale_config
10
 from radicale import config as radicale_config
11
 from tg import TGApp
11
 from tg import TGApp
12
 
12
 
13
+from tracim.lib.base import logger
13
 from tracim.lib.exceptions import AlreadyRunningDaemon
14
 from tracim.lib.exceptions import AlreadyRunningDaemon
15
+from tracim.lib.utils import add_signal_handler
14
 
16
 
15
 
17
 
16
 class DaemonsManager(object):
18
 class DaemonsManager(object):
17
     def __init__(self, app: TGApp):
19
     def __init__(self, app: TGApp):
18
         self._app = app
20
         self._app = app
19
-        self._daemons = {}
20
-        signal.signal(signal.SIGTERM, lambda *_: self.stop_all())
21
-        signal.signal(signal.SIGINT, lambda *_: self.stop_all())
21
+        self._running_daemons = {}
22
+        add_signal_handler(signal.SIGTERM, self.stop_all)
23
+        add_signal_handler(signal.SIGINT, self.stop_all)
22
 
24
 
23
     def run(self, name: str, daemon_class: object, **kwargs) -> None:
25
     def run(self, name: str, daemon_class: object, **kwargs) -> None:
24
         """
26
         """
30
         :param kwargs: Other kwargs will be given to daemon class
32
         :param kwargs: Other kwargs will be given to daemon class
31
         instantiation.
33
         instantiation.
32
         """
34
         """
33
-        if name in self._daemons:
35
+        if name in self._running_daemons:
34
             raise AlreadyRunningDaemon(
36
             raise AlreadyRunningDaemon(
35
                 'Daemon with name "{0}" already running'.format(name)
37
                 'Daemon with name "{0}" already running'.format(name)
36
             )
38
             )
37
 
39
 
40
+        logger.info(self, 'Starting daemon with name "{0}" and class "{1}" ...'
41
+                          .format(name, daemon_class))
38
         daemon = daemon_class(name=name, kwargs=kwargs, daemon=True)
42
         daemon = daemon_class(name=name, kwargs=kwargs, daemon=True)
39
         daemon.start()
43
         daemon.start()
40
-        self._daemons[name] = daemon
44
+        self._running_daemons[name] = daemon
41
 
45
 
42
     def stop(self, name: str) -> None:
46
     def stop(self, name: str) -> None:
43
         """
47
         """
44
         Stop daemon with his name and wait for him.
48
         Stop daemon with his name and wait for him.
45
         Where name is given name when daemon started
49
         Where name is given name when daemon started
46
-        with run method. If daemon name unknow, raise IndexError.
50
+        with run method.
47
         :param name:
51
         :param name:
48
         """
52
         """
49
-        self._daemons[name].stop()
50
-        self._daemons[name].join()
51
-
52
-    def stop_all(self) -> None:
53
+        if name in self._running_daemons:
54
+            logger.info(self, 'Stopping daemon with name "{0}" ...'
55
+                              .format(name))
56
+            self._running_daemons[name].stop()
57
+            self._running_daemons[name].join()
58
+            del self._running_daemons[name]
59
+            logger.info(self, 'Stopping daemon with name "{0}": OK'
60
+                              .format(name))
61
+
62
+    def stop_all(self, *args, **kwargs) -> None:
53
         """
63
         """
54
         Stop all started daemons and w<ait for them.
64
         Stop all started daemons and w<ait for them.
55
         """
65
         """
56
-        for daemon_name in self._daemons:
57
-            self._daemons[daemon_name].stop()
58
-        for daemon_name in self._daemons:
59
-            self._daemons[daemon_name].join()
66
+        logger.info(self, 'Stopping all daemons')
67
+        for name, daemon in self._running_daemons.items():
68
+            logger.info(self, 'Stopping daemon "{0}" ...'.format(name))
69
+            daemon.stop()
70
+
71
+        for name, daemon in self._running_daemons.items():
72
+            daemon.join()
73
+            logger.info(self, 'Stopping daemon "{0}" OK'.format(name))
74
+
75
+        self._running_daemons = {}
60
 
76
 
61
 
77
 
62
 class Daemon(threading.Thread):
78
 class Daemon(threading.Thread):
75
     def __init__(self, *args, **kwargs):
91
     def __init__(self, *args, **kwargs):
76
         super().__init__(*args, **kwargs)
92
         super().__init__(*args, **kwargs)
77
         self._prepare_config()
93
         self._prepare_config()
78
-        self._server = self._get_server()
94
+        self._server = None
79
 
95
 
80
     def run(self):
96
     def run(self):
81
         """
97
         """
82
         To see origin radical server start method, refer to
98
         To see origin radical server start method, refer to
83
         radicale.__main__.run
99
         radicale.__main__.run
84
         """
100
         """
101
+        self._server = self._get_server()
85
         self._server.serve_forever()
102
         self._server.serve_forever()
86
 
103
 
87
     def stop(self):
104
     def stop(self):

+ 23 - 0
tracim/tracim/lib/utils.py 查看文件

1
 # -*- coding: utf-8 -*-
1
 # -*- coding: utf-8 -*-
2
 
2
 
3
 import time
3
 import time
4
+import signal
4
 
5
 
5
 from tracim.lib.base import logger
6
 from tracim.lib.base import logger
6
 
7
 
46
 @property
47
 @property
47
 def NotImplemented():
48
 def NotImplemented():
48
     raise NotImplementedError()
49
     raise NotImplementedError()
50
+
51
+
52
+def add_signal_handler(signal_id, handler, execute_before=True) -> None:
53
+    """
54
+    Add a callback attached to python signal.
55
+    :param signal_id: signal identifier (eg. signal.SIGTERM)
56
+    :param handler: callback to execute when signal trig
57
+    :param execute_before: If True, callback is executed before eventual
58
+    existing callback on given dignal id.
59
+    """
60
+    previous_handler = signal.getsignal(signal_id)
61
+
62
+    def call_callback(*args, **kwargs):
63
+        if not execute_before and callable(previous_handler):
64
+            previous_handler(*args, **kwargs)
65
+
66
+        handler(*args, **kwargs)
67
+
68
+        if execute_before and callable(previous_handler):
69
+            previous_handler(*args, **kwargs)
70
+
71
+    signal.signal(signal_id, call_callback)