4
4
(c) 2009, holger krekel
5
5
"""
6
6
import threading
7
+ try :
8
+ import thread
9
+ except ImportError :
10
+ import _thread as thread
11
+
7
12
import time
8
13
import sys
9
14
22
27
class Reply (object ):
23
28
""" reply instances provide access to the result
24
29
of a function execution that got dispatched
25
- through WorkerPool.dispatch ()
30
+ through WorkerPool.spawn ()
26
31
"""
27
32
_excinfo = None
28
33
def __init__ (self , task ):
@@ -54,12 +59,11 @@ def get(self, timeout=None):
54
59
reraise (excinfo [0 ], excinfo [1 ], excinfo [2 ]) # noqa
55
60
return result
56
61
57
- class WorkerThread ( threading . Thread ) :
62
+ class WorkerThread :
58
63
def __init__ (self , pool ):
59
- threading .Thread .__init__ (self )
60
64
self ._queue = queue .Queue ()
61
65
self ._pool = pool
62
- self .setDaemon ( 1 )
66
+ self ._finishevent = threading . Event ( )
63
67
64
68
def _run_once (self ):
65
69
reply = self ._queue .get ()
@@ -79,16 +83,23 @@ def _run_once(self):
79
83
# at this point, reply, task and all other local variables go away
80
84
return True
81
85
86
+ def start (self ):
87
+ self .id = thread .start_new_thread (self .run , ())
88
+
89
+ @property
90
+ def dead (self ):
91
+ return self ._finishevent .isSet ()
92
+
82
93
def run (self ):
83
94
try :
84
95
while self ._run_once ():
85
- self ._pool ._ready [ self ] = True
96
+ self ._pool ._ready . add ( self )
86
97
finally :
87
- del self ._pool ._alive [self ]
88
98
try :
89
- del self ._pool ._ready [ self ]
99
+ self ._pool ._ready . remove ( self )
90
100
except KeyError :
91
101
pass
102
+ self ._finishevent .set ()
92
103
93
104
def send (self , task ):
94
105
reply = Reply (task )
@@ -98,8 +109,11 @@ def send(self, task):
98
109
def stop (self ):
99
110
self ._queue .put (SystemExit )
100
111
112
+ def join (self , timeout = None ):
113
+ self ._finishevent .wait (timeout )
114
+
101
115
class WorkerPool (object ):
102
- """ A WorkerPool allows to dispatch function executions
116
+ """ A WorkerPool allows to spawn function executions
103
117
to threads. Each Worker Thread is reused for multiple
104
118
function executions. The dispatching operation
105
119
takes care to create and dispatch to existing
@@ -116,29 +130,30 @@ def __init__(self, maxthreads=None):
116
130
create up to `maxthreads` worker threads.
117
131
"""
118
132
self .maxthreads = maxthreads
119
- self ._ready = {}
120
- self ._alive = {}
133
+ self ._running = set ()
134
+ self ._ready = set ()
121
135
122
- def dispatch (self , func , * args , ** kwargs ):
136
+ def spawn (self , func , * args , ** kwargs ):
123
137
""" return Reply object for the asynchronous dispatch
124
138
of the given func(*args, **kwargs) in a
125
139
separate worker thread.
126
140
"""
127
141
if self ._shuttingdown :
128
142
raise IOError ("WorkerPool is already shutting down" )
129
143
try :
130
- thread , _ = self ._ready .popitem ()
144
+ thread = self ._ready .pop ()
131
145
except KeyError : # pop from empty list
132
- if self .maxthreads and len (self ._alive ) >= self .maxthreads :
133
- raise IOError ("can't create more than %d threads." %
146
+ if self .maxthreads and len (self ._running ) >= self .maxthreads :
147
+ raise IOError ("maximum of %d threads are busy, "
148
+ "can't create more." %
134
149
(self .maxthreads ,))
135
150
thread = self ._newthread ()
136
151
return thread .send ((func , args , kwargs ))
137
152
138
153
def _newthread (self ):
139
154
thread = WorkerThread (self )
140
- self ._alive [thread ] = True
141
155
thread .start ()
156
+ self ._running .add (thread )
142
157
return thread
143
158
144
159
def shutdown (self ):
@@ -147,22 +162,23 @@ def shutdown(self):
147
162
"""
148
163
if not self ._shuttingdown :
149
164
self ._shuttingdown = True
150
- for t in list ( self ._alive ) :
165
+ for t in self ._running :
151
166
t .stop ()
152
167
153
- def join (self , timeout = None ):
168
+ def waitall (self , timeout = None ):
154
169
""" wait until all worker threads have terminated. """
155
170
deadline = delta = None
156
171
if timeout is not None :
157
172
deadline = time .time () + timeout
158
- for thread in list (self ._alive ):
173
+ while self ._running :
174
+ thread = self ._running .pop ()
159
175
if deadline :
160
176
delta = deadline - time .time ()
161
177
if delta <= 0 :
162
178
raise IOError ("timeout while joining threads" )
163
179
thread .join (timeout = delta )
164
- if thread .isAlive () :
165
- raise IOError ("timeout while joining threads" )
180
+ if not thread .dead :
181
+ raise IOError ("timeout while joining thread %s" % thread . id )
166
182
167
183
if __name__ == '__channelexec__' :
168
184
maxthreads = channel .receive () # noqa
@@ -176,7 +192,7 @@ def join(self, timeout=None):
176
192
if task is None :
177
193
gw ._trace ("thread-dispatcher got None, exiting" )
178
194
execpool .shutdown ()
179
- execpool .join ()
195
+ execpool .waitall ()
180
196
raise gw ._StopExecLoop
181
197
gw ._trace ("dispatching exec task to thread pool" )
182
- execpool .dispatch (gw .executetask , task )
198
+ execpool .spawn (gw .executetask , task )
0 commit comments