/
pthreading.py
246 lines (191 loc) · 7.88 KB
/
pthreading.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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
#
# Copyright 2011-2012 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Refer to the README and COPYING files for full details of the license
#
"""Reimplement threading.Lock, RLock and Condition with libpthread
The pthreading module provides Lock and Condition synchronization
objects compatible with Python native threading module.
The implementation, however, is based on POSIX thread library as delivered
by the libpthread. Lock and Condition are designed to be a drop-in
replacement for their respective threading counterpart.
Take a look at threading.py of Python 2. Notice that Event.wait() wakes 20
times a second and checks if the event has been set. This CPU hogging has been
fixed in Python 3, but is not expected to change during Python 2 lifetime.
To avoid this waste of resources, put in your main module::
import pthreading
pthreading monkey_patch()
This would hack the Linux-native threading module, and make it use Linux-native
POSIX synchronization objects.
Note: you must invoke pthreading.monkey_patch before importing the thread and
threading modules. If these modules are already imported, monkey_patch will
raise a RuntimeError.
The pthreading code was originally written as part of
`Vdsm <http://wiki.ovirt.org/wiki/Vdsm>`_ by Cyril Plisko, Saggi Mizrahi and
others. For questions, comments and patches please contact `vdsm-devel
<mailto:vdsm-devel@lists.fedorahosted.org>`_.
"""
import time
import errno
import os
import sys
import pthread
class _Lock(pthread.Mutex):
"""
_Lock class mimics Python native threading.Lock() API on top of
the POSIX thread mutex synchronization primitive.
"""
def __enter__(self):
self.acquire()
return self
def __exit__(self, exc_type, exc_value, traceback):
self.release()
def acquire(self, blocking=True):
rc = self.lock() if blocking else self.trylock()
if rc == 0:
return True
elif rc == errno.EBUSY:
return False
else:
raise OSError(rc, os.strerror(rc))
def release(self):
self.unlock()
class Lock(_Lock):
def locked(self):
# Yes, this is horrible hack, and the same one used by Python
# threadmodule.c. But this is part of Python lock interface.
if self.acquire(blocking=False):
self.release()
return False
return True
class RLock(Lock):
def __init__(self):
_Lock.__init__(self, recursive=True)
class Condition(object):
"""
Condition class mimics Python native threading.Condition() API
on top of the POSIX thread conditional variable synchronization
primitive.
"""
def __init__(self, lock=None):
self.__lock = lock if lock else Lock()
self.__cond = pthread.Cond(mutex=self.__lock)
# Export the lock's acquire() and release() methods
self.acquire = self.__lock.acquire
self.release = self.__lock.release
def __enter__(self):
return self.__lock.__enter__()
def __exit__(self, *args):
return self.__lock.__exit__(*args)
def wait(self, timeout=None, balancing=True):
# Balancing is an undocumented argument for Condition.wait()
# It is relevent in python because it busy loops. Since the whole
# purpose of this package is to not busy loop we just silently ignore
# this argument.
if timeout is not None:
bailout = time.time() + timeout
self.wait_until(bailout)
else:
self.__cond.wait()
def wait_until(self, bailout):
"""
Waits until bailout time has passed or the condtion is notified.
This may return before bailout time if notify() or notify_all() were
invoked, or because of spurious wakeup of the underlying
implementation. You must check the return value to detect if bailout
time has passed.
Return True if bailout timeout has passed, False otherwise.
"""
abstime = pthread.timespec()
abstime.tv_sec = int(bailout)
abstime.tv_nsec = int((bailout - int(bailout)) * (10 ** 9))
return self.__cond.timedwait(abstime) == errno.ETIMEDOUT
def notify(self):
return self.__cond.signal()
def notifyAll(self):
return self.__cond.broadcast()
notify_all = notifyAll
class Event(object):
# After Tim Peters' event class (without is_posted())
def __init__(self, verbose=None):
self.__cond = Condition(Lock())
self.__flag = False
def _reset_internal_locks(self):
# private! called by Thread._reset_internal_locks by _after_fork()
self.__cond.__init__()
def isSet(self):
'Return true if and only if the internal flag is true.'
return self.__flag
is_set = isSet
def set(self):
"""Set the internal flag to true.
All threads waiting for the flag to become true are awakened. Threads
that call wait() once the flag is true will not block at all.
"""
with self.__cond:
self.__flag = True
self.__cond.notify_all()
def clear(self):
"""Reset the internal flag to false.
Subsequently, threads calling wait() will block until set() is called to
set the internal flag to true again.
"""
with self.__cond:
self.__flag = False
def wait(self, timeout=None, balancing=True):
"""Block until the internal flag is true.
If the internal flag is true on entry, return immediately. Otherwise,
block until another thread calls set() to set the flag to true, or until
the optional timeout occurs.
When the timeout argument is present and not None, it should be a
floating point number specifying a timeout for the operation in seconds
(or fractions thereof).
This method returns the internal flag on exit, so it will always return
True except if a timeout is given and the operation times out.
"""
# Compute the bailout before taking the lock, since taking the lock may
# block, enlarging the requested timeout.
if timeout is not None:
bailout = time.time() + timeout
with self.__cond:
if timeout is not None:
while not self.__flag:
if self.__cond.wait_until(bailout):
break # Timeout expired
else:
while not self.__flag:
self.__cond.wait()
return self.__flag
def monkey_patch():
"""
Hack threading and thread modules to use our classes
Thus, Queue and SocketServer can easily enjoy them.
"""
if 'thread' in sys.modules or 'threading' in sys.modules:
# If thread was imported, some module may be using now the original
# thread.allocate_lock. If threading was imported, it is already using
# thread.allocate_lock for internal locks. Mixing different lock types
# is a bad idea.
raise RuntimeError("You must monkey_patch before importing thread or "
"threading modules")
import thread
thread.allocate_lock = Lock
import threading
threading.Condition = Condition
threading.Lock = Lock
threading.RLock = RLock
threading.Event = Event