-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathservices.py
More file actions
101 lines (82 loc) · 2.83 KB
/
services.py
File metadata and controls
101 lines (82 loc) · 2.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
from threading import Event, Thread
class StopService(Exception):
pass
class Service:
running_services = []
@classmethod
def register(cls, service):
cls.running_services.append(service)
@classmethod
def deregister(cls, service):
cls.running_services.remove(service)
@classmethod
def stop_all(cls):
for service in cls.running_services:
service.stop()
@classmethod
def wait_all(cls):
for service in cls.running_services:
service.wait()
def __init__(self):
self.stopped = Event()
self.init_done = Event()
def interrupt(self):
pass # Sublasses override to set events
def stop(self):
self.stopped.set()
self.interrupt()
def await_init_done(self):
self.init_done.wait()
def wait(self, *args):
self.svc_thread.join(*args)
def sleep(self, time):
self.stopped.wait(time)
if self.stopped.isSet():
raise StopService('Service stopped')
def init(self):
# Some services might not need to do setup, so it's OK not to override
pass
def run(self):
raise Exception("Service.run(): Implement this in subclass")
def cleanup(self):
# Some services might not need to clean up, so it's OK not to override
pass
def service_thread(self):
Service.register(self)
self.init()
self.init_done.set()
try:
self.run()
except StopService:
# Graceful stop, don't re-raise
pass
except:
# Other error, re-raise
raise
finally:
# Always cleanup
self.cleanup()
Service.deregister(self)
def start(self, synchronous_init=False):
self.svc_thread = Thread(target=self.service_thread)
self.svc_thread.start()
if synchronous_init:
self.await_init_done()
def start_subservice(self, s):
""" A service that starts other services should do so by calling
this function, to avoid funky race conditions when stopping services
that are about to launch other services """
# If we've been stopped since we last woke up, don't actually start
# this service.
if self.stopped.isSet():
raise StopService('Service Stopped')
# Start the service knowing we aren't stopped.
s.start()
# Now that the service is registered to be stopped if stop_all is called
# we check again, to see if we have been stopped since starting this
# subservice, we need to do this because we might have been stopped since
# we last checked, but before the sub service was registered.
if self.stopped.isSet():
s.stop()
s.wait()
raise StopService('Service Stopped')