forked from oVirt/pthreading
/
pthreading.py
201 lines (157 loc) · 5.97 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
#
# Copyright 2011-2017 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 Condition.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 `devel@ovirt
<mailto:devel@ovirt.org>`_.
"""
import time
import errno
import os
import sys
import pthread
__version__ = "0.1.6"
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)
self.__signals = 0
self.__waiters = 0
# 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.
self.__waiters += 1
try:
if timeout is not None:
self._wait_timeout(timeout)
else:
self._wait()
finally:
self.__waiters -= 1
def notify(self):
if self.__signals < self.__waiters:
self.__signals += 1
return self.__cond.signal()
def notifyAll(self):
self.__signals = self.__waiters
return self.__cond.broadcast()
notify_all = notifyAll
def _wait(self):
while True:
self.__cond.wait()
if self.__signals > 0:
self.__signals -= 1
break
def _wait_timeout(self, timeout):
bailout = time.time() + timeout
abstime = pthread.timespec()
abstime.tv_sec = int(bailout)
abstime.tv_nsec = int((bailout - int(bailout)) * (10 ** 9))
while True:
if self.__cond.timedwait(abstime) == errno.ETIMEDOUT:
break
if self.__signals > 0:
self.__signals -= 1
break
_is_monkey_patched = False
def monkey_patch():
"""
Hack threading and thread modules to use our classes
Thus, Queue and SocketServer can easily enjoy them.
"""
global _is_monkey_patched
if _is_monkey_patched:
return
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
_is_monkey_patched = True