-
Notifications
You must be signed in to change notification settings - Fork 3
/
threadedmanager.py
169 lines (130 loc) · 4.99 KB
/
threadedmanager.py
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
import time
import multiprocessing
import threading
from module.core.output import out
class ThreadModule(multiprocessing.Process):
'''
Processes are used so that the modules can be terminated,
the problem with threads are that you cannot force termination
of a module using threading. You can however terminate a
process, as described in the method stop(self).
'''
def stop(self):
'''
stop()
Killing a process. This is done to avoid modules to be able
to run forever.
docs.python.com (multiprocessing.terminate)
Terminate the process. On Unix this is done using the SIGTERM
signal; on Windows TerminateProcess() is used.
Warning (docs.python.com multiprocessing.terminate):
If this method is used when the associated process is
using a pipe or queue then the pipe or queue is liable to become
corrupted and may become unusable by other process...
'''
self.terminate()
def setModule(self, module, data):
'''
setModule(object, dictionary)
Preparing a process with the necessary data object and module,
as these have to be defined before the process is started and
cannot be defined as parameters in the run method.
Keyword arguments:
module -- brunobot extra module with a module.main(data) method
data -- brunobot data dictionary object
'''
self.module = module
self.data = data
self.time = time.time()
def duration(self):
'''
duration() -> integer
Return the time in seconds since the running of the module started.
'''
return int(round((time.time() - self.time),0))
def run(self):
'''
run()
Method representing the process's activity.
'''
self.time = time.time()
self.module.main(self.data)
class ThreadedManager(threading.Thread):
'''
Responsible for running and terminating brunobot extra modules.
Threading is preferred over mutliprocessing because threading
allow usage of share data after the thread is initialized, and
the ThreadedManager needs to get information about new modules
that should be run.
'''
proccesses = []
running = True
def stop(self):
'''
stop()
Stop running the ThreadedManager, used for stopping the
thread when shutting down the bot.
'''
self.running = False
def setConfig(self, config):
'''
setConfig()
Prepare the ThreadedManager with the configuration
file needed to determine how long each of the extra
modules are allowed to run.
Keyword arguments:
config -- brunobot configuration manager
'''
self.cfg = config
def setCommunication(self, communication):
'''
setCommuncation()
Prepare the ThreadedManager with the communication
object used to relay messages to the IRC network socket.
Keyword arguments:
communication -- brunobot connection object
'''
self.communication = communication
def runModule(self, module, data):
'''
runModule()
Creating a new process representation of a brunobot extra module.
Passing the information needed for the process, then running the
process.
Keyword arguments:
module -- brunobot extra module with a module.main(data) method
data -- brunobot data dictionary object
'''
try:
t = ThreadModule()
t.setModule(module, data)
self.proccesses.append(t)
t.start()
except:
out.error('Running of extra module "%s" failed.' % module.name)
def run(self):
'''
run()
Starting the process manager, invoked by self.start().
'''
while self.running:
for proc in self.proccesses:
duration = proc.duration()
maxDuration = self.cfg.get('max_run_time',proc.module.name)
try:
maxDuration = int(maxDuration)
except:
try:
maxDuration = int(self.cfg.get('module','max_run_time'))
except:
maxDuration = 5
if not proc.is_alive():
self.proccesses.remove(proc)
elif duration >= maxDuration:
self.communication.say(proc.data['channel'],
'Running of %s took too long (limit=%ds).' % (proc.module.name, maxDuration))
out.warn('Running of %s took too long (limit=%ds)' % (proc.module.name, maxDuration))
proc.stop()
self.proccesses.remove(proc)
time.sleep(0.3)
out.info('ThreadedManager threadmanager.ThreadedManager().run(self) terminated.')