Browse Source

Daemon manager: fix signal management

Bastien Sevajol (Algoo) 8 years ago
parent
commit
0735440805
2 changed files with 55 additions and 15 deletions
  1. 32 15
      tracim/tracim/lib/daemons.py
  2. 23 0
      tracim/tracim/lib/utils.py

+ 32 - 15
tracim/tracim/lib/daemons.py View File

@@ -10,15 +10,17 @@ from radicale import RequestHandler as RadicaleRequestHandler
10 10
 from radicale import config as radicale_config
11 11
 from tg import TGApp
12 12
 
13
+from tracim.lib.base import logger
13 14
 from tracim.lib.exceptions import AlreadyRunningDaemon
15
+from tracim.lib.utils import add_signal_handler
14 16
 
15 17
 
16 18
 class DaemonsManager(object):
17 19
     def __init__(self, app: TGApp):
18 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 25
     def run(self, name: str, daemon_class: object, **kwargs) -> None:
24 26
         """
@@ -30,33 +32,47 @@ class DaemonsManager(object):
30 32
         :param kwargs: Other kwargs will be given to daemon class
31 33
         instantiation.
32 34
         """
33
-        if name in self._daemons:
35
+        if name in self._running_daemons:
34 36
             raise AlreadyRunningDaemon(
35 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 42
         daemon = daemon_class(name=name, kwargs=kwargs, daemon=True)
39 43
         daemon.start()
40
-        self._daemons[name] = daemon
44
+        self._running_daemons[name] = daemon
41 45
 
42 46
     def stop(self, name: str) -> None:
43 47
         """
44 48
         Stop daemon with his name and wait for him.
45 49
         Where name is given name when daemon started
46
-        with run method. If daemon name unknow, raise IndexError.
50
+        with run method.
47 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 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 78
 class Daemon(threading.Thread):
@@ -75,13 +91,14 @@ class RadicaleDaemon(Daemon):
75 91
     def __init__(self, *args, **kwargs):
76 92
         super().__init__(*args, **kwargs)
77 93
         self._prepare_config()
78
-        self._server = self._get_server()
94
+        self._server = None
79 95
 
80 96
     def run(self):
81 97
         """
82 98
         To see origin radical server start method, refer to
83 99
         radicale.__main__.run
84 100
         """
101
+        self._server = self._get_server()
85 102
         self._server.serve_forever()
86 103
 
87 104
     def stop(self):

+ 23 - 0
tracim/tracim/lib/utils.py View File

@@ -1,6 +1,7 @@
1 1
 # -*- coding: utf-8 -*-
2 2
 
3 3
 import time
4
+import signal
4 5
 
5 6
 from tracim.lib.base import logger
6 7
 
@@ -46,3 +47,25 @@ def replace_reset_password_templates(engines):
46 47
 @property
47 48
 def NotImplemented():
48 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)