Revision a95fd5d7 lib/locking.py
b/lib/locking.py | ||
---|---|---|
23 | 23 |
# pylint: disable-msg=W0613,W0201 |
24 | 24 |
|
25 | 25 |
import threading |
26 |
# Wouldn't it be better to define LockingError in the locking module? |
|
27 |
# Well, for now that's how the rest of the code does it... |
|
28 |
from ganeti import errors |
|
26 | 29 |
|
27 | 30 |
|
28 | 31 |
class SharedLock: |
... | ... | |
52 | 55 |
self.__nwait_exc = 0 |
53 | 56 |
self.__nwait_shr = 0 |
54 | 57 |
|
58 |
# is this lock in the deleted state? |
|
59 |
self.__deleted = False |
|
60 |
|
|
55 | 61 |
def __is_sharer(self): |
56 | 62 |
"""Is the current thread sharing the lock at this time?""" |
57 | 63 |
return threading.currentThread() in self.__shr |
... | ... | |
92 | 98 |
|
93 | 99 |
return result |
94 | 100 |
|
101 |
def __wait(self,c): |
|
102 |
"""Wait on the given condition, and raise an exception if the current lock |
|
103 |
is declared deleted in the meantime. |
|
104 |
|
|
105 |
Args: |
|
106 |
c: condition to wait on |
|
107 |
|
|
108 |
""" |
|
109 |
c.wait() |
|
110 |
if self.__deleted: |
|
111 |
raise errors.LockError('deleted lock') |
|
112 |
|
|
113 |
def __exclusive_acquire(self): |
|
114 |
"""Acquire the lock exclusively. |
|
115 |
|
|
116 |
This is a private function that presumes you are already holding the |
|
117 |
internal lock. It's defined separately to avoid code duplication between |
|
118 |
acquire() and delete() |
|
119 |
|
|
120 |
""" |
|
121 |
self.__nwait_exc += 1 |
|
122 |
try: |
|
123 |
# This is to save ourselves from a nasty race condition that could |
|
124 |
# theoretically make the sharers starve. |
|
125 |
if self.__nwait_shr > 0 or self.__nwait_exc > 1: |
|
126 |
self.__wait(self.__turn_exc) |
|
127 |
|
|
128 |
while len(self.__shr) > 0 or self.__exc is not None: |
|
129 |
self.__wait(self.__turn_exc) |
|
130 |
|
|
131 |
self.__exc = threading.currentThread() |
|
132 |
finally: |
|
133 |
self.__nwait_exc -= 1 |
|
134 |
|
|
135 |
|
|
95 | 136 |
def acquire(self, blocking=1, shared=0): |
96 | 137 |
"""Acquire a shared lock. |
97 | 138 |
|
... | ... | |
108 | 149 |
|
109 | 150 |
self.__lock.acquire() |
110 | 151 |
try: |
152 |
if self.__deleted: |
|
153 |
raise errors.LockError('deleted lock') |
|
154 |
|
|
111 | 155 |
# We cannot acquire the lock if we already have it |
112 | 156 |
assert not self.__is_owned(), "double acquire() on a non-recursive lock" |
113 | 157 |
|
... | ... | |
119 | 163 |
# we'll just wait while there are no exclusive holders. |
120 | 164 |
if self.__nwait_exc > 0: |
121 | 165 |
# TODO: if !blocking... |
122 |
self.__turn_shr.wait()
|
|
166 |
self.__wait(self.__turn_shr)
|
|
123 | 167 |
|
124 | 168 |
while self.__exc is not None: |
125 | 169 |
# TODO: if !blocking... |
126 |
self.__turn_shr.wait()
|
|
170 |
self.__wait(self.__turn_shr)
|
|
127 | 171 |
|
128 | 172 |
self.__shr.add(threading.currentThread()) |
129 | 173 |
finally: |
130 | 174 |
self.__nwait_shr -= 1 |
131 | 175 |
|
132 | 176 |
else: |
133 |
self.__nwait_exc += 1 |
|
134 |
try: |
|
135 |
# This is to save ourselves from a nasty race condition that could |
|
136 |
# theoretically make the sharers starve. |
|
137 |
if self.__nwait_shr > 0 or self.__nwait_exc > 1: |
|
138 |
# TODO: if !blocking... |
|
139 |
self.__turn_exc.wait() |
|
140 |
|
|
141 |
while len(self.__shr) > 0 or self.__exc is not None: |
|
142 |
# TODO: if !blocking... |
|
143 |
self.__turn_exc.wait() |
|
144 |
|
|
145 |
self.__exc = threading.currentThread() |
|
146 |
finally: |
|
147 |
self.__nwait_exc -= 1 |
|
177 |
# TODO: if !blocking... |
|
178 |
# (or modify __exclusive_acquire for non-blocking mode) |
|
179 |
self.__exclusive_acquire() |
|
148 | 180 |
|
149 | 181 |
finally: |
150 | 182 |
self.__lock.release() |
... | ... | |
191 | 223 |
finally: |
192 | 224 |
self.__lock.release() |
193 | 225 |
|
226 |
def delete(self, blocking=1): |
|
227 |
"""Delete a Shared Lock. |
|
228 |
|
|
229 |
This operation will declare the lock for removal. First the lock will be |
|
230 |
acquired in exclusive mode if you don't already own it, then the lock |
|
231 |
will be put in a state where any future and pending acquire() fail. |
|
232 |
|
|
233 |
Args: |
|
234 |
blocking: whether to block while trying to acquire or to operate in |
|
235 |
try-lock mode. this locking mode is not supported yet unless |
|
236 |
you are already holding exclusively the lock. |
|
237 |
|
|
238 |
""" |
|
239 |
self.__lock.acquire() |
|
240 |
try: |
|
241 |
assert not self.__is_sharer(), "cannot delete() a lock while sharing it" |
|
242 |
|
|
243 |
if self.__deleted: |
|
244 |
raise errors.LockError('deleted lock') |
|
245 |
|
|
246 |
if not self.__is_exclusive(): |
|
247 |
if not blocking: |
|
248 |
# We don't have non-blocking mode for now |
|
249 |
raise NotImplementedError |
|
250 |
self.__exclusive_acquire() |
|
251 |
|
|
252 |
self.__deleted = True |
|
253 |
self.__exc = None |
|
254 |
# Wake up everybody, they will fail acquiring the lock and |
|
255 |
# raise an exception instead. |
|
256 |
self.__turn_exc.notifyAll() |
|
257 |
self.__turn_shr.notifyAll() |
|
258 |
|
|
259 |
finally: |
|
260 |
self.__lock.release() |
|
261 |
|
Also available in: Unified diff