Statistics
| Branch: | Tag: | Revision:

root / lib / locking.py @ 84e344d4

History | View | Annotate | Download (30.1 kB)

1 162c1c1f Guido Trotter
#
2 162c1c1f Guido Trotter
#
3 162c1c1f Guido Trotter
4 162c1c1f Guido Trotter
# Copyright (C) 2006, 2007 Google Inc.
5 162c1c1f Guido Trotter
#
6 162c1c1f Guido Trotter
# This program is free software; you can redistribute it and/or modify
7 162c1c1f Guido Trotter
# it under the terms of the GNU General Public License as published by
8 162c1c1f Guido Trotter
# the Free Software Foundation; either version 2 of the License, or
9 162c1c1f Guido Trotter
# (at your option) any later version.
10 162c1c1f Guido Trotter
#
11 162c1c1f Guido Trotter
# This program is distributed in the hope that it will be useful, but
12 162c1c1f Guido Trotter
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 162c1c1f Guido Trotter
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 162c1c1f Guido Trotter
# General Public License for more details.
15 162c1c1f Guido Trotter
#
16 162c1c1f Guido Trotter
# You should have received a copy of the GNU General Public License
17 162c1c1f Guido Trotter
# along with this program; if not, write to the Free Software
18 162c1c1f Guido Trotter
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 162c1c1f Guido Trotter
# 02110-1301, USA.
20 162c1c1f Guido Trotter
21 162c1c1f Guido Trotter
"""Module implementing the Ganeti locking code."""
22 162c1c1f Guido Trotter
23 162c1c1f Guido Trotter
import threading
24 84e344d4 Michael Hanselmann
25 a95fd5d7 Guido Trotter
from ganeti import errors
26 7ee7c0c7 Guido Trotter
from ganeti import utils
27 162c1c1f Guido Trotter
28 162c1c1f Guido Trotter
29 42a999d1 Guido Trotter
def ssynchronized(lock, shared=0):
30 42a999d1 Guido Trotter
  """Shared Synchronization decorator.
31 42a999d1 Guido Trotter

32 42a999d1 Guido Trotter
  Calls the function holding the given lock, either in exclusive or shared
33 42a999d1 Guido Trotter
  mode. It requires the passed lock to be a SharedLock (or support its
34 42a999d1 Guido Trotter
  semantics).
35 42a999d1 Guido Trotter

36 42a999d1 Guido Trotter
  """
37 42a999d1 Guido Trotter
  def wrap(fn):
38 42a999d1 Guido Trotter
    def sync_function(*args, **kwargs):
39 42a999d1 Guido Trotter
      lock.acquire(shared=shared)
40 42a999d1 Guido Trotter
      try:
41 42a999d1 Guido Trotter
        return fn(*args, **kwargs)
42 42a999d1 Guido Trotter
      finally:
43 42a999d1 Guido Trotter
        lock.release()
44 42a999d1 Guido Trotter
    return sync_function
45 42a999d1 Guido Trotter
  return wrap
46 42a999d1 Guido Trotter
47 42a999d1 Guido Trotter
48 84e344d4 Michael Hanselmann
class _CountingCondition(object):
49 84e344d4 Michael Hanselmann
  """Wrapper for Python's built-in threading.Condition class.
50 84e344d4 Michael Hanselmann

51 84e344d4 Michael Hanselmann
  This wrapper keeps a count of active waiters. We can't access the internal
52 84e344d4 Michael Hanselmann
  "__waiters" attribute of threading.Condition because it's not thread-safe.
53 84e344d4 Michael Hanselmann

54 84e344d4 Michael Hanselmann
  """
55 84e344d4 Michael Hanselmann
  __slots__ = [
56 84e344d4 Michael Hanselmann
    "_cond",
57 84e344d4 Michael Hanselmann
    "_nwaiters",
58 84e344d4 Michael Hanselmann
    ]
59 84e344d4 Michael Hanselmann
60 84e344d4 Michael Hanselmann
  def __init__(self, lock):
61 84e344d4 Michael Hanselmann
    """Initializes this class.
62 84e344d4 Michael Hanselmann

63 84e344d4 Michael Hanselmann
    """
64 84e344d4 Michael Hanselmann
    object.__init__(self)
65 84e344d4 Michael Hanselmann
    self._cond = threading.Condition(lock=lock)
66 84e344d4 Michael Hanselmann
    self._nwaiters = 0
67 84e344d4 Michael Hanselmann
68 84e344d4 Michael Hanselmann
  def notifyAll(self):
69 84e344d4 Michael Hanselmann
    """Notifies the condition.
70 84e344d4 Michael Hanselmann

71 84e344d4 Michael Hanselmann
    """
72 84e344d4 Michael Hanselmann
    return self._cond.notifyAll()
73 84e344d4 Michael Hanselmann
74 84e344d4 Michael Hanselmann
  def wait(self, timeout=None):
75 84e344d4 Michael Hanselmann
    """Waits for the condition to be notified.
76 84e344d4 Michael Hanselmann

77 84e344d4 Michael Hanselmann
    @type timeout: float or None
78 84e344d4 Michael Hanselmann
    @param timeout: Timeout in seconds
79 84e344d4 Michael Hanselmann

80 84e344d4 Michael Hanselmann
    """
81 84e344d4 Michael Hanselmann
    assert self._nwaiters >= 0
82 84e344d4 Michael Hanselmann
83 84e344d4 Michael Hanselmann
    self._nwaiters += 1
84 84e344d4 Michael Hanselmann
    try:
85 84e344d4 Michael Hanselmann
      return self._cond.wait(timeout=timeout)
86 84e344d4 Michael Hanselmann
    finally:
87 84e344d4 Michael Hanselmann
      self._nwaiters -= 1
88 84e344d4 Michael Hanselmann
89 84e344d4 Michael Hanselmann
  def has_waiting(self):
90 84e344d4 Michael Hanselmann
    """Returns whether there are active waiters.
91 84e344d4 Michael Hanselmann

92 84e344d4 Michael Hanselmann
    """
93 84e344d4 Michael Hanselmann
    return bool(self._nwaiters)
94 84e344d4 Michael Hanselmann
95 84e344d4 Michael Hanselmann
96 84e344d4 Michael Hanselmann
class SharedLock(object):
97 162c1c1f Guido Trotter
  """Implements a shared lock.
98 162c1c1f Guido Trotter

99 162c1c1f Guido Trotter
  Multiple threads can acquire the lock in a shared way, calling
100 162c1c1f Guido Trotter
  acquire_shared().  In order to acquire the lock in an exclusive way threads
101 162c1c1f Guido Trotter
  can call acquire_exclusive().
102 162c1c1f Guido Trotter

103 162c1c1f Guido Trotter
  The lock prevents starvation but does not guarantee that threads will acquire
104 162c1c1f Guido Trotter
  the shared lock in the order they queued for it, just that they will
105 162c1c1f Guido Trotter
  eventually do so.
106 162c1c1f Guido Trotter

107 162c1c1f Guido Trotter
  """
108 84e344d4 Michael Hanselmann
  __slots__ = [
109 84e344d4 Michael Hanselmann
    "__active_shr_c",
110 84e344d4 Michael Hanselmann
    "__inactive_shr_c",
111 84e344d4 Michael Hanselmann
    "__deleted",
112 84e344d4 Michael Hanselmann
    "__exc",
113 84e344d4 Michael Hanselmann
    "__lock",
114 84e344d4 Michael Hanselmann
    "__pending",
115 84e344d4 Michael Hanselmann
    "__shr",
116 84e344d4 Michael Hanselmann
    ]
117 84e344d4 Michael Hanselmann
118 84e344d4 Michael Hanselmann
  __condition_class = _CountingCondition
119 84e344d4 Michael Hanselmann
120 162c1c1f Guido Trotter
  def __init__(self):
121 84e344d4 Michael Hanselmann
    """Construct a new SharedLock.
122 84e344d4 Michael Hanselmann

123 84e344d4 Michael Hanselmann
    """
124 84e344d4 Michael Hanselmann
    object.__init__(self)
125 84e344d4 Michael Hanselmann
126 84e344d4 Michael Hanselmann
    # Internal lock
127 162c1c1f Guido Trotter
    self.__lock = threading.Lock()
128 162c1c1f Guido Trotter
129 84e344d4 Michael Hanselmann
    # Queue containing waiting acquires
130 84e344d4 Michael Hanselmann
    self.__pending = []
131 84e344d4 Michael Hanselmann
132 84e344d4 Michael Hanselmann
    # Active and inactive conditions for shared locks
133 84e344d4 Michael Hanselmann
    self.__active_shr_c = self.__condition_class(self.__lock)
134 84e344d4 Michael Hanselmann
    self.__inactive_shr_c = self.__condition_class(self.__lock)
135 84e344d4 Michael Hanselmann
136 84e344d4 Michael Hanselmann
    # Current lock holders
137 162c1c1f Guido Trotter
    self.__shr = set()
138 162c1c1f Guido Trotter
    self.__exc = None
139 162c1c1f Guido Trotter
140 a95fd5d7 Guido Trotter
    # is this lock in the deleted state?
141 a95fd5d7 Guido Trotter
    self.__deleted = False
142 a95fd5d7 Guido Trotter
143 84e344d4 Michael Hanselmann
  def __check_deleted(self):
144 84e344d4 Michael Hanselmann
    """Raises an exception if the lock has been deleted.
145 84e344d4 Michael Hanselmann

146 84e344d4 Michael Hanselmann
    """
147 84e344d4 Michael Hanselmann
    if self.__deleted:
148 84e344d4 Michael Hanselmann
      raise errors.LockError("Deleted lock")
149 84e344d4 Michael Hanselmann
150 162c1c1f Guido Trotter
  def __is_sharer(self):
151 84e344d4 Michael Hanselmann
    """Is the current thread sharing the lock at this time?
152 84e344d4 Michael Hanselmann

153 84e344d4 Michael Hanselmann
    """
154 162c1c1f Guido Trotter
    return threading.currentThread() in self.__shr
155 162c1c1f Guido Trotter
156 162c1c1f Guido Trotter
  def __is_exclusive(self):
157 84e344d4 Michael Hanselmann
    """Is the current thread holding the lock exclusively at this time?
158 84e344d4 Michael Hanselmann

159 84e344d4 Michael Hanselmann
    """
160 162c1c1f Guido Trotter
    return threading.currentThread() == self.__exc
161 162c1c1f Guido Trotter
162 162c1c1f Guido Trotter
  def __is_owned(self, shared=-1):
163 162c1c1f Guido Trotter
    """Is the current thread somehow owning the lock at this time?
164 162c1c1f Guido Trotter

165 162c1c1f Guido Trotter
    This is a private version of the function, which presumes you're holding
166 162c1c1f Guido Trotter
    the internal lock.
167 162c1c1f Guido Trotter

168 162c1c1f Guido Trotter
    """
169 162c1c1f Guido Trotter
    if shared < 0:
170 162c1c1f Guido Trotter
      return self.__is_sharer() or self.__is_exclusive()
171 162c1c1f Guido Trotter
    elif shared:
172 162c1c1f Guido Trotter
      return self.__is_sharer()
173 162c1c1f Guido Trotter
    else:
174 162c1c1f Guido Trotter
      return self.__is_exclusive()
175 162c1c1f Guido Trotter
176 162c1c1f Guido Trotter
  def _is_owned(self, shared=-1):
177 162c1c1f Guido Trotter
    """Is the current thread somehow owning the lock at this time?
178 162c1c1f Guido Trotter

179 c41eea6e Iustin Pop
    @param shared:
180 c41eea6e Iustin Pop
        - < 0: check for any type of ownership (default)
181 c41eea6e Iustin Pop
        - 0: check for exclusive ownership
182 c41eea6e Iustin Pop
        - > 0: check for shared ownership
183 162c1c1f Guido Trotter

184 162c1c1f Guido Trotter
    """
185 162c1c1f Guido Trotter
    self.__lock.acquire()
186 162c1c1f Guido Trotter
    try:
187 84e344d4 Michael Hanselmann
      return self.__is_owned(shared=shared)
188 162c1c1f Guido Trotter
    finally:
189 162c1c1f Guido Trotter
      self.__lock.release()
190 162c1c1f Guido Trotter
191 84e344d4 Michael Hanselmann
  def _count_pending(self):
192 84e344d4 Michael Hanselmann
    """Returns the number of pending acquires.
193 a95fd5d7 Guido Trotter

194 84e344d4 Michael Hanselmann
    @rtype: int
195 a95fd5d7 Guido Trotter

196 a95fd5d7 Guido Trotter
    """
197 84e344d4 Michael Hanselmann
    self.__lock.acquire()
198 84e344d4 Michael Hanselmann
    try:
199 84e344d4 Michael Hanselmann
      return len(self.__pending)
200 84e344d4 Michael Hanselmann
    finally:
201 84e344d4 Michael Hanselmann
      self.__lock.release()
202 a95fd5d7 Guido Trotter
203 84e344d4 Michael Hanselmann
  def __do_acquire(self, shared):
204 84e344d4 Michael Hanselmann
    """Actually acquire the lock.
205 84e344d4 Michael Hanselmann

206 84e344d4 Michael Hanselmann
    """
207 84e344d4 Michael Hanselmann
    if shared:
208 84e344d4 Michael Hanselmann
      self.__shr.add(threading.currentThread())
209 84e344d4 Michael Hanselmann
    else:
210 84e344d4 Michael Hanselmann
      self.__exc = threading.currentThread()
211 a95fd5d7 Guido Trotter
212 84e344d4 Michael Hanselmann
  def __can_acquire(self, shared):
213 84e344d4 Michael Hanselmann
    """Determine whether lock can be acquired.
214 a95fd5d7 Guido Trotter

215 a95fd5d7 Guido Trotter
    """
216 84e344d4 Michael Hanselmann
    if shared:
217 84e344d4 Michael Hanselmann
      return self.__exc is None
218 84e344d4 Michael Hanselmann
    else:
219 84e344d4 Michael Hanselmann
      return len(self.__shr) == 0 and self.__exc is None
220 a95fd5d7 Guido Trotter
221 84e344d4 Michael Hanselmann
  def __is_on_top(self, cond):
222 84e344d4 Michael Hanselmann
    """Checks whether the passed condition is on top of the queue.
223 a95fd5d7 Guido Trotter

224 84e344d4 Michael Hanselmann
    The caller must make sure the queue isn't empty.
225 a95fd5d7 Guido Trotter

226 84e344d4 Michael Hanselmann
    """
227 84e344d4 Michael Hanselmann
    return self.__pending[0] == cond
228 4d686df8 Guido Trotter
229 84e344d4 Michael Hanselmann
  def __acquire_unlocked(self, shared=0, timeout=None):
230 84e344d4 Michael Hanselmann
    """Acquire a shared lock.
231 9216a9f7 Michael Hanselmann

232 84e344d4 Michael Hanselmann
    @param shared: whether to acquire in shared mode; by default an
233 84e344d4 Michael Hanselmann
        exclusive lock will be acquired
234 84e344d4 Michael Hanselmann
    @param timeout: maximum waiting time before giving up
235 9216a9f7 Michael Hanselmann

236 9216a9f7 Michael Hanselmann
    """
237 84e344d4 Michael Hanselmann
    self.__check_deleted()
238 9216a9f7 Michael Hanselmann
239 84e344d4 Michael Hanselmann
    # We cannot acquire the lock if we already have it
240 84e344d4 Michael Hanselmann
    assert not self.__is_owned(), "double acquire() on a non-recursive lock"
241 84e344d4 Michael Hanselmann
242 84e344d4 Michael Hanselmann
    # Check whether someone else holds the lock or there are pending acquires.
243 84e344d4 Michael Hanselmann
    if not self.__pending and self.__can_acquire(shared):
244 84e344d4 Michael Hanselmann
      # Apparently not, can acquire lock directly.
245 84e344d4 Michael Hanselmann
      self.__do_acquire(shared)
246 84e344d4 Michael Hanselmann
      return True
247 9216a9f7 Michael Hanselmann
248 84e344d4 Michael Hanselmann
    if shared:
249 84e344d4 Michael Hanselmann
      wait_condition = self.__active_shr_c
250 9216a9f7 Michael Hanselmann
251 84e344d4 Michael Hanselmann
      # Check if we're not yet in the queue
252 84e344d4 Michael Hanselmann
      if wait_condition not in self.__pending:
253 84e344d4 Michael Hanselmann
        self.__pending.append(wait_condition)
254 84e344d4 Michael Hanselmann
    else:
255 84e344d4 Michael Hanselmann
      wait_condition = self.__condition_class(self.__lock)
256 84e344d4 Michael Hanselmann
      # Always add to queue
257 84e344d4 Michael Hanselmann
      self.__pending.append(wait_condition)
258 84e344d4 Michael Hanselmann
259 84e344d4 Michael Hanselmann
    try:
260 84e344d4 Michael Hanselmann
      # Wait until we become the topmost acquire in the queue or the timeout
261 84e344d4 Michael Hanselmann
      # expires.
262 84e344d4 Michael Hanselmann
      while not (self.__is_on_top(wait_condition) and
263 84e344d4 Michael Hanselmann
                 self.__can_acquire(shared)):
264 84e344d4 Michael Hanselmann
        # Wait for notification
265 84e344d4 Michael Hanselmann
        wait_condition.wait(timeout)
266 84e344d4 Michael Hanselmann
        self.__check_deleted()
267 84e344d4 Michael Hanselmann
268 84e344d4 Michael Hanselmann
        # A lot of code assumes blocking acquires always succeed. Loop
269 84e344d4 Michael Hanselmann
        # internally for that case.
270 84e344d4 Michael Hanselmann
        if timeout is not None:
271 84e344d4 Michael Hanselmann
          break
272 84e344d4 Michael Hanselmann
273 84e344d4 Michael Hanselmann
      if self.__is_on_top(wait_condition) and self.__can_acquire(shared):
274 84e344d4 Michael Hanselmann
        self.__do_acquire(shared)
275 84e344d4 Michael Hanselmann
        return True
276 9216a9f7 Michael Hanselmann
    finally:
277 84e344d4 Michael Hanselmann
      # Remove condition from queue if there are no more waiters
278 84e344d4 Michael Hanselmann
      if not wait_condition.has_waiting() and not self.__deleted:
279 84e344d4 Michael Hanselmann
        self.__pending.remove(wait_condition)
280 9216a9f7 Michael Hanselmann
281 84e344d4 Michael Hanselmann
    return False
282 9216a9f7 Michael Hanselmann
283 84e344d4 Michael Hanselmann
  def acquire(self, shared=0, timeout=None):
284 162c1c1f Guido Trotter
    """Acquire a shared lock.
285 162c1c1f Guido Trotter

286 84e344d4 Michael Hanselmann
    @type shared: int
287 c41eea6e Iustin Pop
    @param shared: whether to acquire in shared mode; by default an
288 c41eea6e Iustin Pop
        exclusive lock will be acquired
289 84e344d4 Michael Hanselmann
    @type timeout: float
290 84e344d4 Michael Hanselmann
    @param timeout: maximum waiting time before giving up
291 162c1c1f Guido Trotter

292 162c1c1f Guido Trotter
    """
293 162c1c1f Guido Trotter
    self.__lock.acquire()
294 162c1c1f Guido Trotter
    try:
295 84e344d4 Michael Hanselmann
      return self.__acquire_unlocked(shared, timeout)
296 162c1c1f Guido Trotter
    finally:
297 162c1c1f Guido Trotter
      self.__lock.release()
298 162c1c1f Guido Trotter
299 162c1c1f Guido Trotter
  def release(self):
300 162c1c1f Guido Trotter
    """Release a Shared Lock.
301 162c1c1f Guido Trotter

302 162c1c1f Guido Trotter
    You must have acquired the lock, either in shared or in exclusive mode,
303 162c1c1f Guido Trotter
    before calling this function.
304 162c1c1f Guido Trotter

305 162c1c1f Guido Trotter
    """
306 162c1c1f Guido Trotter
    self.__lock.acquire()
307 162c1c1f Guido Trotter
    try:
308 84e344d4 Michael Hanselmann
      assert self.__is_exclusive() or self.__is_sharer(), \
309 84e344d4 Michael Hanselmann
        "Cannot release non-owned lock"
310 84e344d4 Michael Hanselmann
311 162c1c1f Guido Trotter
      # Autodetect release type
312 162c1c1f Guido Trotter
      if self.__is_exclusive():
313 162c1c1f Guido Trotter
        self.__exc = None
314 84e344d4 Michael Hanselmann
      else:
315 162c1c1f Guido Trotter
        self.__shr.remove(threading.currentThread())
316 162c1c1f Guido Trotter
317 84e344d4 Michael Hanselmann
      # Notify topmost condition in queue
318 84e344d4 Michael Hanselmann
      if self.__pending:
319 84e344d4 Michael Hanselmann
        first_condition = self.__pending[0]
320 84e344d4 Michael Hanselmann
        first_condition.notifyAll()
321 4d686df8 Guido Trotter
322 84e344d4 Michael Hanselmann
        if first_condition == self.__active_shr_c:
323 84e344d4 Michael Hanselmann
          self.__active_shr_c = self.__inactive_shr_c
324 84e344d4 Michael Hanselmann
          self.__inactive_shr_c = first_condition
325 162c1c1f Guido Trotter
326 162c1c1f Guido Trotter
    finally:
327 162c1c1f Guido Trotter
      self.__lock.release()
328 162c1c1f Guido Trotter
329 84e344d4 Michael Hanselmann
  def delete(self, timeout=None):
330 a95fd5d7 Guido Trotter
    """Delete a Shared Lock.
331 a95fd5d7 Guido Trotter

332 a95fd5d7 Guido Trotter
    This operation will declare the lock for removal. First the lock will be
333 a95fd5d7 Guido Trotter
    acquired in exclusive mode if you don't already own it, then the lock
334 a95fd5d7 Guido Trotter
    will be put in a state where any future and pending acquire() fail.
335 a95fd5d7 Guido Trotter

336 84e344d4 Michael Hanselmann
    @type timeout: float
337 84e344d4 Michael Hanselmann
    @param timeout: maximum waiting time before giving up
338 a95fd5d7 Guido Trotter

339 a95fd5d7 Guido Trotter
    """
340 a95fd5d7 Guido Trotter
    self.__lock.acquire()
341 a95fd5d7 Guido Trotter
    try:
342 84e344d4 Michael Hanselmann
      assert not self.__is_sharer(), "Cannot delete() a lock while sharing it"
343 84e344d4 Michael Hanselmann
344 84e344d4 Michael Hanselmann
      self.__check_deleted()
345 a95fd5d7 Guido Trotter
346 84e344d4 Michael Hanselmann
      # The caller is allowed to hold the lock exclusively already.
347 84e344d4 Michael Hanselmann
      acquired = self.__is_exclusive()
348 a95fd5d7 Guido Trotter
349 84e344d4 Michael Hanselmann
      if not acquired:
350 84e344d4 Michael Hanselmann
        acquired = self.__acquire_unlocked(timeout)
351 84e344d4 Michael Hanselmann
352 84e344d4 Michael Hanselmann
      if acquired:
353 84e344d4 Michael Hanselmann
        self.__deleted = True
354 84e344d4 Michael Hanselmann
        self.__exc = None
355 a95fd5d7 Guido Trotter
356 84e344d4 Michael Hanselmann
        # Notify all acquires. They'll throw an error.
357 84e344d4 Michael Hanselmann
        while self.__pending:
358 84e344d4 Michael Hanselmann
          self.__pending.pop().notifyAll()
359 a95fd5d7 Guido Trotter
360 84e344d4 Michael Hanselmann
      return acquired
361 a95fd5d7 Guido Trotter
    finally:
362 a95fd5d7 Guido Trotter
      self.__lock.release()
363 a95fd5d7 Guido Trotter
364 aaae9bc0 Guido Trotter
365 f12eadb3 Iustin Pop
# Whenever we want to acquire a full LockSet we pass None as the value
366 5bbd3f7f Michael Hanselmann
# to acquire.  Hide this behind this nicely named constant.
367 e310b019 Guido Trotter
ALL_SET = None
368 e310b019 Guido Trotter
369 e310b019 Guido Trotter
370 aaae9bc0 Guido Trotter
class LockSet:
371 aaae9bc0 Guido Trotter
  """Implements a set of locks.
372 aaae9bc0 Guido Trotter

373 aaae9bc0 Guido Trotter
  This abstraction implements a set of shared locks for the same resource type,
374 aaae9bc0 Guido Trotter
  distinguished by name. The user can lock a subset of the resources and the
375 aaae9bc0 Guido Trotter
  LockSet will take care of acquiring the locks always in the same order, thus
376 aaae9bc0 Guido Trotter
  preventing deadlock.
377 aaae9bc0 Guido Trotter

378 aaae9bc0 Guido Trotter
  All the locks needed in the same set must be acquired together, though.
379 aaae9bc0 Guido Trotter

380 aaae9bc0 Guido Trotter
  """
381 aaae9bc0 Guido Trotter
  def __init__(self, members=None):
382 aaae9bc0 Guido Trotter
    """Constructs a new LockSet.
383 aaae9bc0 Guido Trotter

384 c41eea6e Iustin Pop
    @param members: initial members of the set
385 aaae9bc0 Guido Trotter

386 aaae9bc0 Guido Trotter
    """
387 aaae9bc0 Guido Trotter
    # Used internally to guarantee coherency.
388 aaae9bc0 Guido Trotter
    self.__lock = SharedLock()
389 aaae9bc0 Guido Trotter
390 aaae9bc0 Guido Trotter
    # The lockdict indexes the relationship name -> lock
391 aaae9bc0 Guido Trotter
    # The order-of-locking is implied by the alphabetical order of names
392 aaae9bc0 Guido Trotter
    self.__lockdict = {}
393 aaae9bc0 Guido Trotter
394 aaae9bc0 Guido Trotter
    if members is not None:
395 aaae9bc0 Guido Trotter
      for name in members:
396 aaae9bc0 Guido Trotter
        self.__lockdict[name] = SharedLock()
397 aaae9bc0 Guido Trotter
398 aaae9bc0 Guido Trotter
    # The owner dict contains the set of locks each thread owns. For
399 aaae9bc0 Guido Trotter
    # performance each thread can access its own key without a global lock on
400 aaae9bc0 Guido Trotter
    # this structure. It is paramount though that *no* other type of access is
401 aaae9bc0 Guido Trotter
    # done to this structure (eg. no looping over its keys). *_owner helper
402 aaae9bc0 Guido Trotter
    # function are defined to guarantee access is correct, but in general never
403 aaae9bc0 Guido Trotter
    # do anything different than __owners[threading.currentThread()], or there
404 aaae9bc0 Guido Trotter
    # will be trouble.
405 aaae9bc0 Guido Trotter
    self.__owners = {}
406 aaae9bc0 Guido Trotter
407 aaae9bc0 Guido Trotter
  def _is_owned(self):
408 aaae9bc0 Guido Trotter
    """Is the current thread a current level owner?"""
409 aaae9bc0 Guido Trotter
    return threading.currentThread() in self.__owners
410 aaae9bc0 Guido Trotter
411 b2dabfd6 Guido Trotter
  def _add_owned(self, name=None):
412 aaae9bc0 Guido Trotter
    """Note the current thread owns the given lock"""
413 b2dabfd6 Guido Trotter
    if name is None:
414 b2dabfd6 Guido Trotter
      if not self._is_owned():
415 b2dabfd6 Guido Trotter
        self.__owners[threading.currentThread()] = set()
416 aaae9bc0 Guido Trotter
    else:
417 b2dabfd6 Guido Trotter
      if self._is_owned():
418 b2dabfd6 Guido Trotter
        self.__owners[threading.currentThread()].add(name)
419 b2dabfd6 Guido Trotter
      else:
420 b2dabfd6 Guido Trotter
        self.__owners[threading.currentThread()] = set([name])
421 b2dabfd6 Guido Trotter
422 b2dabfd6 Guido Trotter
  def _del_owned(self, name=None):
423 aaae9bc0 Guido Trotter
    """Note the current thread owns the given lock"""
424 aaae9bc0 Guido Trotter
425 b2dabfd6 Guido Trotter
    if name is not None:
426 b2dabfd6 Guido Trotter
      self.__owners[threading.currentThread()].remove(name)
427 b2dabfd6 Guido Trotter
428 b2dabfd6 Guido Trotter
    # Only remove the key if we don't hold the set-lock as well
429 b2dabfd6 Guido Trotter
    if (not self.__lock._is_owned() and
430 b2dabfd6 Guido Trotter
        not self.__owners[threading.currentThread()]):
431 aaae9bc0 Guido Trotter
      del self.__owners[threading.currentThread()]
432 aaae9bc0 Guido Trotter
433 aaae9bc0 Guido Trotter
  def _list_owned(self):
434 aaae9bc0 Guido Trotter
    """Get the set of resource names owned by the current thread"""
435 aaae9bc0 Guido Trotter
    if self._is_owned():
436 aaae9bc0 Guido Trotter
      return self.__owners[threading.currentThread()].copy()
437 aaae9bc0 Guido Trotter
    else:
438 aaae9bc0 Guido Trotter
      return set()
439 aaae9bc0 Guido Trotter
440 aaae9bc0 Guido Trotter
  def __names(self):
441 aaae9bc0 Guido Trotter
    """Return the current set of names.
442 aaae9bc0 Guido Trotter

443 aaae9bc0 Guido Trotter
    Only call this function while holding __lock and don't iterate on the
444 aaae9bc0 Guido Trotter
    result after releasing the lock.
445 aaae9bc0 Guido Trotter

446 aaae9bc0 Guido Trotter
    """
447 0cf257c5 Guido Trotter
    return self.__lockdict.keys()
448 aaae9bc0 Guido Trotter
449 aaae9bc0 Guido Trotter
  def _names(self):
450 aaae9bc0 Guido Trotter
    """Return a copy of the current set of elements.
451 aaae9bc0 Guido Trotter

452 aaae9bc0 Guido Trotter
    Used only for debugging purposes.
453 cdb08f44 Michael Hanselmann

454 aaae9bc0 Guido Trotter
    """
455 d4803c24 Guido Trotter
    # If we don't already own the set-level lock acquired
456 d4803c24 Guido Trotter
    # we'll get it and note we need to release it later.
457 d4803c24 Guido Trotter
    release_lock = False
458 d4803c24 Guido Trotter
    if not self.__lock._is_owned():
459 d4803c24 Guido Trotter
      release_lock = True
460 d4803c24 Guido Trotter
      self.__lock.acquire(shared=1)
461 aaae9bc0 Guido Trotter
    try:
462 aaae9bc0 Guido Trotter
      result = self.__names()
463 aaae9bc0 Guido Trotter
    finally:
464 d4803c24 Guido Trotter
      if release_lock:
465 d4803c24 Guido Trotter
        self.__lock.release()
466 0cf257c5 Guido Trotter
    return set(result)
467 aaae9bc0 Guido Trotter
468 aaae9bc0 Guido Trotter
  def acquire(self, names, blocking=1, shared=0):
469 aaae9bc0 Guido Trotter
    """Acquire a set of resource locks.
470 aaae9bc0 Guido Trotter

471 c41eea6e Iustin Pop
    @param names: the names of the locks which shall be acquired
472 c41eea6e Iustin Pop
        (special lock names, or instance/node names)
473 c41eea6e Iustin Pop
    @param shared: whether to acquire in shared mode; by default an
474 c41eea6e Iustin Pop
        exclusive lock will be acquired
475 c41eea6e Iustin Pop
    @param blocking: whether to block while trying to acquire or to
476 c41eea6e Iustin Pop
        operate in try-lock mode (this locking mode is not supported yet)
477 aaae9bc0 Guido Trotter

478 c41eea6e Iustin Pop
    @return: True when all the locks are successfully acquired
479 aaae9bc0 Guido Trotter

480 c41eea6e Iustin Pop
    @raise errors.LockError: when any lock we try to acquire has
481 c41eea6e Iustin Pop
        been deleted before we succeed. In this case none of the
482 c41eea6e Iustin Pop
        locks requested will be acquired.
483 aaae9bc0 Guido Trotter

484 aaae9bc0 Guido Trotter
    """
485 aaae9bc0 Guido Trotter
    if not blocking:
486 aaae9bc0 Guido Trotter
      # We don't have non-blocking mode for now
487 aaae9bc0 Guido Trotter
      raise NotImplementedError
488 aaae9bc0 Guido Trotter
489 aaae9bc0 Guido Trotter
    # Check we don't already own locks at this level
490 aaae9bc0 Guido Trotter
    assert not self._is_owned(), "Cannot acquire locks in the same set twice"
491 aaae9bc0 Guido Trotter
492 3b7ed473 Guido Trotter
    if names is None:
493 3b7ed473 Guido Trotter
      # If no names are given acquire the whole set by not letting new names
494 3b7ed473 Guido Trotter
      # being added before we release, and getting the current list of names.
495 3b7ed473 Guido Trotter
      # Some of them may then be deleted later, but we'll cope with this.
496 3b7ed473 Guido Trotter
      #
497 3b7ed473 Guido Trotter
      # We'd like to acquire this lock in a shared way, as it's nice if
498 3b7ed473 Guido Trotter
      # everybody else can use the instances at the same time. If are acquiring
499 3b7ed473 Guido Trotter
      # them exclusively though they won't be able to do this anyway, though,
500 3b7ed473 Guido Trotter
      # so we'll get the list lock exclusively as well in order to be able to
501 3b7ed473 Guido Trotter
      # do add() on the set while owning it.
502 3b7ed473 Guido Trotter
      self.__lock.acquire(shared=shared)
503 b2dabfd6 Guido Trotter
      try:
504 b2dabfd6 Guido Trotter
        # note we own the set-lock
505 b2dabfd6 Guido Trotter
        self._add_owned()
506 b2dabfd6 Guido Trotter
        names = self.__names()
507 b2dabfd6 Guido Trotter
      except:
508 b2dabfd6 Guido Trotter
        # We shouldn't have problems adding the lock to the owners list, but
509 b2dabfd6 Guido Trotter
        # if we did we'll try to release this lock and re-raise exception.
510 b2dabfd6 Guido Trotter
        # Of course something is going to be really wrong, after this.
511 b2dabfd6 Guido Trotter
        self.__lock.release()
512 b2dabfd6 Guido Trotter
        raise
513 3b7ed473 Guido Trotter
514 806e20fd Guido Trotter
    try:
515 806e20fd Guido Trotter
      # Support passing in a single resource to acquire rather than many
516 806e20fd Guido Trotter
      if isinstance(names, basestring):
517 806e20fd Guido Trotter
        names = [names]
518 806e20fd Guido Trotter
      else:
519 2a21bc88 Iustin Pop
        names = sorted(names)
520 806e20fd Guido Trotter
521 806e20fd Guido Trotter
      acquire_list = []
522 806e20fd Guido Trotter
      # First we look the locks up on __lockdict. We have no way of being sure
523 806e20fd Guido Trotter
      # they will still be there after, but this makes it a lot faster should
524 806e20fd Guido Trotter
      # just one of them be the already wrong
525 34ca3914 Guido Trotter
      for lname in utils.UniqueSequence(names):
526 806e20fd Guido Trotter
        try:
527 4e07ec8c Guido Trotter
          lock = self.__lockdict[lname] # raises KeyError if lock is not there
528 806e20fd Guido Trotter
          acquire_list.append((lname, lock))
529 806e20fd Guido Trotter
        except (KeyError):
530 3b7ed473 Guido Trotter
          if self.__lock._is_owned():
531 f12eadb3 Iustin Pop
            # We are acquiring all the set, it doesn't matter if this
532 f12eadb3 Iustin Pop
            # particular element is not there anymore.
533 3b7ed473 Guido Trotter
            continue
534 3b7ed473 Guido Trotter
          else:
535 3b7ed473 Guido Trotter
            raise errors.LockError('non-existing lock in set (%s)' % lname)
536 806e20fd Guido Trotter
537 806e20fd Guido Trotter
      # This will hold the locknames we effectively acquired.
538 806e20fd Guido Trotter
      acquired = set()
539 806e20fd Guido Trotter
      # Now acquire_list contains a sorted list of resources and locks we want.
540 806e20fd Guido Trotter
      # In order to get them we loop on this (private) list and acquire() them.
541 806e20fd Guido Trotter
      # We gave no real guarantee they will still exist till this is done but
542 806e20fd Guido Trotter
      # .acquire() itself is safe and will alert us if the lock gets deleted.
543 806e20fd Guido Trotter
      for (lname, lock) in acquire_list:
544 aaae9bc0 Guido Trotter
        try:
545 806e20fd Guido Trotter
          lock.acquire(shared=shared) # raises LockError if the lock is deleted
546 ea3f80bf Guido Trotter
          # now the lock cannot be deleted, we have it!
547 b2dabfd6 Guido Trotter
          self._add_owned(name=lname)
548 ea3f80bf Guido Trotter
          acquired.add(lname)
549 806e20fd Guido Trotter
        except (errors.LockError):
550 3b7ed473 Guido Trotter
          if self.__lock._is_owned():
551 f12eadb3 Iustin Pop
            # We are acquiring all the set, it doesn't matter if this
552 f12eadb3 Iustin Pop
            # particular element is not there anymore.
553 3b7ed473 Guido Trotter
            continue
554 3b7ed473 Guido Trotter
          else:
555 3b7ed473 Guido Trotter
            name_fail = lname
556 3b7ed473 Guido Trotter
            for lname in self._list_owned():
557 3b7ed473 Guido Trotter
              self.__lockdict[lname].release()
558 b2dabfd6 Guido Trotter
              self._del_owned(name=lname)
559 3b7ed473 Guido Trotter
            raise errors.LockError('non-existing lock in set (%s)' % name_fail)
560 ea3f80bf Guido Trotter
        except:
561 ea3f80bf Guido Trotter
          # We shouldn't have problems adding the lock to the owners list, but
562 ea3f80bf Guido Trotter
          # if we did we'll try to release this lock and re-raise exception.
563 ea3f80bf Guido Trotter
          # Of course something is going to be really wrong, after this.
564 ea3f80bf Guido Trotter
          if lock._is_owned():
565 ea3f80bf Guido Trotter
            lock.release()
566 470ce2ee Michael Hanselmann
          raise
567 806e20fd Guido Trotter
568 806e20fd Guido Trotter
    except:
569 3b7ed473 Guido Trotter
      # If something went wrong and we had the set-lock let's release it...
570 3b7ed473 Guido Trotter
      if self.__lock._is_owned():
571 3b7ed473 Guido Trotter
        self.__lock.release()
572 806e20fd Guido Trotter
      raise
573 aaae9bc0 Guido Trotter
574 0cc00929 Guido Trotter
    return acquired
575 aaae9bc0 Guido Trotter
576 aaae9bc0 Guido Trotter
  def release(self, names=None):
577 aaae9bc0 Guido Trotter
    """Release a set of resource locks, at the same level.
578 aaae9bc0 Guido Trotter

579 aaae9bc0 Guido Trotter
    You must have acquired the locks, either in shared or in exclusive mode,
580 aaae9bc0 Guido Trotter
    before releasing them.
581 aaae9bc0 Guido Trotter

582 c41eea6e Iustin Pop
    @param names: the names of the locks which shall be released
583 c41eea6e Iustin Pop
        (defaults to all the locks acquired at that level).
584 aaae9bc0 Guido Trotter

585 aaae9bc0 Guido Trotter
    """
586 aaae9bc0 Guido Trotter
    assert self._is_owned(), "release() on lock set while not owner"
587 aaae9bc0 Guido Trotter
588 aaae9bc0 Guido Trotter
    # Support passing in a single resource to release rather than many
589 aaae9bc0 Guido Trotter
    if isinstance(names, basestring):
590 aaae9bc0 Guido Trotter
      names = [names]
591 aaae9bc0 Guido Trotter
592 aaae9bc0 Guido Trotter
    if names is None:
593 aaae9bc0 Guido Trotter
      names = self._list_owned()
594 aaae9bc0 Guido Trotter
    else:
595 aaae9bc0 Guido Trotter
      names = set(names)
596 aaae9bc0 Guido Trotter
      assert self._list_owned().issuperset(names), (
597 aaae9bc0 Guido Trotter
               "release() on unheld resources %s" %
598 aaae9bc0 Guido Trotter
               names.difference(self._list_owned()))
599 aaae9bc0 Guido Trotter
600 3b7ed473 Guido Trotter
    # First of all let's release the "all elements" lock, if set.
601 3b7ed473 Guido Trotter
    # After this 'add' can work again
602 3b7ed473 Guido Trotter
    if self.__lock._is_owned():
603 3b7ed473 Guido Trotter
      self.__lock.release()
604 b2dabfd6 Guido Trotter
      self._del_owned()
605 3b7ed473 Guido Trotter
606 aaae9bc0 Guido Trotter
    for lockname in names:
607 aaae9bc0 Guido Trotter
      # If we are sure the lock doesn't leave __lockdict without being
608 aaae9bc0 Guido Trotter
      # exclusively held we can do this...
609 aaae9bc0 Guido Trotter
      self.__lockdict[lockname].release()
610 b2dabfd6 Guido Trotter
      self._del_owned(name=lockname)
611 aaae9bc0 Guido Trotter
612 aaae9bc0 Guido Trotter
  def add(self, names, acquired=0, shared=0):
613 aaae9bc0 Guido Trotter
    """Add a new set of elements to the set
614 aaae9bc0 Guido Trotter

615 c41eea6e Iustin Pop
    @param names: names of the new elements to add
616 c41eea6e Iustin Pop
    @param acquired: pre-acquire the new resource?
617 c41eea6e Iustin Pop
    @param shared: is the pre-acquisition shared?
618 aaae9bc0 Guido Trotter

619 aaae9bc0 Guido Trotter
    """
620 d2aff862 Guido Trotter
    # Check we don't already own locks at this level
621 d2aff862 Guido Trotter
    assert not self._is_owned() or self.__lock._is_owned(shared=0), \
622 d2aff862 Guido Trotter
      "Cannot add locks if the set is only partially owned, or shared"
623 3b7ed473 Guido Trotter
624 aaae9bc0 Guido Trotter
    # Support passing in a single resource to add rather than many
625 aaae9bc0 Guido Trotter
    if isinstance(names, basestring):
626 aaae9bc0 Guido Trotter
      names = [names]
627 aaae9bc0 Guido Trotter
628 ab62526c Guido Trotter
    # If we don't already own the set-level lock acquired in an exclusive way
629 3b7ed473 Guido Trotter
    # we'll get it and note we need to release it later.
630 3b7ed473 Guido Trotter
    release_lock = False
631 3b7ed473 Guido Trotter
    if not self.__lock._is_owned():
632 3b7ed473 Guido Trotter
      release_lock = True
633 3b7ed473 Guido Trotter
      self.__lock.acquire()
634 3b7ed473 Guido Trotter
635 aaae9bc0 Guido Trotter
    try:
636 0cf257c5 Guido Trotter
      invalid_names = set(self.__names()).intersection(names)
637 aaae9bc0 Guido Trotter
      if invalid_names:
638 aaae9bc0 Guido Trotter
        # This must be an explicit raise, not an assert, because assert is
639 aaae9bc0 Guido Trotter
        # turned off when using optimization, and this can happen because of
640 aaae9bc0 Guido Trotter
        # concurrency even if the user doesn't want it.
641 aaae9bc0 Guido Trotter
        raise errors.LockError("duplicate add() (%s)" % invalid_names)
642 aaae9bc0 Guido Trotter
643 aaae9bc0 Guido Trotter
      for lockname in names:
644 aaae9bc0 Guido Trotter
        lock = SharedLock()
645 aaae9bc0 Guido Trotter
646 aaae9bc0 Guido Trotter
        if acquired:
647 aaae9bc0 Guido Trotter
          lock.acquire(shared=shared)
648 aaae9bc0 Guido Trotter
          # now the lock cannot be deleted, we have it!
649 aaae9bc0 Guido Trotter
          try:
650 b2dabfd6 Guido Trotter
            self._add_owned(name=lockname)
651 aaae9bc0 Guido Trotter
          except:
652 aaae9bc0 Guido Trotter
            # We shouldn't have problems adding the lock to the owners list,
653 aaae9bc0 Guido Trotter
            # but if we did we'll try to release this lock and re-raise
654 aaae9bc0 Guido Trotter
            # exception.  Of course something is going to be really wrong,
655 aaae9bc0 Guido Trotter
            # after this.  On the other hand the lock hasn't been added to the
656 aaae9bc0 Guido Trotter
            # __lockdict yet so no other threads should be pending on it. This
657 aaae9bc0 Guido Trotter
            # release is just a safety measure.
658 aaae9bc0 Guido Trotter
            lock.release()
659 aaae9bc0 Guido Trotter
            raise
660 aaae9bc0 Guido Trotter
661 aaae9bc0 Guido Trotter
        self.__lockdict[lockname] = lock
662 aaae9bc0 Guido Trotter
663 aaae9bc0 Guido Trotter
    finally:
664 3b7ed473 Guido Trotter
      # Only release __lock if we were not holding it previously.
665 3b7ed473 Guido Trotter
      if release_lock:
666 3b7ed473 Guido Trotter
        self.__lock.release()
667 aaae9bc0 Guido Trotter
668 aaae9bc0 Guido Trotter
    return True
669 aaae9bc0 Guido Trotter
670 aaae9bc0 Guido Trotter
  def remove(self, names, blocking=1):
671 aaae9bc0 Guido Trotter
    """Remove elements from the lock set.
672 aaae9bc0 Guido Trotter

673 aaae9bc0 Guido Trotter
    You can either not hold anything in the lockset or already hold a superset
674 aaae9bc0 Guido Trotter
    of the elements you want to delete, exclusively.
675 aaae9bc0 Guido Trotter

676 c41eea6e Iustin Pop
    @param names: names of the resource to remove.
677 c41eea6e Iustin Pop
    @param blocking: whether to block while trying to acquire or to
678 c41eea6e Iustin Pop
        operate in try-lock mode (this locking mode is not supported
679 c41eea6e Iustin Pop
        yet unless you are already holding exclusively the locks)
680 aaae9bc0 Guido Trotter

681 c41eea6e Iustin Pop
    @return:: a list of locks which we removed; the list is always
682 c41eea6e Iustin Pop
        equal to the names list if we were holding all the locks
683 c41eea6e Iustin Pop
        exclusively
684 aaae9bc0 Guido Trotter

685 aaae9bc0 Guido Trotter
    """
686 aaae9bc0 Guido Trotter
    if not blocking and not self._is_owned():
687 aaae9bc0 Guido Trotter
      # We don't have non-blocking mode for now
688 aaae9bc0 Guido Trotter
      raise NotImplementedError
689 aaae9bc0 Guido Trotter
690 aaae9bc0 Guido Trotter
    # Support passing in a single resource to remove rather than many
691 aaae9bc0 Guido Trotter
    if isinstance(names, basestring):
692 aaae9bc0 Guido Trotter
      names = [names]
693 aaae9bc0 Guido Trotter
694 aaae9bc0 Guido Trotter
    # If we own any subset of this lock it must be a superset of what we want
695 aaae9bc0 Guido Trotter
    # to delete. The ownership must also be exclusive, but that will be checked
696 aaae9bc0 Guido Trotter
    # by the lock itself.
697 aaae9bc0 Guido Trotter
    assert not self._is_owned() or self._list_owned().issuperset(names), (
698 aaae9bc0 Guido Trotter
      "remove() on acquired lockset while not owning all elements")
699 aaae9bc0 Guido Trotter
700 3f404fc5 Guido Trotter
    removed = []
701 aaae9bc0 Guido Trotter
702 aaae9bc0 Guido Trotter
    for lname in names:
703 aaae9bc0 Guido Trotter
      # Calling delete() acquires the lock exclusively if we don't already own
704 aaae9bc0 Guido Trotter
      # it, and causes all pending and subsequent lock acquires to fail. It's
705 aaae9bc0 Guido Trotter
      # fine to call it out of order because delete() also implies release(),
706 aaae9bc0 Guido Trotter
      # and the assertion above guarantees that if we either already hold
707 aaae9bc0 Guido Trotter
      # everything we want to delete, or we hold none.
708 aaae9bc0 Guido Trotter
      try:
709 aaae9bc0 Guido Trotter
        self.__lockdict[lname].delete()
710 3f404fc5 Guido Trotter
        removed.append(lname)
711 aaae9bc0 Guido Trotter
      except (KeyError, errors.LockError):
712 aaae9bc0 Guido Trotter
        # This cannot happen if we were already holding it, verify:
713 aaae9bc0 Guido Trotter
        assert not self._is_owned(), "remove failed while holding lockset"
714 aaae9bc0 Guido Trotter
      else:
715 aaae9bc0 Guido Trotter
        # If no LockError was raised we are the ones who deleted the lock.
716 aaae9bc0 Guido Trotter
        # This means we can safely remove it from lockdict, as any further or
717 aaae9bc0 Guido Trotter
        # pending delete() or acquire() will fail (and nobody can have the lock
718 aaae9bc0 Guido Trotter
        # since before our call to delete()).
719 aaae9bc0 Guido Trotter
        #
720 aaae9bc0 Guido Trotter
        # This is done in an else clause because if the exception was thrown
721 aaae9bc0 Guido Trotter
        # it's the job of the one who actually deleted it.
722 aaae9bc0 Guido Trotter
        del self.__lockdict[lname]
723 aaae9bc0 Guido Trotter
        # And let's remove it from our private list if we owned it.
724 aaae9bc0 Guido Trotter
        if self._is_owned():
725 b2dabfd6 Guido Trotter
          self._del_owned(name=lname)
726 aaae9bc0 Guido Trotter
727 3f404fc5 Guido Trotter
    return removed
728 aaae9bc0 Guido Trotter
729 7ee7c0c7 Guido Trotter
730 7ee7c0c7 Guido Trotter
# Locking levels, must be acquired in increasing order.
731 7ee7c0c7 Guido Trotter
# Current rules are:
732 7ee7c0c7 Guido Trotter
#   - at level LEVEL_CLUSTER resides the Big Ganeti Lock (BGL) which must be
733 7ee7c0c7 Guido Trotter
#   acquired before performing any operation, either in shared or in exclusive
734 7ee7c0c7 Guido Trotter
#   mode. acquiring the BGL in exclusive mode is discouraged and should be
735 7ee7c0c7 Guido Trotter
#   avoided.
736 7ee7c0c7 Guido Trotter
#   - at levels LEVEL_NODE and LEVEL_INSTANCE reside node and instance locks.
737 7ee7c0c7 Guido Trotter
#   If you need more than one node, or more than one instance, acquire them at
738 7ee7c0c7 Guido Trotter
#   the same time.
739 7ee7c0c7 Guido Trotter
LEVEL_CLUSTER = 0
740 04e1bfaf Guido Trotter
LEVEL_INSTANCE = 1
741 04e1bfaf Guido Trotter
LEVEL_NODE = 2
742 7ee7c0c7 Guido Trotter
743 7ee7c0c7 Guido Trotter
LEVELS = [LEVEL_CLUSTER,
744 04e1bfaf Guido Trotter
          LEVEL_INSTANCE,
745 04e1bfaf Guido Trotter
          LEVEL_NODE]
746 7ee7c0c7 Guido Trotter
747 7ee7c0c7 Guido Trotter
# Lock levels which are modifiable
748 7ee7c0c7 Guido Trotter
LEVELS_MOD = [LEVEL_NODE, LEVEL_INSTANCE]
749 7ee7c0c7 Guido Trotter
750 ea205dbc Michael Hanselmann
LEVEL_NAMES = {
751 ea205dbc Michael Hanselmann
  LEVEL_CLUSTER: "cluster",
752 ea205dbc Michael Hanselmann
  LEVEL_INSTANCE: "instance",
753 ea205dbc Michael Hanselmann
  LEVEL_NODE: "node",
754 ea205dbc Michael Hanselmann
  }
755 ea205dbc Michael Hanselmann
756 08a6c581 Guido Trotter
# Constant for the big ganeti lock
757 7ee7c0c7 Guido Trotter
BGL = 'BGL'
758 7ee7c0c7 Guido Trotter
759 7ee7c0c7 Guido Trotter
760 7ee7c0c7 Guido Trotter
class GanetiLockManager:
761 7ee7c0c7 Guido Trotter
  """The Ganeti Locking Library
762 7ee7c0c7 Guido Trotter

763 5bbd3f7f Michael Hanselmann
  The purpose of this small library is to manage locking for ganeti clusters
764 7ee7c0c7 Guido Trotter
  in a central place, while at the same time doing dynamic checks against
765 7ee7c0c7 Guido Trotter
  possible deadlocks. It will also make it easier to transition to a different
766 7ee7c0c7 Guido Trotter
  lock type should we migrate away from python threads.
767 7ee7c0c7 Guido Trotter

768 7ee7c0c7 Guido Trotter
  """
769 7ee7c0c7 Guido Trotter
  _instance = None
770 7ee7c0c7 Guido Trotter
771 7ee7c0c7 Guido Trotter
  def __init__(self, nodes=None, instances=None):
772 7ee7c0c7 Guido Trotter
    """Constructs a new GanetiLockManager object.
773 7ee7c0c7 Guido Trotter

774 4e07ec8c Guido Trotter
    There should be only a GanetiLockManager object at any time, so this
775 4e07ec8c Guido Trotter
    function raises an error if this is not the case.
776 7ee7c0c7 Guido Trotter

777 c41eea6e Iustin Pop
    @param nodes: list of node names
778 c41eea6e Iustin Pop
    @param instances: list of instance names
779 7ee7c0c7 Guido Trotter

780 7ee7c0c7 Guido Trotter
    """
781 c41eea6e Iustin Pop
    assert self.__class__._instance is None, \
782 c41eea6e Iustin Pop
           "double GanetiLockManager instance"
783 c41eea6e Iustin Pop
784 7ee7c0c7 Guido Trotter
    self.__class__._instance = self
785 7ee7c0c7 Guido Trotter
786 7ee7c0c7 Guido Trotter
    # The keyring contains all the locks, at their level and in the correct
787 7ee7c0c7 Guido Trotter
    # locking order.
788 7ee7c0c7 Guido Trotter
    self.__keyring = {
789 7ee7c0c7 Guido Trotter
      LEVEL_CLUSTER: LockSet([BGL]),
790 7ee7c0c7 Guido Trotter
      LEVEL_NODE: LockSet(nodes),
791 7ee7c0c7 Guido Trotter
      LEVEL_INSTANCE: LockSet(instances),
792 7ee7c0c7 Guido Trotter
    }
793 7ee7c0c7 Guido Trotter
794 7ee7c0c7 Guido Trotter
  def _names(self, level):
795 7ee7c0c7 Guido Trotter
    """List the lock names at the given level.
796 7ee7c0c7 Guido Trotter

797 c41eea6e Iustin Pop
    This can be used for debugging/testing purposes.
798 c41eea6e Iustin Pop

799 c41eea6e Iustin Pop
    @param level: the level whose list of locks to get
800 7ee7c0c7 Guido Trotter

801 7ee7c0c7 Guido Trotter
    """
802 7ee7c0c7 Guido Trotter
    assert level in LEVELS, "Invalid locking level %s" % level
803 7ee7c0c7 Guido Trotter
    return self.__keyring[level]._names()
804 7ee7c0c7 Guido Trotter
805 7ee7c0c7 Guido Trotter
  def _is_owned(self, level):
806 7ee7c0c7 Guido Trotter
    """Check whether we are owning locks at the given level
807 7ee7c0c7 Guido Trotter

808 7ee7c0c7 Guido Trotter
    """
809 7ee7c0c7 Guido Trotter
    return self.__keyring[level]._is_owned()
810 7ee7c0c7 Guido Trotter
811 d4f4b3e7 Guido Trotter
  is_owned = _is_owned
812 d4f4b3e7 Guido Trotter
813 7ee7c0c7 Guido Trotter
  def _list_owned(self, level):
814 7ee7c0c7 Guido Trotter
    """Get the set of owned locks at the given level
815 7ee7c0c7 Guido Trotter

816 7ee7c0c7 Guido Trotter
    """
817 7ee7c0c7 Guido Trotter
    return self.__keyring[level]._list_owned()
818 7ee7c0c7 Guido Trotter
819 7ee7c0c7 Guido Trotter
  def _upper_owned(self, level):
820 7ee7c0c7 Guido Trotter
    """Check that we don't own any lock at a level greater than the given one.
821 7ee7c0c7 Guido Trotter

822 7ee7c0c7 Guido Trotter
    """
823 7ee7c0c7 Guido Trotter
    # This way of checking only works if LEVELS[i] = i, which we check for in
824 7ee7c0c7 Guido Trotter
    # the test cases.
825 7ee7c0c7 Guido Trotter
    return utils.any((self._is_owned(l) for l in LEVELS[level + 1:]))
826 7ee7c0c7 Guido Trotter
827 7ee7c0c7 Guido Trotter
  def _BGL_owned(self):
828 7ee7c0c7 Guido Trotter
    """Check if the current thread owns the BGL.
829 7ee7c0c7 Guido Trotter

830 7ee7c0c7 Guido Trotter
    Both an exclusive or a shared acquisition work.
831 7ee7c0c7 Guido Trotter

832 7ee7c0c7 Guido Trotter
    """
833 7ee7c0c7 Guido Trotter
    return BGL in self.__keyring[LEVEL_CLUSTER]._list_owned()
834 7ee7c0c7 Guido Trotter
835 7ee7c0c7 Guido Trotter
  def _contains_BGL(self, level, names):
836 c41eea6e Iustin Pop
    """Check if the level contains the BGL.
837 c41eea6e Iustin Pop

838 c41eea6e Iustin Pop
    Check if acting on the given level and set of names will change
839 c41eea6e Iustin Pop
    the status of the Big Ganeti Lock.
840 7ee7c0c7 Guido Trotter

841 7ee7c0c7 Guido Trotter
    """
842 7ee7c0c7 Guido Trotter
    return level == LEVEL_CLUSTER and (names is None or BGL in names)
843 7ee7c0c7 Guido Trotter
844 7ee7c0c7 Guido Trotter
  def acquire(self, level, names, blocking=1, shared=0):
845 7ee7c0c7 Guido Trotter
    """Acquire a set of resource locks, at the same level.
846 7ee7c0c7 Guido Trotter

847 c41eea6e Iustin Pop
    @param level: the level at which the locks shall be acquired;
848 5bbd3f7f Michael Hanselmann
        it must be a member of LEVELS.
849 c41eea6e Iustin Pop
    @param names: the names of the locks which shall be acquired
850 c41eea6e Iustin Pop
        (special lock names, or instance/node names)
851 c41eea6e Iustin Pop
    @param shared: whether to acquire in shared mode; by default
852 c41eea6e Iustin Pop
        an exclusive lock will be acquired
853 c41eea6e Iustin Pop
    @param blocking: whether to block while trying to acquire or to
854 c41eea6e Iustin Pop
        operate in try-lock mode (this locking mode is not supported yet)
855 7ee7c0c7 Guido Trotter

856 7ee7c0c7 Guido Trotter
    """
857 7ee7c0c7 Guido Trotter
    assert level in LEVELS, "Invalid locking level %s" % level
858 7ee7c0c7 Guido Trotter
859 7ee7c0c7 Guido Trotter
    # Check that we are either acquiring the Big Ganeti Lock or we already own
860 7ee7c0c7 Guido Trotter
    # it. Some "legacy" opcodes need to be sure they are run non-concurrently
861 7ee7c0c7 Guido Trotter
    # so even if we've migrated we need to at least share the BGL to be
862 7ee7c0c7 Guido Trotter
    # compatible with them. Of course if we own the BGL exclusively there's no
863 7ee7c0c7 Guido Trotter
    # point in acquiring any other lock, unless perhaps we are half way through
864 7ee7c0c7 Guido Trotter
    # the migration of the current opcode.
865 7ee7c0c7 Guido Trotter
    assert (self._contains_BGL(level, names) or self._BGL_owned()), (
866 7ee7c0c7 Guido Trotter
            "You must own the Big Ganeti Lock before acquiring any other")
867 7ee7c0c7 Guido Trotter
868 7ee7c0c7 Guido Trotter
    # Check we don't own locks at the same or upper levels.
869 21a6c826 Guido Trotter
    assert not self._upper_owned(level), ("Cannot acquire locks at a level"
870 7ee7c0c7 Guido Trotter
           " while owning some at a greater one")
871 7ee7c0c7 Guido Trotter
872 7ee7c0c7 Guido Trotter
    # Acquire the locks in the set.
873 7ee7c0c7 Guido Trotter
    return self.__keyring[level].acquire(names, shared=shared,
874 7ee7c0c7 Guido Trotter
                                         blocking=blocking)
875 7ee7c0c7 Guido Trotter
876 7ee7c0c7 Guido Trotter
  def release(self, level, names=None):
877 7ee7c0c7 Guido Trotter
    """Release a set of resource locks, at the same level.
878 7ee7c0c7 Guido Trotter

879 c41eea6e Iustin Pop
    You must have acquired the locks, either in shared or in exclusive
880 c41eea6e Iustin Pop
    mode, before releasing them.
881 7ee7c0c7 Guido Trotter

882 c41eea6e Iustin Pop
    @param level: the level at which the locks shall be released;
883 5bbd3f7f Michael Hanselmann
        it must be a member of LEVELS
884 c41eea6e Iustin Pop
    @param names: the names of the locks which shall be released
885 c41eea6e Iustin Pop
        (defaults to all the locks acquired at that level)
886 7ee7c0c7 Guido Trotter

887 7ee7c0c7 Guido Trotter
    """
888 7ee7c0c7 Guido Trotter
    assert level in LEVELS, "Invalid locking level %s" % level
889 7ee7c0c7 Guido Trotter
    assert (not self._contains_BGL(level, names) or
890 7ee7c0c7 Guido Trotter
            not self._upper_owned(LEVEL_CLUSTER)), (
891 7ee7c0c7 Guido Trotter
            "Cannot release the Big Ganeti Lock while holding something"
892 7ee7c0c7 Guido Trotter
            " at upper levels")
893 7ee7c0c7 Guido Trotter
894 7ee7c0c7 Guido Trotter
    # Release will complain if we don't own the locks already
895 7ee7c0c7 Guido Trotter
    return self.__keyring[level].release(names)
896 7ee7c0c7 Guido Trotter
897 7ee7c0c7 Guido Trotter
  def add(self, level, names, acquired=0, shared=0):
898 7ee7c0c7 Guido Trotter
    """Add locks at the specified level.
899 7ee7c0c7 Guido Trotter

900 c41eea6e Iustin Pop
    @param level: the level at which the locks shall be added;
901 5bbd3f7f Michael Hanselmann
        it must be a member of LEVELS_MOD.
902 c41eea6e Iustin Pop
    @param names: names of the locks to acquire
903 c41eea6e Iustin Pop
    @param acquired: whether to acquire the newly added locks
904 c41eea6e Iustin Pop
    @param shared: whether the acquisition will be shared
905 c41eea6e Iustin Pop

906 7ee7c0c7 Guido Trotter
    """
907 7ee7c0c7 Guido Trotter
    assert level in LEVELS_MOD, "Invalid or immutable level %s" % level
908 7ee7c0c7 Guido Trotter
    assert self._BGL_owned(), ("You must own the BGL before performing other"
909 7ee7c0c7 Guido Trotter
           " operations")
910 7ee7c0c7 Guido Trotter
    assert not self._upper_owned(level), ("Cannot add locks at a level"
911 7ee7c0c7 Guido Trotter
           " while owning some at a greater one")
912 7ee7c0c7 Guido Trotter
    return self.__keyring[level].add(names, acquired=acquired, shared=shared)
913 7ee7c0c7 Guido Trotter
914 7ee7c0c7 Guido Trotter
  def remove(self, level, names, blocking=1):
915 7ee7c0c7 Guido Trotter
    """Remove locks from the specified level.
916 7ee7c0c7 Guido Trotter

917 c41eea6e Iustin Pop
    You must either already own the locks you are trying to remove
918 c41eea6e Iustin Pop
    exclusively or not own any lock at an upper level.
919 7ee7c0c7 Guido Trotter

920 c41eea6e Iustin Pop
    @param level: the level at which the locks shall be removed;
921 c41eea6e Iustin Pop
        it must be a member of LEVELS_MOD
922 c41eea6e Iustin Pop
    @param names: the names of the locks which shall be removed
923 c41eea6e Iustin Pop
        (special lock names, or instance/node names)
924 c41eea6e Iustin Pop
    @param blocking: whether to block while trying to operate in
925 c41eea6e Iustin Pop
        try-lock mode (this locking mode is not supported yet)
926 7ee7c0c7 Guido Trotter

927 7ee7c0c7 Guido Trotter
    """
928 7ee7c0c7 Guido Trotter
    assert level in LEVELS_MOD, "Invalid or immutable level %s" % level
929 7ee7c0c7 Guido Trotter
    assert self._BGL_owned(), ("You must own the BGL before performing other"
930 7ee7c0c7 Guido Trotter
           " operations")
931 f12eadb3 Iustin Pop
    # Check we either own the level or don't own anything from here
932 f12eadb3 Iustin Pop
    # up. LockSet.remove() will check the case in which we don't own
933 f12eadb3 Iustin Pop
    # all the needed resources, or we have a shared ownership.
934 7ee7c0c7 Guido Trotter
    assert self._is_owned(level) or not self._upper_owned(level), (
935 7ee7c0c7 Guido Trotter
           "Cannot remove locks at a level while not owning it or"
936 7ee7c0c7 Guido Trotter
           " owning some at a greater one")
937 cdb08f44 Michael Hanselmann
    return self.__keyring[level].remove(names, blocking=blocking)