Statistics
| Branch: | Tag: | Revision:

root / lib / locking.py @ 63b4bb1e

History | View | Annotate | Download (55.5 kB)

1 162c1c1f Guido Trotter
#
2 162c1c1f Guido Trotter
#
3 162c1c1f Guido Trotter
4 eb62069e Iustin Pop
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 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 b459a848 Andrea Spadaccini
# pylint: disable=W0212
24 c70d2d9b Iustin Pop
25 c70d2d9b Iustin Pop
# W0212 since e.g. LockSet methods use (a lot) the internals of
26 c70d2d9b Iustin Pop
# SharedLock
27 162c1c1f Guido Trotter
28 d76167a5 Michael Hanselmann
import os
29 d76167a5 Michael Hanselmann
import select
30 162c1c1f Guido Trotter
import threading
31 d76167a5 Michael Hanselmann
import errno
32 19b9ba9a Michael Hanselmann
import weakref
33 19b9ba9a Michael Hanselmann
import logging
34 887c7aa6 Michael Hanselmann
import heapq
35 e4e35357 Michael Hanselmann
import itertools
36 84e344d4 Michael Hanselmann
37 a95fd5d7 Guido Trotter
from ganeti import errors
38 7ee7c0c7 Guido Trotter
from ganeti import utils
39 cea881e5 Michael Hanselmann
from ganeti import compat
40 24d16f76 Michael Hanselmann
from ganeti import query
41 162c1c1f Guido Trotter
42 162c1c1f Guido Trotter
43 c31825f7 Michael Hanselmann
_EXCLUSIVE_TEXT = "exclusive"
44 c31825f7 Michael Hanselmann
_SHARED_TEXT = "shared"
45 24d16f76 Michael Hanselmann
_DELETED_TEXT = "deleted"
46 c31825f7 Michael Hanselmann
47 887c7aa6 Michael Hanselmann
_DEFAULT_PRIORITY = 0
48 887c7aa6 Michael Hanselmann
49 c31825f7 Michael Hanselmann
50 dbb11e8b Guido Trotter
def ssynchronized(mylock, shared=0):
51 42a999d1 Guido Trotter
  """Shared Synchronization decorator.
52 42a999d1 Guido Trotter

53 42a999d1 Guido Trotter
  Calls the function holding the given lock, either in exclusive or shared
54 42a999d1 Guido Trotter
  mode. It requires the passed lock to be a SharedLock (or support its
55 42a999d1 Guido Trotter
  semantics).
56 42a999d1 Guido Trotter

57 dbb11e8b Guido Trotter
  @type mylock: lockable object or string
58 dbb11e8b Guido Trotter
  @param mylock: lock to acquire or class member name of the lock to acquire
59 dbb11e8b Guido Trotter

60 42a999d1 Guido Trotter
  """
61 42a999d1 Guido Trotter
  def wrap(fn):
62 42a999d1 Guido Trotter
    def sync_function(*args, **kwargs):
63 dbb11e8b Guido Trotter
      if isinstance(mylock, basestring):
64 dbb11e8b Guido Trotter
        assert args, "cannot ssynchronize on non-class method: self not found"
65 dbb11e8b Guido Trotter
        # args[0] is "self"
66 dbb11e8b Guido Trotter
        lock = getattr(args[0], mylock)
67 dbb11e8b Guido Trotter
      else:
68 dbb11e8b Guido Trotter
        lock = mylock
69 42a999d1 Guido Trotter
      lock.acquire(shared=shared)
70 42a999d1 Guido Trotter
      try:
71 42a999d1 Guido Trotter
        return fn(*args, **kwargs)
72 42a999d1 Guido Trotter
      finally:
73 42a999d1 Guido Trotter
        lock.release()
74 42a999d1 Guido Trotter
    return sync_function
75 42a999d1 Guido Trotter
  return wrap
76 42a999d1 Guido Trotter
77 42a999d1 Guido Trotter
78 34cb5617 Guido Trotter
class _SingleNotifyPipeConditionWaiter(object):
79 34cb5617 Guido Trotter
  """Helper class for SingleNotifyPipeCondition
80 d76167a5 Michael Hanselmann

81 d76167a5 Michael Hanselmann
  """
82 d76167a5 Michael Hanselmann
  __slots__ = [
83 d76167a5 Michael Hanselmann
    "_fd",
84 d76167a5 Michael Hanselmann
    "_poller",
85 d76167a5 Michael Hanselmann
    ]
86 d76167a5 Michael Hanselmann
87 34cb5617 Guido Trotter
  def __init__(self, poller, fd):
88 34cb5617 Guido Trotter
    """Constructor for _SingleNotifyPipeConditionWaiter
89 d76167a5 Michael Hanselmann

90 d76167a5 Michael Hanselmann
    @type poller: select.poll
91 d76167a5 Michael Hanselmann
    @param poller: Poller object
92 d76167a5 Michael Hanselmann
    @type fd: int
93 d76167a5 Michael Hanselmann
    @param fd: File descriptor to wait for
94 d76167a5 Michael Hanselmann

95 d76167a5 Michael Hanselmann
    """
96 d76167a5 Michael Hanselmann
    object.__init__(self)
97 d76167a5 Michael Hanselmann
    self._poller = poller
98 d76167a5 Michael Hanselmann
    self._fd = fd
99 d76167a5 Michael Hanselmann
100 d76167a5 Michael Hanselmann
  def __call__(self, timeout):
101 d76167a5 Michael Hanselmann
    """Wait for something to happen on the pipe.
102 d76167a5 Michael Hanselmann

103 d76167a5 Michael Hanselmann
    @type timeout: float or None
104 d76167a5 Michael Hanselmann
    @param timeout: Timeout for waiting (can be None)
105 d76167a5 Michael Hanselmann

106 d76167a5 Michael Hanselmann
    """
107 557838c1 Renรฉ Nussbaumer
    running_timeout = utils.RunningTimeout(timeout, True)
108 f4e673fb Michael Hanselmann
109 f4e673fb Michael Hanselmann
    while True:
110 f4e673fb Michael Hanselmann
      remaining_time = running_timeout.Remaining()
111 f4e673fb Michael Hanselmann
112 b44b0141 Michael Hanselmann
      if remaining_time is not None:
113 b44b0141 Michael Hanselmann
        if remaining_time < 0.0:
114 b44b0141 Michael Hanselmann
          break
115 d76167a5 Michael Hanselmann
116 413b7472 Michael Hanselmann
        # Our calculation uses seconds, poll() wants milliseconds
117 b44b0141 Michael Hanselmann
        remaining_time *= 1000
118 d76167a5 Michael Hanselmann
119 d76167a5 Michael Hanselmann
      try:
120 d76167a5 Michael Hanselmann
        result = self._poller.poll(remaining_time)
121 d76167a5 Michael Hanselmann
      except EnvironmentError, err:
122 d76167a5 Michael Hanselmann
        if err.errno != errno.EINTR:
123 d76167a5 Michael Hanselmann
          raise
124 d76167a5 Michael Hanselmann
        result = None
125 d76167a5 Michael Hanselmann
126 d76167a5 Michael Hanselmann
      # Check whether we were notified
127 d76167a5 Michael Hanselmann
      if result and result[0][0] == self._fd:
128 d76167a5 Michael Hanselmann
        break
129 d76167a5 Michael Hanselmann
130 d76167a5 Michael Hanselmann
131 2419060d Guido Trotter
class _BaseCondition(object):
132 2419060d Guido Trotter
  """Base class containing common code for conditions.
133 2419060d Guido Trotter

134 2419060d Guido Trotter
  Some of this code is taken from python's threading module.
135 2419060d Guido Trotter

136 2419060d Guido Trotter
  """
137 2419060d Guido Trotter
  __slots__ = [
138 2419060d Guido Trotter
    "_lock",
139 2419060d Guido Trotter
    "acquire",
140 2419060d Guido Trotter
    "release",
141 7f890059 Guido Trotter
    "_is_owned",
142 7f890059 Guido Trotter
    "_acquire_restore",
143 7f890059 Guido Trotter
    "_release_save",
144 2419060d Guido Trotter
    ]
145 2419060d Guido Trotter
146 2419060d Guido Trotter
  def __init__(self, lock):
147 2419060d Guido Trotter
    """Constructor for _BaseCondition.
148 2419060d Guido Trotter

149 69b99987 Michael Hanselmann
    @type lock: threading.Lock
150 2419060d Guido Trotter
    @param lock: condition base lock
151 2419060d Guido Trotter

152 2419060d Guido Trotter
    """
153 2419060d Guido Trotter
    object.__init__(self)
154 2419060d Guido Trotter
155 7f890059 Guido Trotter
    try:
156 7f890059 Guido Trotter
      self._release_save = lock._release_save
157 7f890059 Guido Trotter
    except AttributeError:
158 7f890059 Guido Trotter
      self._release_save = self._base_release_save
159 7f890059 Guido Trotter
    try:
160 7f890059 Guido Trotter
      self._acquire_restore = lock._acquire_restore
161 7f890059 Guido Trotter
    except AttributeError:
162 7f890059 Guido Trotter
      self._acquire_restore = self._base_acquire_restore
163 7f890059 Guido Trotter
    try:
164 ee2b99e3 Michael Hanselmann
      self._is_owned = lock.is_owned
165 7f890059 Guido Trotter
    except AttributeError:
166 7f890059 Guido Trotter
      self._is_owned = self._base_is_owned
167 2419060d Guido Trotter
168 2419060d Guido Trotter
    self._lock = lock
169 2419060d Guido Trotter
170 2419060d Guido Trotter
    # Export the lock's acquire() and release() methods
171 2419060d Guido Trotter
    self.acquire = lock.acquire
172 2419060d Guido Trotter
    self.release = lock.release
173 2419060d Guido Trotter
174 7f890059 Guido Trotter
  def _base_is_owned(self):
175 2419060d Guido Trotter
    """Check whether lock is owned by current thread.
176 2419060d Guido Trotter

177 2419060d Guido Trotter
    """
178 2419060d Guido Trotter
    if self._lock.acquire(0):
179 2419060d Guido Trotter
      self._lock.release()
180 2419060d Guido Trotter
      return False
181 2419060d Guido Trotter
    return True
182 2419060d Guido Trotter
183 7f890059 Guido Trotter
  def _base_release_save(self):
184 7f890059 Guido Trotter
    self._lock.release()
185 7f890059 Guido Trotter
186 7f890059 Guido Trotter
  def _base_acquire_restore(self, _):
187 7f890059 Guido Trotter
    self._lock.acquire()
188 7f890059 Guido Trotter
189 2419060d Guido Trotter
  def _check_owned(self):
190 2419060d Guido Trotter
    """Raise an exception if the current thread doesn't own the lock.
191 2419060d Guido Trotter

192 2419060d Guido Trotter
    """
193 2419060d Guido Trotter
    if not self._is_owned():
194 2419060d Guido Trotter
      raise RuntimeError("cannot work with un-aquired lock")
195 2419060d Guido Trotter
196 2419060d Guido Trotter
197 34cb5617 Guido Trotter
class SingleNotifyPipeCondition(_BaseCondition):
198 34cb5617 Guido Trotter
  """Condition which can only be notified once.
199 d76167a5 Michael Hanselmann

200 34cb5617 Guido Trotter
  This condition class uses pipes and poll, internally, to be able to wait for
201 34cb5617 Guido Trotter
  notification with a timeout, without resorting to polling. It is almost
202 34cb5617 Guido Trotter
  compatible with Python's threading.Condition, with the following differences:
203 34cb5617 Guido Trotter
    - notifyAll can only be called once, and no wait can happen after that
204 34cb5617 Guido Trotter
    - notify is not supported, only notifyAll
205 d76167a5 Michael Hanselmann

206 d76167a5 Michael Hanselmann
  """
207 34cb5617 Guido Trotter
208 154b9580 Balazs Lecz
  __slots__ = [
209 d76167a5 Michael Hanselmann
    "_poller",
210 d76167a5 Michael Hanselmann
    "_read_fd",
211 d76167a5 Michael Hanselmann
    "_write_fd",
212 d76167a5 Michael Hanselmann
    "_nwaiters",
213 34cb5617 Guido Trotter
    "_notified",
214 d76167a5 Michael Hanselmann
    ]
215 d76167a5 Michael Hanselmann
216 34cb5617 Guido Trotter
  _waiter_class = _SingleNotifyPipeConditionWaiter
217 d76167a5 Michael Hanselmann
218 34cb5617 Guido Trotter
  def __init__(self, lock):
219 34cb5617 Guido Trotter
    """Constructor for SingleNotifyPipeCondition
220 d76167a5 Michael Hanselmann

221 d76167a5 Michael Hanselmann
    """
222 34cb5617 Guido Trotter
    _BaseCondition.__init__(self, lock)
223 d76167a5 Michael Hanselmann
    self._nwaiters = 0
224 34cb5617 Guido Trotter
    self._notified = False
225 34cb5617 Guido Trotter
    self._read_fd = None
226 34cb5617 Guido Trotter
    self._write_fd = None
227 34cb5617 Guido Trotter
    self._poller = None
228 d76167a5 Michael Hanselmann
229 34cb5617 Guido Trotter
  def _check_unnotified(self):
230 69b99987 Michael Hanselmann
    """Throws an exception if already notified.
231 69b99987 Michael Hanselmann

232 69b99987 Michael Hanselmann
    """
233 34cb5617 Guido Trotter
    if self._notified:
234 34cb5617 Guido Trotter
      raise RuntimeError("cannot use already notified condition")
235 d76167a5 Michael Hanselmann
236 34cb5617 Guido Trotter
  def _Cleanup(self):
237 34cb5617 Guido Trotter
    """Cleanup open file descriptors, if any.
238 d76167a5 Michael Hanselmann

239 d76167a5 Michael Hanselmann
    """
240 34cb5617 Guido Trotter
    if self._read_fd is not None:
241 34cb5617 Guido Trotter
      os.close(self._read_fd)
242 34cb5617 Guido Trotter
      self._read_fd = None
243 d76167a5 Michael Hanselmann
244 34cb5617 Guido Trotter
    if self._write_fd is not None:
245 34cb5617 Guido Trotter
      os.close(self._write_fd)
246 34cb5617 Guido Trotter
      self._write_fd = None
247 34cb5617 Guido Trotter
    self._poller = None
248 d76167a5 Michael Hanselmann
249 83f2d5f6 Michael Hanselmann
  def wait(self, timeout):
250 34cb5617 Guido Trotter
    """Wait for a notification.
251 d76167a5 Michael Hanselmann

252 34cb5617 Guido Trotter
    @type timeout: float or None
253 34cb5617 Guido Trotter
    @param timeout: Waiting timeout (can be None)
254 d76167a5 Michael Hanselmann

255 d76167a5 Michael Hanselmann
    """
256 34cb5617 Guido Trotter
    self._check_owned()
257 34cb5617 Guido Trotter
    self._check_unnotified()
258 d76167a5 Michael Hanselmann
259 34cb5617 Guido Trotter
    self._nwaiters += 1
260 34cb5617 Guido Trotter
    try:
261 34cb5617 Guido Trotter
      if self._poller is None:
262 34cb5617 Guido Trotter
        (self._read_fd, self._write_fd) = os.pipe()
263 34cb5617 Guido Trotter
        self._poller = select.poll()
264 34cb5617 Guido Trotter
        self._poller.register(self._read_fd, select.POLLHUP)
265 d76167a5 Michael Hanselmann
266 34cb5617 Guido Trotter
      wait_fn = self._waiter_class(self._poller, self._read_fd)
267 7f890059 Guido Trotter
      state = self._release_save()
268 34cb5617 Guido Trotter
      try:
269 34cb5617 Guido Trotter
        # Wait for notification
270 34cb5617 Guido Trotter
        wait_fn(timeout)
271 34cb5617 Guido Trotter
      finally:
272 34cb5617 Guido Trotter
        # Re-acquire lock
273 7f890059 Guido Trotter
        self._acquire_restore(state)
274 34cb5617 Guido Trotter
    finally:
275 34cb5617 Guido Trotter
      self._nwaiters -= 1
276 34cb5617 Guido Trotter
      if self._nwaiters == 0:
277 34cb5617 Guido Trotter
        self._Cleanup()
278 d76167a5 Michael Hanselmann
279 b459a848 Andrea Spadaccini
  def notifyAll(self): # pylint: disable=C0103
280 d76167a5 Michael Hanselmann
    """Close the writing side of the pipe to notify all waiters.
281 d76167a5 Michael Hanselmann

282 d76167a5 Michael Hanselmann
    """
283 34cb5617 Guido Trotter
    self._check_owned()
284 34cb5617 Guido Trotter
    self._check_unnotified()
285 34cb5617 Guido Trotter
    self._notified = True
286 d76167a5 Michael Hanselmann
    if self._write_fd is not None:
287 d76167a5 Michael Hanselmann
      os.close(self._write_fd)
288 d76167a5 Michael Hanselmann
      self._write_fd = None
289 d76167a5 Michael Hanselmann
290 d76167a5 Michael Hanselmann
291 34cb5617 Guido Trotter
class PipeCondition(_BaseCondition):
292 48dabc6a Michael Hanselmann
  """Group-only non-polling condition with counters.
293 48dabc6a Michael Hanselmann

294 48dabc6a Michael Hanselmann
  This condition class uses pipes and poll, internally, to be able to wait for
295 48dabc6a Michael Hanselmann
  notification with a timeout, without resorting to polling. It is almost
296 48dabc6a Michael Hanselmann
  compatible with Python's threading.Condition, but only supports notifyAll and
297 48dabc6a Michael Hanselmann
  non-recursive locks. As an additional features it's able to report whether
298 48dabc6a Michael Hanselmann
  there are any waiting threads.
299 48dabc6a Michael Hanselmann

300 48dabc6a Michael Hanselmann
  """
301 154b9580 Balazs Lecz
  __slots__ = [
302 c31825f7 Michael Hanselmann
    "_waiters",
303 34cb5617 Guido Trotter
    "_single_condition",
304 48dabc6a Michael Hanselmann
    ]
305 48dabc6a Michael Hanselmann
306 34cb5617 Guido Trotter
  _single_condition_class = SingleNotifyPipeCondition
307 48dabc6a Michael Hanselmann
308 48dabc6a Michael Hanselmann
  def __init__(self, lock):
309 48dabc6a Michael Hanselmann
    """Initializes this class.
310 48dabc6a Michael Hanselmann

311 48dabc6a Michael Hanselmann
    """
312 2419060d Guido Trotter
    _BaseCondition.__init__(self, lock)
313 c31825f7 Michael Hanselmann
    self._waiters = set()
314 34cb5617 Guido Trotter
    self._single_condition = self._single_condition_class(self._lock)
315 48dabc6a Michael Hanselmann
316 83f2d5f6 Michael Hanselmann
  def wait(self, timeout):
317 48dabc6a Michael Hanselmann
    """Wait for a notification.
318 48dabc6a Michael Hanselmann

319 48dabc6a Michael Hanselmann
    @type timeout: float or None
320 48dabc6a Michael Hanselmann
    @param timeout: Waiting timeout (can be None)
321 48dabc6a Michael Hanselmann

322 48dabc6a Michael Hanselmann
    """
323 48dabc6a Michael Hanselmann
    self._check_owned()
324 48dabc6a Michael Hanselmann
325 48dabc6a Michael Hanselmann
    # Keep local reference to the pipe. It could be replaced by another thread
326 48dabc6a Michael Hanselmann
    # notifying while we're waiting.
327 c31825f7 Michael Hanselmann
    cond = self._single_condition
328 48dabc6a Michael Hanselmann
329 c31825f7 Michael Hanselmann
    self._waiters.add(threading.currentThread())
330 48dabc6a Michael Hanselmann
    try:
331 c31825f7 Michael Hanselmann
      cond.wait(timeout)
332 48dabc6a Michael Hanselmann
    finally:
333 c31825f7 Michael Hanselmann
      self._check_owned()
334 c31825f7 Michael Hanselmann
      self._waiters.remove(threading.currentThread())
335 48dabc6a Michael Hanselmann
336 b459a848 Andrea Spadaccini
  def notifyAll(self): # pylint: disable=C0103
337 48dabc6a Michael Hanselmann
    """Notify all currently waiting threads.
338 48dabc6a Michael Hanselmann

339 48dabc6a Michael Hanselmann
    """
340 48dabc6a Michael Hanselmann
    self._check_owned()
341 34cb5617 Guido Trotter
    self._single_condition.notifyAll()
342 34cb5617 Guido Trotter
    self._single_condition = self._single_condition_class(self._lock)
343 48dabc6a Michael Hanselmann
344 c31825f7 Michael Hanselmann
  def get_waiting(self):
345 c31825f7 Michael Hanselmann
    """Returns a list of all waiting threads.
346 c31825f7 Michael Hanselmann

347 c31825f7 Michael Hanselmann
    """
348 c31825f7 Michael Hanselmann
    self._check_owned()
349 c31825f7 Michael Hanselmann
350 c31825f7 Michael Hanselmann
    return self._waiters
351 c31825f7 Michael Hanselmann
352 48dabc6a Michael Hanselmann
  def has_waiting(self):
353 48dabc6a Michael Hanselmann
    """Returns whether there are active waiters.
354 48dabc6a Michael Hanselmann

355 48dabc6a Michael Hanselmann
    """
356 48dabc6a Michael Hanselmann
    self._check_owned()
357 48dabc6a Michael Hanselmann
358 c31825f7 Michael Hanselmann
    return bool(self._waiters)
359 48dabc6a Michael Hanselmann
360 20699809 Michael Hanselmann
  def __repr__(self):
361 20699809 Michael Hanselmann
    return ("<%s.%s waiters=%s at %#x>" %
362 20699809 Michael Hanselmann
            (self.__class__.__module__, self.__class__.__name__,
363 20699809 Michael Hanselmann
             self._waiters, id(self)))
364 20699809 Michael Hanselmann
365 48dabc6a Michael Hanselmann
366 887c7aa6 Michael Hanselmann
class _PipeConditionWithMode(PipeCondition):
367 887c7aa6 Michael Hanselmann
  __slots__ = [
368 887c7aa6 Michael Hanselmann
    "shared",
369 887c7aa6 Michael Hanselmann
    ]
370 887c7aa6 Michael Hanselmann
371 887c7aa6 Michael Hanselmann
  def __init__(self, lock, shared):
372 887c7aa6 Michael Hanselmann
    """Initializes this class.
373 887c7aa6 Michael Hanselmann

374 887c7aa6 Michael Hanselmann
    """
375 887c7aa6 Michael Hanselmann
    self.shared = shared
376 887c7aa6 Michael Hanselmann
    PipeCondition.__init__(self, lock)
377 887c7aa6 Michael Hanselmann
378 887c7aa6 Michael Hanselmann
379 84e344d4 Michael Hanselmann
class SharedLock(object):
380 162c1c1f Guido Trotter
  """Implements a shared lock.
381 162c1c1f Guido Trotter

382 05ad571c Michael Hanselmann
  Multiple threads can acquire the lock in a shared way by calling
383 05ad571c Michael Hanselmann
  C{acquire(shared=1)}. In order to acquire the lock in an exclusive way
384 05ad571c Michael Hanselmann
  threads can call C{acquire(shared=0)}.
385 162c1c1f Guido Trotter

386 887c7aa6 Michael Hanselmann
  Notes on data structures: C{__pending} contains a priority queue (heapq) of
387 887c7aa6 Michael Hanselmann
  all pending acquires: C{[(priority1: prioqueue1), (priority2: prioqueue2),
388 887c7aa6 Michael Hanselmann
  ...]}. Each per-priority queue contains a normal in-order list of conditions
389 887c7aa6 Michael Hanselmann
  to be notified when the lock can be acquired. Shared locks are grouped
390 887c7aa6 Michael Hanselmann
  together by priority and the condition for them is stored in
391 887c7aa6 Michael Hanselmann
  C{__pending_shared} if it already exists. C{__pending_by_prio} keeps
392 887c7aa6 Michael Hanselmann
  references for the per-priority queues indexed by priority for faster access.
393 162c1c1f Guido Trotter

394 7f93570a Iustin Pop
  @type name: string
395 7f93570a Iustin Pop
  @ivar name: the name of the lock
396 7f93570a Iustin Pop

397 162c1c1f Guido Trotter
  """
398 84e344d4 Michael Hanselmann
  __slots__ = [
399 19b9ba9a Michael Hanselmann
    "__weakref__",
400 84e344d4 Michael Hanselmann
    "__deleted",
401 84e344d4 Michael Hanselmann
    "__exc",
402 84e344d4 Michael Hanselmann
    "__lock",
403 84e344d4 Michael Hanselmann
    "__pending",
404 887c7aa6 Michael Hanselmann
    "__pending_by_prio",
405 887c7aa6 Michael Hanselmann
    "__pending_shared",
406 84e344d4 Michael Hanselmann
    "__shr",
407 7f93570a Iustin Pop
    "name",
408 84e344d4 Michael Hanselmann
    ]
409 84e344d4 Michael Hanselmann
410 887c7aa6 Michael Hanselmann
  __condition_class = _PipeConditionWithMode
411 84e344d4 Michael Hanselmann
412 19b9ba9a Michael Hanselmann
  def __init__(self, name, monitor=None):
413 84e344d4 Michael Hanselmann
    """Construct a new SharedLock.
414 84e344d4 Michael Hanselmann

415 7f93570a Iustin Pop
    @param name: the name of the lock
416 19b9ba9a Michael Hanselmann
    @type monitor: L{LockMonitor}
417 19b9ba9a Michael Hanselmann
    @param monitor: Lock monitor with which to register
418 7f93570a Iustin Pop

419 84e344d4 Michael Hanselmann
    """
420 84e344d4 Michael Hanselmann
    object.__init__(self)
421 84e344d4 Michael Hanselmann
422 7f93570a Iustin Pop
    self.name = name
423 7f93570a Iustin Pop
424 84e344d4 Michael Hanselmann
    # Internal lock
425 162c1c1f Guido Trotter
    self.__lock = threading.Lock()
426 162c1c1f Guido Trotter
427 84e344d4 Michael Hanselmann
    # Queue containing waiting acquires
428 84e344d4 Michael Hanselmann
    self.__pending = []
429 887c7aa6 Michael Hanselmann
    self.__pending_by_prio = {}
430 887c7aa6 Michael Hanselmann
    self.__pending_shared = {}
431 84e344d4 Michael Hanselmann
432 84e344d4 Michael Hanselmann
    # Current lock holders
433 162c1c1f Guido Trotter
    self.__shr = set()
434 162c1c1f Guido Trotter
    self.__exc = None
435 162c1c1f Guido Trotter
436 a95fd5d7 Guido Trotter
    # is this lock in the deleted state?
437 a95fd5d7 Guido Trotter
    self.__deleted = False
438 a95fd5d7 Guido Trotter
439 19b9ba9a Michael Hanselmann
    # Register with lock monitor
440 19b9ba9a Michael Hanselmann
    if monitor:
441 44b4eddc Michael Hanselmann
      logging.debug("Adding lock %s to monitor", name)
442 19b9ba9a Michael Hanselmann
      monitor.RegisterLock(self)
443 19b9ba9a Michael Hanselmann
444 20699809 Michael Hanselmann
  def __repr__(self):
445 20699809 Michael Hanselmann
    return ("<%s.%s name=%s at %#x>" %
446 20699809 Michael Hanselmann
            (self.__class__.__module__, self.__class__.__name__,
447 20699809 Michael Hanselmann
             self.name, id(self)))
448 20699809 Michael Hanselmann
449 44b4eddc Michael Hanselmann
  def GetLockInfo(self, requested):
450 19b9ba9a Michael Hanselmann
    """Retrieves information for querying locks.
451 19b9ba9a Michael Hanselmann

452 24d16f76 Michael Hanselmann
    @type requested: set
453 24d16f76 Michael Hanselmann
    @param requested: Requested information, see C{query.LQ_*}
454 19b9ba9a Michael Hanselmann

455 19b9ba9a Michael Hanselmann
    """
456 19b9ba9a Michael Hanselmann
    self.__lock.acquire()
457 19b9ba9a Michael Hanselmann
    try:
458 19b9ba9a Michael Hanselmann
      # Note: to avoid unintentional race conditions, no references to
459 19b9ba9a Michael Hanselmann
      # modifiable objects should be returned unless they were created in this
460 19b9ba9a Michael Hanselmann
      # function.
461 24d16f76 Michael Hanselmann
      mode = None
462 24d16f76 Michael Hanselmann
      owner_names = None
463 24d16f76 Michael Hanselmann
464 24d16f76 Michael Hanselmann
      if query.LQ_MODE in requested:
465 24d16f76 Michael Hanselmann
        if self.__deleted:
466 24d16f76 Michael Hanselmann
          mode = _DELETED_TEXT
467 24d16f76 Michael Hanselmann
          assert not (self.__exc or self.__shr)
468 24d16f76 Michael Hanselmann
        elif self.__exc:
469 24d16f76 Michael Hanselmann
          mode = _EXCLUSIVE_TEXT
470 24d16f76 Michael Hanselmann
        elif self.__shr:
471 24d16f76 Michael Hanselmann
          mode = _SHARED_TEXT
472 24d16f76 Michael Hanselmann
473 24d16f76 Michael Hanselmann
      # Current owner(s) are wanted
474 24d16f76 Michael Hanselmann
      if query.LQ_OWNER in requested:
475 24d16f76 Michael Hanselmann
        if self.__exc:
476 24d16f76 Michael Hanselmann
          owner = [self.__exc]
477 19b9ba9a Michael Hanselmann
        else:
478 24d16f76 Michael Hanselmann
          owner = self.__shr
479 24d16f76 Michael Hanselmann
480 24d16f76 Michael Hanselmann
        if owner:
481 24d16f76 Michael Hanselmann
          assert not self.__deleted
482 24d16f76 Michael Hanselmann
          owner_names = [i.getName() for i in owner]
483 19b9ba9a Michael Hanselmann
484 24d16f76 Michael Hanselmann
      # Pending acquires are wanted
485 24d16f76 Michael Hanselmann
      if query.LQ_PENDING in requested:
486 24d16f76 Michael Hanselmann
        pending = []
487 24d16f76 Michael Hanselmann
488 24d16f76 Michael Hanselmann
        # Sorting instead of copying and using heaq functions for simplicity
489 24d16f76 Michael Hanselmann
        for (_, prioqueue) in sorted(self.__pending):
490 24d16f76 Michael Hanselmann
          for cond in prioqueue:
491 24d16f76 Michael Hanselmann
            if cond.shared:
492 24d16f76 Michael Hanselmann
              pendmode = _SHARED_TEXT
493 24d16f76 Michael Hanselmann
            else:
494 24d16f76 Michael Hanselmann
              pendmode = _EXCLUSIVE_TEXT
495 24d16f76 Michael Hanselmann
496 24d16f76 Michael Hanselmann
            # List of names will be sorted in L{query._GetLockPending}
497 24d16f76 Michael Hanselmann
            pending.append((pendmode, [i.getName()
498 24d16f76 Michael Hanselmann
                                       for i in cond.get_waiting()]))
499 24d16f76 Michael Hanselmann
      else:
500 24d16f76 Michael Hanselmann
        pending = None
501 24d16f76 Michael Hanselmann
502 44b4eddc Michael Hanselmann
      return [(self.name, mode, owner_names, pending)]
503 19b9ba9a Michael Hanselmann
    finally:
504 19b9ba9a Michael Hanselmann
      self.__lock.release()
505 19b9ba9a Michael Hanselmann
506 84e344d4 Michael Hanselmann
  def __check_deleted(self):
507 84e344d4 Michael Hanselmann
    """Raises an exception if the lock has been deleted.
508 84e344d4 Michael Hanselmann

509 84e344d4 Michael Hanselmann
    """
510 84e344d4 Michael Hanselmann
    if self.__deleted:
511 7f93570a Iustin Pop
      raise errors.LockError("Deleted lock %s" % self.name)
512 84e344d4 Michael Hanselmann
513 162c1c1f Guido Trotter
  def __is_sharer(self):
514 84e344d4 Michael Hanselmann
    """Is the current thread sharing the lock at this time?
515 84e344d4 Michael Hanselmann

516 84e344d4 Michael Hanselmann
    """
517 162c1c1f Guido Trotter
    return threading.currentThread() in self.__shr
518 162c1c1f Guido Trotter
519 162c1c1f Guido Trotter
  def __is_exclusive(self):
520 84e344d4 Michael Hanselmann
    """Is the current thread holding the lock exclusively at this time?
521 84e344d4 Michael Hanselmann

522 84e344d4 Michael Hanselmann
    """
523 162c1c1f Guido Trotter
    return threading.currentThread() == self.__exc
524 162c1c1f Guido Trotter
525 162c1c1f Guido Trotter
  def __is_owned(self, shared=-1):
526 162c1c1f Guido Trotter
    """Is the current thread somehow owning the lock at this time?
527 162c1c1f Guido Trotter

528 162c1c1f Guido Trotter
    This is a private version of the function, which presumes you're holding
529 162c1c1f Guido Trotter
    the internal lock.
530 162c1c1f Guido Trotter

531 162c1c1f Guido Trotter
    """
532 162c1c1f Guido Trotter
    if shared < 0:
533 162c1c1f Guido Trotter
      return self.__is_sharer() or self.__is_exclusive()
534 162c1c1f Guido Trotter
    elif shared:
535 162c1c1f Guido Trotter
      return self.__is_sharer()
536 162c1c1f Guido Trotter
    else:
537 162c1c1f Guido Trotter
      return self.__is_exclusive()
538 162c1c1f Guido Trotter
539 ee2b99e3 Michael Hanselmann
  def is_owned(self, shared=-1):
540 162c1c1f Guido Trotter
    """Is the current thread somehow owning the lock at this time?
541 162c1c1f Guido Trotter

542 c41eea6e Iustin Pop
    @param shared:
543 c41eea6e Iustin Pop
        - < 0: check for any type of ownership (default)
544 c41eea6e Iustin Pop
        - 0: check for exclusive ownership
545 c41eea6e Iustin Pop
        - > 0: check for shared ownership
546 162c1c1f Guido Trotter

547 162c1c1f Guido Trotter
    """
548 162c1c1f Guido Trotter
    self.__lock.acquire()
549 162c1c1f Guido Trotter
    try:
550 84e344d4 Michael Hanselmann
      return self.__is_owned(shared=shared)
551 162c1c1f Guido Trotter
    finally:
552 162c1c1f Guido Trotter
      self.__lock.release()
553 162c1c1f Guido Trotter
554 ee2b99e3 Michael Hanselmann
  #: Necessary to remain compatible with threading.Condition, which tries to
555 ee2b99e3 Michael Hanselmann
  #: retrieve a locks' "_is_owned" attribute
556 ee2b99e3 Michael Hanselmann
  _is_owned = is_owned
557 fdfe88b1 Michael Hanselmann
558 84e344d4 Michael Hanselmann
  def _count_pending(self):
559 84e344d4 Michael Hanselmann
    """Returns the number of pending acquires.
560 a95fd5d7 Guido Trotter

561 84e344d4 Michael Hanselmann
    @rtype: int
562 a95fd5d7 Guido Trotter

563 a95fd5d7 Guido Trotter
    """
564 84e344d4 Michael Hanselmann
    self.__lock.acquire()
565 84e344d4 Michael Hanselmann
    try:
566 887c7aa6 Michael Hanselmann
      return sum(len(prioqueue) for (_, prioqueue) in self.__pending)
567 887c7aa6 Michael Hanselmann
    finally:
568 887c7aa6 Michael Hanselmann
      self.__lock.release()
569 887c7aa6 Michael Hanselmann
570 887c7aa6 Michael Hanselmann
  def _check_empty(self):
571 887c7aa6 Michael Hanselmann
    """Checks whether there are any pending acquires.
572 887c7aa6 Michael Hanselmann

573 887c7aa6 Michael Hanselmann
    @rtype: bool
574 887c7aa6 Michael Hanselmann

575 887c7aa6 Michael Hanselmann
    """
576 887c7aa6 Michael Hanselmann
    self.__lock.acquire()
577 887c7aa6 Michael Hanselmann
    try:
578 887c7aa6 Michael Hanselmann
      # Order is important: __find_first_pending_queue modifies __pending
579 113359fe Michael Hanselmann
      (_, prioqueue) = self.__find_first_pending_queue()
580 113359fe Michael Hanselmann
581 113359fe Michael Hanselmann
      return not (prioqueue or
582 887c7aa6 Michael Hanselmann
                  self.__pending or
583 887c7aa6 Michael Hanselmann
                  self.__pending_by_prio or
584 887c7aa6 Michael Hanselmann
                  self.__pending_shared)
585 84e344d4 Michael Hanselmann
    finally:
586 84e344d4 Michael Hanselmann
      self.__lock.release()
587 a95fd5d7 Guido Trotter
588 84e344d4 Michael Hanselmann
  def __do_acquire(self, shared):
589 84e344d4 Michael Hanselmann
    """Actually acquire the lock.
590 84e344d4 Michael Hanselmann

591 84e344d4 Michael Hanselmann
    """
592 84e344d4 Michael Hanselmann
    if shared:
593 84e344d4 Michael Hanselmann
      self.__shr.add(threading.currentThread())
594 84e344d4 Michael Hanselmann
    else:
595 84e344d4 Michael Hanselmann
      self.__exc = threading.currentThread()
596 a95fd5d7 Guido Trotter
597 84e344d4 Michael Hanselmann
  def __can_acquire(self, shared):
598 84e344d4 Michael Hanselmann
    """Determine whether lock can be acquired.
599 a95fd5d7 Guido Trotter

600 a95fd5d7 Guido Trotter
    """
601 84e344d4 Michael Hanselmann
    if shared:
602 84e344d4 Michael Hanselmann
      return self.__exc is None
603 84e344d4 Michael Hanselmann
    else:
604 84e344d4 Michael Hanselmann
      return len(self.__shr) == 0 and self.__exc is None
605 a95fd5d7 Guido Trotter
606 887c7aa6 Michael Hanselmann
  def __find_first_pending_queue(self):
607 887c7aa6 Michael Hanselmann
    """Tries to find the topmost queued entry with pending acquires.
608 887c7aa6 Michael Hanselmann

609 887c7aa6 Michael Hanselmann
    Removes empty entries while going through the list.
610 887c7aa6 Michael Hanselmann

611 887c7aa6 Michael Hanselmann
    """
612 887c7aa6 Michael Hanselmann
    while self.__pending:
613 887c7aa6 Michael Hanselmann
      (priority, prioqueue) = self.__pending[0]
614 887c7aa6 Michael Hanselmann
615 887c7aa6 Michael Hanselmann
      if prioqueue:
616 113359fe Michael Hanselmann
        return (priority, prioqueue)
617 887c7aa6 Michael Hanselmann
618 113359fe Michael Hanselmann
      # Remove empty queue
619 113359fe Michael Hanselmann
      heapq.heappop(self.__pending)
620 113359fe Michael Hanselmann
      del self.__pending_by_prio[priority]
621 113359fe Michael Hanselmann
      assert priority not in self.__pending_shared
622 113359fe Michael Hanselmann
623 113359fe Michael Hanselmann
    return (None, None)
624 887c7aa6 Michael Hanselmann
625 84e344d4 Michael Hanselmann
  def __is_on_top(self, cond):
626 84e344d4 Michael Hanselmann
    """Checks whether the passed condition is on top of the queue.
627 a95fd5d7 Guido Trotter

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

630 84e344d4 Michael Hanselmann
    """
631 113359fe Michael Hanselmann
    (_, prioqueue) = self.__find_first_pending_queue()
632 113359fe Michael Hanselmann
633 113359fe Michael Hanselmann
    return cond == prioqueue[0]
634 4d686df8 Guido Trotter
635 887c7aa6 Michael Hanselmann
  def __acquire_unlocked(self, shared, timeout, priority):
636 84e344d4 Michael Hanselmann
    """Acquire a shared lock.
637 9216a9f7 Michael Hanselmann

638 84e344d4 Michael Hanselmann
    @param shared: whether to acquire in shared mode; by default an
639 84e344d4 Michael Hanselmann
        exclusive lock will be acquired
640 84e344d4 Michael Hanselmann
    @param timeout: maximum waiting time before giving up
641 887c7aa6 Michael Hanselmann
    @type priority: integer
642 887c7aa6 Michael Hanselmann
    @param priority: Priority for acquiring lock
643 9216a9f7 Michael Hanselmann

644 9216a9f7 Michael Hanselmann
    """
645 84e344d4 Michael Hanselmann
    self.__check_deleted()
646 9216a9f7 Michael Hanselmann
647 84e344d4 Michael Hanselmann
    # We cannot acquire the lock if we already have it
648 7f93570a Iustin Pop
    assert not self.__is_owned(), ("double acquire() on a non-recursive lock"
649 7f93570a Iustin Pop
                                   " %s" % self.name)
650 84e344d4 Michael Hanselmann
651 887c7aa6 Michael Hanselmann
    # Remove empty entries from queue
652 887c7aa6 Michael Hanselmann
    self.__find_first_pending_queue()
653 887c7aa6 Michael Hanselmann
654 84e344d4 Michael Hanselmann
    # Check whether someone else holds the lock or there are pending acquires.
655 84e344d4 Michael Hanselmann
    if not self.__pending and self.__can_acquire(shared):
656 84e344d4 Michael Hanselmann
      # Apparently not, can acquire lock directly.
657 84e344d4 Michael Hanselmann
      self.__do_acquire(shared)
658 84e344d4 Michael Hanselmann
      return True
659 9216a9f7 Michael Hanselmann
660 887c7aa6 Michael Hanselmann
    prioqueue = self.__pending_by_prio.get(priority, None)
661 9216a9f7 Michael Hanselmann
662 887c7aa6 Michael Hanselmann
    if shared:
663 887c7aa6 Michael Hanselmann
      # Try to re-use condition for shared acquire
664 887c7aa6 Michael Hanselmann
      wait_condition = self.__pending_shared.get(priority, None)
665 887c7aa6 Michael Hanselmann
      assert (wait_condition is None or
666 887c7aa6 Michael Hanselmann
              (wait_condition.shared and wait_condition in prioqueue))
667 84e344d4 Michael Hanselmann
    else:
668 887c7aa6 Michael Hanselmann
      wait_condition = None
669 887c7aa6 Michael Hanselmann
670 887c7aa6 Michael Hanselmann
    if wait_condition is None:
671 887c7aa6 Michael Hanselmann
      if prioqueue is None:
672 887c7aa6 Michael Hanselmann
        assert priority not in self.__pending_by_prio
673 887c7aa6 Michael Hanselmann
674 887c7aa6 Michael Hanselmann
        prioqueue = []
675 887c7aa6 Michael Hanselmann
        heapq.heappush(self.__pending, (priority, prioqueue))
676 887c7aa6 Michael Hanselmann
        self.__pending_by_prio[priority] = prioqueue
677 887c7aa6 Michael Hanselmann
678 887c7aa6 Michael Hanselmann
      wait_condition = self.__condition_class(self.__lock, shared)
679 887c7aa6 Michael Hanselmann
      prioqueue.append(wait_condition)
680 887c7aa6 Michael Hanselmann
681 887c7aa6 Michael Hanselmann
      if shared:
682 887c7aa6 Michael Hanselmann
        # Keep reference for further shared acquires on same priority. This is
683 887c7aa6 Michael Hanselmann
        # better than trying to find it in the list of pending acquires.
684 887c7aa6 Michael Hanselmann
        assert priority not in self.__pending_shared
685 887c7aa6 Michael Hanselmann
        self.__pending_shared[priority] = wait_condition
686 84e344d4 Michael Hanselmann
687 84e344d4 Michael Hanselmann
    try:
688 84e344d4 Michael Hanselmann
      # Wait until we become the topmost acquire in the queue or the timeout
689 84e344d4 Michael Hanselmann
      # expires.
690 887c7aa6 Michael Hanselmann
      # TODO: Decrease timeout with spurious notifications
691 84e344d4 Michael Hanselmann
      while not (self.__is_on_top(wait_condition) and
692 84e344d4 Michael Hanselmann
                 self.__can_acquire(shared)):
693 84e344d4 Michael Hanselmann
        # Wait for notification
694 84e344d4 Michael Hanselmann
        wait_condition.wait(timeout)
695 84e344d4 Michael Hanselmann
        self.__check_deleted()
696 84e344d4 Michael Hanselmann
697 84e344d4 Michael Hanselmann
        # A lot of code assumes blocking acquires always succeed. Loop
698 84e344d4 Michael Hanselmann
        # internally for that case.
699 84e344d4 Michael Hanselmann
        if timeout is not None:
700 84e344d4 Michael Hanselmann
          break
701 84e344d4 Michael Hanselmann
702 84e344d4 Michael Hanselmann
      if self.__is_on_top(wait_condition) and self.__can_acquire(shared):
703 84e344d4 Michael Hanselmann
        self.__do_acquire(shared)
704 84e344d4 Michael Hanselmann
        return True
705 9216a9f7 Michael Hanselmann
    finally:
706 84e344d4 Michael Hanselmann
      # Remove condition from queue if there are no more waiters
707 887c7aa6 Michael Hanselmann
      if not wait_condition.has_waiting():
708 887c7aa6 Michael Hanselmann
        prioqueue.remove(wait_condition)
709 887c7aa6 Michael Hanselmann
        if wait_condition.shared:
710 113359fe Michael Hanselmann
          # Remove from list of shared acquires if it wasn't while releasing
711 113359fe Michael Hanselmann
          # (e.g. on lock deletion)
712 113359fe Michael Hanselmann
          self.__pending_shared.pop(priority, None)
713 9216a9f7 Michael Hanselmann
714 84e344d4 Michael Hanselmann
    return False
715 9216a9f7 Michael Hanselmann
716 7100c2fa Michael Hanselmann
  def acquire(self, shared=0, timeout=None, priority=None,
717 887c7aa6 Michael Hanselmann
              test_notify=None):
718 162c1c1f Guido Trotter
    """Acquire a shared lock.
719 162c1c1f Guido Trotter

720 ec44d893 Guido Trotter
    @type shared: integer (0/1) used as a boolean
721 c41eea6e Iustin Pop
    @param shared: whether to acquire in shared mode; by default an
722 c41eea6e Iustin Pop
        exclusive lock will be acquired
723 84e344d4 Michael Hanselmann
    @type timeout: float
724 84e344d4 Michael Hanselmann
    @param timeout: maximum waiting time before giving up
725 887c7aa6 Michael Hanselmann
    @type priority: integer
726 887c7aa6 Michael Hanselmann
    @param priority: Priority for acquiring lock
727 008b92fa Michael Hanselmann
    @type test_notify: callable or None
728 008b92fa Michael Hanselmann
    @param test_notify: Special callback function for unittesting
729 162c1c1f Guido Trotter

730 162c1c1f Guido Trotter
    """
731 7100c2fa Michael Hanselmann
    if priority is None:
732 7100c2fa Michael Hanselmann
      priority = _DEFAULT_PRIORITY
733 7100c2fa Michael Hanselmann
734 162c1c1f Guido Trotter
    self.__lock.acquire()
735 162c1c1f Guido Trotter
    try:
736 008b92fa Michael Hanselmann
      # We already got the lock, notify now
737 008b92fa Michael Hanselmann
      if __debug__ and callable(test_notify):
738 008b92fa Michael Hanselmann
        test_notify()
739 008b92fa Michael Hanselmann
740 887c7aa6 Michael Hanselmann
      return self.__acquire_unlocked(shared, timeout, priority)
741 162c1c1f Guido Trotter
    finally:
742 162c1c1f Guido Trotter
      self.__lock.release()
743 162c1c1f Guido Trotter
744 3dbe3ddf Michael Hanselmann
  def downgrade(self):
745 3dbe3ddf Michael Hanselmann
    """Changes the lock mode from exclusive to shared.
746 3dbe3ddf Michael Hanselmann

747 3dbe3ddf Michael Hanselmann
    Pending acquires in shared mode on the same priority will go ahead.
748 3dbe3ddf Michael Hanselmann

749 3dbe3ddf Michael Hanselmann
    """
750 3dbe3ddf Michael Hanselmann
    self.__lock.acquire()
751 3dbe3ddf Michael Hanselmann
    try:
752 3dbe3ddf Michael Hanselmann
      assert self.__is_owned(), "Lock must be owned"
753 3dbe3ddf Michael Hanselmann
754 3dbe3ddf Michael Hanselmann
      if self.__is_exclusive():
755 3dbe3ddf Michael Hanselmann
        # Do nothing if the lock is already acquired in shared mode
756 3dbe3ddf Michael Hanselmann
        self.__exc = None
757 3dbe3ddf Michael Hanselmann
        self.__do_acquire(1)
758 3dbe3ddf Michael Hanselmann
759 3dbe3ddf Michael Hanselmann
        # Important: pending shared acquires should only jump ahead if there
760 3dbe3ddf Michael Hanselmann
        # was a transition from exclusive to shared, otherwise an owner of a
761 3dbe3ddf Michael Hanselmann
        # shared lock can keep calling this function to push incoming shared
762 3dbe3ddf Michael Hanselmann
        # acquires
763 3dbe3ddf Michael Hanselmann
        (priority, prioqueue) = self.__find_first_pending_queue()
764 3dbe3ddf Michael Hanselmann
        if prioqueue:
765 3dbe3ddf Michael Hanselmann
          # Is there a pending shared acquire on this priority?
766 3dbe3ddf Michael Hanselmann
          cond = self.__pending_shared.pop(priority, None)
767 3dbe3ddf Michael Hanselmann
          if cond:
768 3dbe3ddf Michael Hanselmann
            assert cond.shared
769 3dbe3ddf Michael Hanselmann
            assert cond in prioqueue
770 3dbe3ddf Michael Hanselmann
771 3dbe3ddf Michael Hanselmann
            # Ensure shared acquire is on top of queue
772 3dbe3ddf Michael Hanselmann
            if len(prioqueue) > 1:
773 3dbe3ddf Michael Hanselmann
              prioqueue.remove(cond)
774 3dbe3ddf Michael Hanselmann
              prioqueue.insert(0, cond)
775 3dbe3ddf Michael Hanselmann
776 3dbe3ddf Michael Hanselmann
            # Notify
777 3dbe3ddf Michael Hanselmann
            cond.notifyAll()
778 3dbe3ddf Michael Hanselmann
779 3dbe3ddf Michael Hanselmann
      assert not self.__is_exclusive()
780 3dbe3ddf Michael Hanselmann
      assert self.__is_sharer()
781 3dbe3ddf Michael Hanselmann
782 3dbe3ddf Michael Hanselmann
      return True
783 3dbe3ddf Michael Hanselmann
    finally:
784 3dbe3ddf Michael Hanselmann
      self.__lock.release()
785 3dbe3ddf Michael Hanselmann
786 162c1c1f Guido Trotter
  def release(self):
787 162c1c1f Guido Trotter
    """Release a Shared Lock.
788 162c1c1f Guido Trotter

789 162c1c1f Guido Trotter
    You must have acquired the lock, either in shared or in exclusive mode,
790 162c1c1f Guido Trotter
    before calling this function.
791 162c1c1f Guido Trotter

792 162c1c1f Guido Trotter
    """
793 162c1c1f Guido Trotter
    self.__lock.acquire()
794 162c1c1f Guido Trotter
    try:
795 84e344d4 Michael Hanselmann
      assert self.__is_exclusive() or self.__is_sharer(), \
796 84e344d4 Michael Hanselmann
        "Cannot release non-owned lock"
797 84e344d4 Michael Hanselmann
798 162c1c1f Guido Trotter
      # Autodetect release type
799 162c1c1f Guido Trotter
      if self.__is_exclusive():
800 162c1c1f Guido Trotter
        self.__exc = None
801 84e344d4 Michael Hanselmann
      else:
802 162c1c1f Guido Trotter
        self.__shr.remove(threading.currentThread())
803 162c1c1f Guido Trotter
804 84e344d4 Michael Hanselmann
      # Notify topmost condition in queue
805 113359fe Michael Hanselmann
      (priority, prioqueue) = self.__find_first_pending_queue()
806 887c7aa6 Michael Hanselmann
      if prioqueue:
807 113359fe Michael Hanselmann
        cond = prioqueue[0]
808 113359fe Michael Hanselmann
        cond.notifyAll()
809 113359fe Michael Hanselmann
        if cond.shared:
810 113359fe Michael Hanselmann
          # Prevent further shared acquires from sneaking in while waiters are
811 113359fe Michael Hanselmann
          # notified
812 113359fe Michael Hanselmann
          self.__pending_shared.pop(priority, None)
813 162c1c1f Guido Trotter
814 162c1c1f Guido Trotter
    finally:
815 162c1c1f Guido Trotter
      self.__lock.release()
816 162c1c1f Guido Trotter
817 7100c2fa Michael Hanselmann
  def delete(self, timeout=None, priority=None):
818 a95fd5d7 Guido Trotter
    """Delete a Shared Lock.
819 a95fd5d7 Guido Trotter

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

824 84e344d4 Michael Hanselmann
    @type timeout: float
825 84e344d4 Michael Hanselmann
    @param timeout: maximum waiting time before giving up
826 887c7aa6 Michael Hanselmann
    @type priority: integer
827 887c7aa6 Michael Hanselmann
    @param priority: Priority for acquiring lock
828 a95fd5d7 Guido Trotter

829 a95fd5d7 Guido Trotter
    """
830 7100c2fa Michael Hanselmann
    if priority is None:
831 7100c2fa Michael Hanselmann
      priority = _DEFAULT_PRIORITY
832 7100c2fa Michael Hanselmann
833 a95fd5d7 Guido Trotter
    self.__lock.acquire()
834 a95fd5d7 Guido Trotter
    try:
835 84e344d4 Michael Hanselmann
      assert not self.__is_sharer(), "Cannot delete() a lock while sharing it"
836 84e344d4 Michael Hanselmann
837 84e344d4 Michael Hanselmann
      self.__check_deleted()
838 a95fd5d7 Guido Trotter
839 84e344d4 Michael Hanselmann
      # The caller is allowed to hold the lock exclusively already.
840 84e344d4 Michael Hanselmann
      acquired = self.__is_exclusive()
841 a95fd5d7 Guido Trotter
842 84e344d4 Michael Hanselmann
      if not acquired:
843 887c7aa6 Michael Hanselmann
        acquired = self.__acquire_unlocked(0, timeout, priority)
844 a66bd91b Michael Hanselmann
845 a66bd91b Michael Hanselmann
        assert self.__is_exclusive() and not self.__is_sharer(), \
846 a66bd91b Michael Hanselmann
          "Lock wasn't acquired in exclusive mode"
847 84e344d4 Michael Hanselmann
848 84e344d4 Michael Hanselmann
      if acquired:
849 84e344d4 Michael Hanselmann
        self.__deleted = True
850 84e344d4 Michael Hanselmann
        self.__exc = None
851 a95fd5d7 Guido Trotter
852 19b9ba9a Michael Hanselmann
        assert not (self.__exc or self.__shr), "Found owner during deletion"
853 19b9ba9a Michael Hanselmann
854 84e344d4 Michael Hanselmann
        # Notify all acquires. They'll throw an error.
855 887c7aa6 Michael Hanselmann
        for (_, prioqueue) in self.__pending:
856 887c7aa6 Michael Hanselmann
          for cond in prioqueue:
857 887c7aa6 Michael Hanselmann
            cond.notifyAll()
858 887c7aa6 Michael Hanselmann
859 887c7aa6 Michael Hanselmann
        assert self.__deleted
860 a95fd5d7 Guido Trotter
861 84e344d4 Michael Hanselmann
      return acquired
862 a95fd5d7 Guido Trotter
    finally:
863 a95fd5d7 Guido Trotter
      self.__lock.release()
864 a95fd5d7 Guido Trotter
865 1a4e32d0 Guido Trotter
  def _release_save(self):
866 1a4e32d0 Guido Trotter
    shared = self.__is_sharer()
867 1a4e32d0 Guido Trotter
    self.release()
868 1a4e32d0 Guido Trotter
    return shared
869 1a4e32d0 Guido Trotter
870 1a4e32d0 Guido Trotter
  def _acquire_restore(self, shared):
871 1a4e32d0 Guido Trotter
    self.acquire(shared=shared)
872 1a4e32d0 Guido Trotter
873 aaae9bc0 Guido Trotter
874 f12eadb3 Iustin Pop
# Whenever we want to acquire a full LockSet we pass None as the value
875 5bbd3f7f Michael Hanselmann
# to acquire.  Hide this behind this nicely named constant.
876 e310b019 Guido Trotter
ALL_SET = None
877 e310b019 Guido Trotter
878 e310b019 Guido Trotter
879 5aab242c Michael Hanselmann
class _AcquireTimeout(Exception):
880 5aab242c Michael Hanselmann
  """Internal exception to abort an acquire on a timeout.
881 5aab242c Michael Hanselmann

882 5aab242c Michael Hanselmann
  """
883 5aab242c Michael Hanselmann
884 5aab242c Michael Hanselmann
885 aaae9bc0 Guido Trotter
class LockSet:
886 aaae9bc0 Guido Trotter
  """Implements a set of locks.
887 aaae9bc0 Guido Trotter

888 aaae9bc0 Guido Trotter
  This abstraction implements a set of shared locks for the same resource type,
889 aaae9bc0 Guido Trotter
  distinguished by name. The user can lock a subset of the resources and the
890 aaae9bc0 Guido Trotter
  LockSet will take care of acquiring the locks always in the same order, thus
891 aaae9bc0 Guido Trotter
  preventing deadlock.
892 aaae9bc0 Guido Trotter

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

895 7f93570a Iustin Pop
  @type name: string
896 7f93570a Iustin Pop
  @ivar name: the name of the lockset
897 7f93570a Iustin Pop

898 aaae9bc0 Guido Trotter
  """
899 19b9ba9a Michael Hanselmann
  def __init__(self, members, name, monitor=None):
900 aaae9bc0 Guido Trotter
    """Constructs a new LockSet.
901 aaae9bc0 Guido Trotter

902 ec44d893 Guido Trotter
    @type members: list of strings
903 c41eea6e Iustin Pop
    @param members: initial members of the set
904 19b9ba9a Michael Hanselmann
    @type monitor: L{LockMonitor}
905 19b9ba9a Michael Hanselmann
    @param monitor: Lock monitor with which to register member locks
906 aaae9bc0 Guido Trotter

907 aaae9bc0 Guido Trotter
    """
908 7f93570a Iustin Pop
    assert members is not None, "members parameter is not a list"
909 7f93570a Iustin Pop
    self.name = name
910 7f93570a Iustin Pop
911 19b9ba9a Michael Hanselmann
    # Lock monitor
912 19b9ba9a Michael Hanselmann
    self.__monitor = monitor
913 19b9ba9a Michael Hanselmann
914 c307ee34 Michael Hanselmann
    # Used internally to guarantee coherency
915 c307ee34 Michael Hanselmann
    self.__lock = SharedLock(self._GetLockName("[lockset]"), monitor=monitor)
916 aaae9bc0 Guido Trotter
917 aaae9bc0 Guido Trotter
    # The lockdict indexes the relationship name -> lock
918 aaae9bc0 Guido Trotter
    # The order-of-locking is implied by the alphabetical order of names
919 aaae9bc0 Guido Trotter
    self.__lockdict = {}
920 aaae9bc0 Guido Trotter
921 7f93570a Iustin Pop
    for mname in members:
922 19b9ba9a Michael Hanselmann
      self.__lockdict[mname] = SharedLock(self._GetLockName(mname),
923 19b9ba9a Michael Hanselmann
                                          monitor=monitor)
924 aaae9bc0 Guido Trotter
925 aaae9bc0 Guido Trotter
    # The owner dict contains the set of locks each thread owns. For
926 aaae9bc0 Guido Trotter
    # performance each thread can access its own key without a global lock on
927 aaae9bc0 Guido Trotter
    # this structure. It is paramount though that *no* other type of access is
928 aaae9bc0 Guido Trotter
    # done to this structure (eg. no looping over its keys). *_owner helper
929 aaae9bc0 Guido Trotter
    # function are defined to guarantee access is correct, but in general never
930 aaae9bc0 Guido Trotter
    # do anything different than __owners[threading.currentThread()], or there
931 aaae9bc0 Guido Trotter
    # will be trouble.
932 aaae9bc0 Guido Trotter
    self.__owners = {}
933 aaae9bc0 Guido Trotter
934 4fb780d1 Michael Hanselmann
  def _GetLockName(self, mname):
935 4fb780d1 Michael Hanselmann
    """Returns the name for a member lock.
936 4fb780d1 Michael Hanselmann

937 4fb780d1 Michael Hanselmann
    """
938 4fb780d1 Michael Hanselmann
    return "%s/%s" % (self.name, mname)
939 4fb780d1 Michael Hanselmann
940 3dbe3ddf Michael Hanselmann
  def _get_lock(self):
941 3dbe3ddf Michael Hanselmann
    """Returns the lockset-internal lock.
942 3dbe3ddf Michael Hanselmann

943 3dbe3ddf Michael Hanselmann
    """
944 3dbe3ddf Michael Hanselmann
    return self.__lock
945 3dbe3ddf Michael Hanselmann
946 3dbe3ddf Michael Hanselmann
  def _get_lockdict(self):
947 3dbe3ddf Michael Hanselmann
    """Returns the lockset-internal lock dictionary.
948 3dbe3ddf Michael Hanselmann

949 3dbe3ddf Michael Hanselmann
    Accessing this structure is only safe in single-thread usage or when the
950 3dbe3ddf Michael Hanselmann
    lockset-internal lock is held.
951 3dbe3ddf Michael Hanselmann

952 3dbe3ddf Michael Hanselmann
    """
953 3dbe3ddf Michael Hanselmann
    return self.__lockdict
954 3dbe3ddf Michael Hanselmann
955 ee2b99e3 Michael Hanselmann
  def is_owned(self):
956 c6a622cf Michael Hanselmann
    """Is the current thread a current level owner?
957 c6a622cf Michael Hanselmann

958 c6a622cf Michael Hanselmann
    @note: Use L{check_owned} to check if a specific lock is held
959 c6a622cf Michael Hanselmann

960 c6a622cf Michael Hanselmann
    """
961 aaae9bc0 Guido Trotter
    return threading.currentThread() in self.__owners
962 aaae9bc0 Guido Trotter
963 c6a622cf Michael Hanselmann
  def check_owned(self, names, shared=-1):
964 c6a622cf Michael Hanselmann
    """Check if locks are owned in a specific mode.
965 c6a622cf Michael Hanselmann

966 c6a622cf Michael Hanselmann
    @type names: sequence or string
967 c6a622cf Michael Hanselmann
    @param names: Lock names (or a single lock name)
968 c6a622cf Michael Hanselmann
    @param shared: See L{SharedLock.is_owned}
969 c6a622cf Michael Hanselmann
    @rtype: bool
970 c6a622cf Michael Hanselmann
    @note: Use L{is_owned} to check if the current thread holds I{any} lock and
971 c6a622cf Michael Hanselmann
      L{list_owned} to get the names of all owned locks
972 c6a622cf Michael Hanselmann

973 c6a622cf Michael Hanselmann
    """
974 c6a622cf Michael Hanselmann
    if isinstance(names, basestring):
975 c6a622cf Michael Hanselmann
      names = [names]
976 c6a622cf Michael Hanselmann
977 c6a622cf Michael Hanselmann
    # Avoid check if no locks are owned anyway
978 c6a622cf Michael Hanselmann
    if names and self.is_owned():
979 c6a622cf Michael Hanselmann
      candidates = []
980 c6a622cf Michael Hanselmann
981 c6a622cf Michael Hanselmann
      # Gather references to all locks (in case they're deleted in the meantime)
982 c6a622cf Michael Hanselmann
      for lname in names:
983 c6a622cf Michael Hanselmann
        try:
984 c6a622cf Michael Hanselmann
          lock = self.__lockdict[lname]
985 c6a622cf Michael Hanselmann
        except KeyError:
986 c6a622cf Michael Hanselmann
          raise errors.LockError("Non-existing lock '%s' in set '%s' (it may"
987 c6a622cf Michael Hanselmann
                                 " have been removed)" % (lname, self.name))
988 c6a622cf Michael Hanselmann
        else:
989 c6a622cf Michael Hanselmann
          candidates.append(lock)
990 c6a622cf Michael Hanselmann
991 c6a622cf Michael Hanselmann
      return compat.all(lock.is_owned(shared=shared) for lock in candidates)
992 c6a622cf Michael Hanselmann
    else:
993 c6a622cf Michael Hanselmann
      return False
994 c6a622cf Michael Hanselmann
995 b2dabfd6 Guido Trotter
  def _add_owned(self, name=None):
996 aaae9bc0 Guido Trotter
    """Note the current thread owns the given lock"""
997 b2dabfd6 Guido Trotter
    if name is None:
998 ee2b99e3 Michael Hanselmann
      if not self.is_owned():
999 b2dabfd6 Guido Trotter
        self.__owners[threading.currentThread()] = set()
1000 aaae9bc0 Guido Trotter
    else:
1001 ee2b99e3 Michael Hanselmann
      if self.is_owned():
1002 b2dabfd6 Guido Trotter
        self.__owners[threading.currentThread()].add(name)
1003 b2dabfd6 Guido Trotter
      else:
1004 b2dabfd6 Guido Trotter
        self.__owners[threading.currentThread()] = set([name])
1005 b2dabfd6 Guido Trotter
1006 b2dabfd6 Guido Trotter
  def _del_owned(self, name=None):
1007 aaae9bc0 Guido Trotter
    """Note the current thread owns the given lock"""
1008 aaae9bc0 Guido Trotter
1009 ee2b99e3 Michael Hanselmann
    assert not (name is None and self.__lock.is_owned()), \
1010 e4335b5b Michael Hanselmann
           "Cannot hold internal lock when deleting owner status"
1011 e4335b5b Michael Hanselmann
1012 b2dabfd6 Guido Trotter
    if name is not None:
1013 b2dabfd6 Guido Trotter
      self.__owners[threading.currentThread()].remove(name)
1014 b2dabfd6 Guido Trotter
1015 b2dabfd6 Guido Trotter
    # Only remove the key if we don't hold the set-lock as well
1016 ee2b99e3 Michael Hanselmann
    if (not self.__lock.is_owned() and
1017 b2dabfd6 Guido Trotter
        not self.__owners[threading.currentThread()]):
1018 aaae9bc0 Guido Trotter
      del self.__owners[threading.currentThread()]
1019 aaae9bc0 Guido Trotter
1020 ee2b99e3 Michael Hanselmann
  def list_owned(self):
1021 aaae9bc0 Guido Trotter
    """Get the set of resource names owned by the current thread"""
1022 ee2b99e3 Michael Hanselmann
    if self.is_owned():
1023 aaae9bc0 Guido Trotter
      return self.__owners[threading.currentThread()].copy()
1024 aaae9bc0 Guido Trotter
    else:
1025 aaae9bc0 Guido Trotter
      return set()
1026 aaae9bc0 Guido Trotter
1027 5aab242c Michael Hanselmann
  def _release_and_delete_owned(self):
1028 5aab242c Michael Hanselmann
    """Release and delete all resources owned by the current thread"""
1029 ee2b99e3 Michael Hanselmann
    for lname in self.list_owned():
1030 56452af7 Michael Hanselmann
      lock = self.__lockdict[lname]
1031 ee2b99e3 Michael Hanselmann
      if lock.is_owned():
1032 56452af7 Michael Hanselmann
        lock.release()
1033 5aab242c Michael Hanselmann
      self._del_owned(name=lname)
1034 5aab242c Michael Hanselmann
1035 aaae9bc0 Guido Trotter
  def __names(self):
1036 aaae9bc0 Guido Trotter
    """Return the current set of names.
1037 aaae9bc0 Guido Trotter

1038 aaae9bc0 Guido Trotter
    Only call this function while holding __lock and don't iterate on the
1039 aaae9bc0 Guido Trotter
    result after releasing the lock.
1040 aaae9bc0 Guido Trotter

1041 aaae9bc0 Guido Trotter
    """
1042 0cf257c5 Guido Trotter
    return self.__lockdict.keys()
1043 aaae9bc0 Guido Trotter
1044 aaae9bc0 Guido Trotter
  def _names(self):
1045 aaae9bc0 Guido Trotter
    """Return a copy of the current set of elements.
1046 aaae9bc0 Guido Trotter

1047 aaae9bc0 Guido Trotter
    Used only for debugging purposes.
1048 cdb08f44 Michael Hanselmann

1049 aaae9bc0 Guido Trotter
    """
1050 d4803c24 Guido Trotter
    # If we don't already own the set-level lock acquired
1051 d4803c24 Guido Trotter
    # we'll get it and note we need to release it later.
1052 d4803c24 Guido Trotter
    release_lock = False
1053 ee2b99e3 Michael Hanselmann
    if not self.__lock.is_owned():
1054 d4803c24 Guido Trotter
      release_lock = True
1055 d4803c24 Guido Trotter
      self.__lock.acquire(shared=1)
1056 aaae9bc0 Guido Trotter
    try:
1057 aaae9bc0 Guido Trotter
      result = self.__names()
1058 aaae9bc0 Guido Trotter
    finally:
1059 d4803c24 Guido Trotter
      if release_lock:
1060 d4803c24 Guido Trotter
        self.__lock.release()
1061 0cf257c5 Guido Trotter
    return set(result)
1062 aaae9bc0 Guido Trotter
1063 7100c2fa Michael Hanselmann
  def acquire(self, names, timeout=None, shared=0, priority=None,
1064 887c7aa6 Michael Hanselmann
              test_notify=None):
1065 aaae9bc0 Guido Trotter
    """Acquire a set of resource locks.
1066 aaae9bc0 Guido Trotter

1067 ec44d893 Guido Trotter
    @type names: list of strings (or string)
1068 c41eea6e Iustin Pop
    @param names: the names of the locks which shall be acquired
1069 c41eea6e Iustin Pop
        (special lock names, or instance/node names)
1070 ec44d893 Guido Trotter
    @type shared: integer (0/1) used as a boolean
1071 c41eea6e Iustin Pop
    @param shared: whether to acquire in shared mode; by default an
1072 c41eea6e Iustin Pop
        exclusive lock will be acquired
1073 5aab242c Michael Hanselmann
    @type timeout: float or None
1074 5e0a6daf Michael Hanselmann
    @param timeout: Maximum time to acquire all locks
1075 887c7aa6 Michael Hanselmann
    @type priority: integer
1076 887c7aa6 Michael Hanselmann
    @param priority: Priority for acquiring locks
1077 5aab242c Michael Hanselmann
    @type test_notify: callable or None
1078 5aab242c Michael Hanselmann
    @param test_notify: Special callback function for unittesting
1079 aaae9bc0 Guido Trotter

1080 5aab242c Michael Hanselmann
    @return: Set of all locks successfully acquired or None in case of timeout
1081 aaae9bc0 Guido Trotter

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

1086 aaae9bc0 Guido Trotter
    """
1087 5aab242c Michael Hanselmann
    assert timeout is None or timeout >= 0.0
1088 aaae9bc0 Guido Trotter
1089 aaae9bc0 Guido Trotter
    # Check we don't already own locks at this level
1090 ee2b99e3 Michael Hanselmann
    assert not self.is_owned(), ("Cannot acquire locks in the same set twice"
1091 ee2b99e3 Michael Hanselmann
                                 " (lockset %s)" % self.name)
1092 aaae9bc0 Guido Trotter
1093 7100c2fa Michael Hanselmann
    if priority is None:
1094 7100c2fa Michael Hanselmann
      priority = _DEFAULT_PRIORITY
1095 7100c2fa Michael Hanselmann
1096 5aab242c Michael Hanselmann
    # We need to keep track of how long we spent waiting for a lock. The
1097 5aab242c Michael Hanselmann
    # timeout passed to this function is over all lock acquires.
1098 557838c1 Renรฉ Nussbaumer
    running_timeout = utils.RunningTimeout(timeout, False)
1099 5aab242c Michael Hanselmann
1100 806e20fd Guido Trotter
    try:
1101 76e2f08a Michael Hanselmann
      if names is not None:
1102 5aab242c Michael Hanselmann
        # Support passing in a single resource to acquire rather than many
1103 5aab242c Michael Hanselmann
        if isinstance(names, basestring):
1104 5aab242c Michael Hanselmann
          names = [names]
1105 5aab242c Michael Hanselmann
1106 887c7aa6 Michael Hanselmann
        return self.__acquire_inner(names, False, shared, priority,
1107 7e8841bd Michael Hanselmann
                                    running_timeout.Remaining, test_notify)
1108 76e2f08a Michael Hanselmann
1109 76e2f08a Michael Hanselmann
      else:
1110 76e2f08a Michael Hanselmann
        # If no names are given acquire the whole set by not letting new names
1111 76e2f08a Michael Hanselmann
        # being added before we release, and getting the current list of names.
1112 76e2f08a Michael Hanselmann
        # Some of them may then be deleted later, but we'll cope with this.
1113 76e2f08a Michael Hanselmann
        #
1114 76e2f08a Michael Hanselmann
        # We'd like to acquire this lock in a shared way, as it's nice if
1115 887c7aa6 Michael Hanselmann
        # everybody else can use the instances at the same time. If we are
1116 76e2f08a Michael Hanselmann
        # acquiring them exclusively though they won't be able to do this
1117 76e2f08a Michael Hanselmann
        # anyway, though, so we'll get the list lock exclusively as well in
1118 76e2f08a Michael Hanselmann
        # order to be able to do add() on the set while owning it.
1119 887c7aa6 Michael Hanselmann
        if not self.__lock.acquire(shared=shared, priority=priority,
1120 7e8841bd Michael Hanselmann
                                   timeout=running_timeout.Remaining()):
1121 76e2f08a Michael Hanselmann
          raise _AcquireTimeout()
1122 76e2f08a Michael Hanselmann
        try:
1123 76e2f08a Michael Hanselmann
          # note we own the set-lock
1124 76e2f08a Michael Hanselmann
          self._add_owned()
1125 76e2f08a Michael Hanselmann
1126 887c7aa6 Michael Hanselmann
          return self.__acquire_inner(self.__names(), True, shared, priority,
1127 7e8841bd Michael Hanselmann
                                      running_timeout.Remaining, test_notify)
1128 76e2f08a Michael Hanselmann
        except:
1129 76e2f08a Michael Hanselmann
          # We shouldn't have problems adding the lock to the owners list, but
1130 76e2f08a Michael Hanselmann
          # if we did we'll try to release this lock and re-raise exception.
1131 76e2f08a Michael Hanselmann
          # Of course something is going to be really wrong, after this.
1132 5aab242c Michael Hanselmann
          self.__lock.release()
1133 76e2f08a Michael Hanselmann
          self._del_owned()
1134 76e2f08a Michael Hanselmann
          raise
1135 5aab242c Michael Hanselmann
1136 5aab242c Michael Hanselmann
    except _AcquireTimeout:
1137 5aab242c Michael Hanselmann
      return None
1138 aaae9bc0 Guido Trotter
1139 887c7aa6 Michael Hanselmann
  def __acquire_inner(self, names, want_all, shared, priority,
1140 887c7aa6 Michael Hanselmann
                      timeout_fn, test_notify):
1141 7e8841bd Michael Hanselmann
    """Inner logic for acquiring a number of locks.
1142 7e8841bd Michael Hanselmann

1143 7e8841bd Michael Hanselmann
    @param names: Names of the locks to be acquired
1144 7e8841bd Michael Hanselmann
    @param want_all: Whether all locks in the set should be acquired
1145 7e8841bd Michael Hanselmann
    @param shared: Whether to acquire in shared mode
1146 7e8841bd Michael Hanselmann
    @param timeout_fn: Function returning remaining timeout
1147 887c7aa6 Michael Hanselmann
    @param priority: Priority for acquiring locks
1148 7e8841bd Michael Hanselmann
    @param test_notify: Special callback function for unittesting
1149 76e2f08a Michael Hanselmann

1150 76e2f08a Michael Hanselmann
    """
1151 76e2f08a Michael Hanselmann
    acquire_list = []
1152 76e2f08a Michael Hanselmann
1153 76e2f08a Michael Hanselmann
    # First we look the locks up on __lockdict. We have no way of being sure
1154 76e2f08a Michael Hanselmann
    # they will still be there after, but this makes it a lot faster should
1155 71e1863e Michael Hanselmann
    # just one of them be the already wrong. Using a sorted sequence to prevent
1156 71e1863e Michael Hanselmann
    # deadlocks.
1157 71e1863e Michael Hanselmann
    for lname in sorted(utils.UniqueSequence(names)):
1158 76e2f08a Michael Hanselmann
      try:
1159 76e2f08a Michael Hanselmann
        lock = self.__lockdict[lname] # raises KeyError if lock is not there
1160 76e2f08a Michael Hanselmann
      except KeyError:
1161 76e2f08a Michael Hanselmann
        if want_all:
1162 76e2f08a Michael Hanselmann
          # We are acquiring all the set, it doesn't matter if this particular
1163 76e2f08a Michael Hanselmann
          # element is not there anymore.
1164 76e2f08a Michael Hanselmann
          continue
1165 76e2f08a Michael Hanselmann
1166 e1137eb6 Michael Hanselmann
        raise errors.LockError("Non-existing lock %s in set %s (it may have"
1167 e1137eb6 Michael Hanselmann
                               " been removed)" % (lname, self.name))
1168 76e2f08a Michael Hanselmann
1169 9b154270 Michael Hanselmann
      acquire_list.append((lname, lock))
1170 9b154270 Michael Hanselmann
1171 76e2f08a Michael Hanselmann
    # This will hold the locknames we effectively acquired.
1172 76e2f08a Michael Hanselmann
    acquired = set()
1173 76e2f08a Michael Hanselmann
1174 76e2f08a Michael Hanselmann
    try:
1175 76e2f08a Michael Hanselmann
      # Now acquire_list contains a sorted list of resources and locks we
1176 76e2f08a Michael Hanselmann
      # want.  In order to get them we loop on this (private) list and
1177 76e2f08a Michael Hanselmann
      # acquire() them.  We gave no real guarantee they will still exist till
1178 76e2f08a Michael Hanselmann
      # this is done but .acquire() itself is safe and will alert us if the
1179 76e2f08a Michael Hanselmann
      # lock gets deleted.
1180 76e2f08a Michael Hanselmann
      for (lname, lock) in acquire_list:
1181 76e2f08a Michael Hanselmann
        if __debug__ and callable(test_notify):
1182 76e2f08a Michael Hanselmann
          test_notify_fn = lambda: test_notify(lname)
1183 76e2f08a Michael Hanselmann
        else:
1184 76e2f08a Michael Hanselmann
          test_notify_fn = None
1185 76e2f08a Michael Hanselmann
1186 76e2f08a Michael Hanselmann
        timeout = timeout_fn()
1187 76e2f08a Michael Hanselmann
1188 76e2f08a Michael Hanselmann
        try:
1189 76e2f08a Michael Hanselmann
          # raises LockError if the lock was deleted
1190 76e2f08a Michael Hanselmann
          acq_success = lock.acquire(shared=shared, timeout=timeout,
1191 887c7aa6 Michael Hanselmann
                                     priority=priority,
1192 76e2f08a Michael Hanselmann
                                     test_notify=test_notify_fn)
1193 76e2f08a Michael Hanselmann
        except errors.LockError:
1194 76e2f08a Michael Hanselmann
          if want_all:
1195 76e2f08a Michael Hanselmann
            # We are acquiring all the set, it doesn't matter if this
1196 76e2f08a Michael Hanselmann
            # particular element is not there anymore.
1197 76e2f08a Michael Hanselmann
            continue
1198 76e2f08a Michael Hanselmann
1199 e1137eb6 Michael Hanselmann
          raise errors.LockError("Non-existing lock %s in set %s (it may"
1200 e1137eb6 Michael Hanselmann
                                 " have been removed)" % (lname, self.name))
1201 76e2f08a Michael Hanselmann
1202 76e2f08a Michael Hanselmann
        if not acq_success:
1203 76e2f08a Michael Hanselmann
          # Couldn't get lock or timeout occurred
1204 76e2f08a Michael Hanselmann
          if timeout is None:
1205 76e2f08a Michael Hanselmann
            # This shouldn't happen as SharedLock.acquire(timeout=None) is
1206 76e2f08a Michael Hanselmann
            # blocking.
1207 7f93570a Iustin Pop
            raise errors.LockError("Failed to get lock %s (set %s)" %
1208 7f93570a Iustin Pop
                                   (lname, self.name))
1209 76e2f08a Michael Hanselmann
1210 76e2f08a Michael Hanselmann
          raise _AcquireTimeout()
1211 76e2f08a Michael Hanselmann
1212 76e2f08a Michael Hanselmann
        try:
1213 76e2f08a Michael Hanselmann
          # now the lock cannot be deleted, we have it!
1214 76e2f08a Michael Hanselmann
          self._add_owned(name=lname)
1215 76e2f08a Michael Hanselmann
          acquired.add(lname)
1216 76e2f08a Michael Hanselmann
1217 76e2f08a Michael Hanselmann
        except:
1218 76e2f08a Michael Hanselmann
          # We shouldn't have problems adding the lock to the owners list, but
1219 76e2f08a Michael Hanselmann
          # if we did we'll try to release this lock and re-raise exception.
1220 76e2f08a Michael Hanselmann
          # Of course something is going to be really wrong after this.
1221 ee2b99e3 Michael Hanselmann
          if lock.is_owned():
1222 76e2f08a Michael Hanselmann
            lock.release()
1223 76e2f08a Michael Hanselmann
          raise
1224 76e2f08a Michael Hanselmann
1225 76e2f08a Michael Hanselmann
    except:
1226 76e2f08a Michael Hanselmann
      # Release all owned locks
1227 76e2f08a Michael Hanselmann
      self._release_and_delete_owned()
1228 76e2f08a Michael Hanselmann
      raise
1229 76e2f08a Michael Hanselmann
1230 0cc00929 Guido Trotter
    return acquired
1231 aaae9bc0 Guido Trotter
1232 3dbe3ddf Michael Hanselmann
  def downgrade(self, names=None):
1233 3dbe3ddf Michael Hanselmann
    """Downgrade a set of resource locks from exclusive to shared mode.
1234 3dbe3ddf Michael Hanselmann

1235 3dbe3ddf Michael Hanselmann
    The locks must have been acquired in exclusive mode.
1236 3dbe3ddf Michael Hanselmann

1237 3dbe3ddf Michael Hanselmann
    """
1238 ee2b99e3 Michael Hanselmann
    assert self.is_owned(), ("downgrade on lockset %s while not owning any"
1239 ee2b99e3 Michael Hanselmann
                             " lock" % self.name)
1240 3dbe3ddf Michael Hanselmann
1241 3dbe3ddf Michael Hanselmann
    # Support passing in a single resource to downgrade rather than many
1242 3dbe3ddf Michael Hanselmann
    if isinstance(names, basestring):
1243 3dbe3ddf Michael Hanselmann
      names = [names]
1244 3dbe3ddf Michael Hanselmann
1245 ee2b99e3 Michael Hanselmann
    owned = self.list_owned()
1246 3dbe3ddf Michael Hanselmann
1247 3dbe3ddf Michael Hanselmann
    if names is None:
1248 3dbe3ddf Michael Hanselmann
      names = owned
1249 3dbe3ddf Michael Hanselmann
    else:
1250 3dbe3ddf Michael Hanselmann
      names = set(names)
1251 3dbe3ddf Michael Hanselmann
      assert owned.issuperset(names), \
1252 3dbe3ddf Michael Hanselmann
        ("downgrade() on unheld resources %s (set %s)" %
1253 3dbe3ddf Michael Hanselmann
         (names.difference(owned), self.name))
1254 3dbe3ddf Michael Hanselmann
1255 3dbe3ddf Michael Hanselmann
    for lockname in names:
1256 3dbe3ddf Michael Hanselmann
      self.__lockdict[lockname].downgrade()
1257 3dbe3ddf Michael Hanselmann
1258 3dbe3ddf Michael Hanselmann
    # Do we own the lockset in exclusive mode?
1259 ee2b99e3 Michael Hanselmann
    if self.__lock.is_owned(shared=0):
1260 3dbe3ddf Michael Hanselmann
      # Have all locks been downgraded?
1261 ee2b99e3 Michael Hanselmann
      if not compat.any(lock.is_owned(shared=0)
1262 3dbe3ddf Michael Hanselmann
                        for lock in self.__lockdict.values()):
1263 3dbe3ddf Michael Hanselmann
        self.__lock.downgrade()
1264 ee2b99e3 Michael Hanselmann
        assert self.__lock.is_owned(shared=1)
1265 3dbe3ddf Michael Hanselmann
1266 3dbe3ddf Michael Hanselmann
    return True
1267 3dbe3ddf Michael Hanselmann
1268 aaae9bc0 Guido Trotter
  def release(self, names=None):
1269 aaae9bc0 Guido Trotter
    """Release a set of resource locks, at the same level.
1270 aaae9bc0 Guido Trotter

1271 aaae9bc0 Guido Trotter
    You must have acquired the locks, either in shared or in exclusive mode,
1272 aaae9bc0 Guido Trotter
    before releasing them.
1273 aaae9bc0 Guido Trotter

1274 ec44d893 Guido Trotter
    @type names: list of strings, or None
1275 c41eea6e Iustin Pop
    @param names: the names of the locks which shall be released
1276 c41eea6e Iustin Pop
        (defaults to all the locks acquired at that level).
1277 aaae9bc0 Guido Trotter

1278 aaae9bc0 Guido Trotter
    """
1279 ee2b99e3 Michael Hanselmann
    assert self.is_owned(), ("release() on lock set %s while not owner" %
1280 ee2b99e3 Michael Hanselmann
                             self.name)
1281 aaae9bc0 Guido Trotter
1282 aaae9bc0 Guido Trotter
    # Support passing in a single resource to release rather than many
1283 aaae9bc0 Guido Trotter
    if isinstance(names, basestring):
1284 aaae9bc0 Guido Trotter
      names = [names]
1285 aaae9bc0 Guido Trotter
1286 aaae9bc0 Guido Trotter
    if names is None:
1287 ee2b99e3 Michael Hanselmann
      names = self.list_owned()
1288 aaae9bc0 Guido Trotter
    else:
1289 aaae9bc0 Guido Trotter
      names = set(names)
1290 ee2b99e3 Michael Hanselmann
      assert self.list_owned().issuperset(names), (
1291 7f93570a Iustin Pop
               "release() on unheld resources %s (set %s)" %
1292 ee2b99e3 Michael Hanselmann
               (names.difference(self.list_owned()), self.name))
1293 aaae9bc0 Guido Trotter
1294 3b7ed473 Guido Trotter
    # First of all let's release the "all elements" lock, if set.
1295 3b7ed473 Guido Trotter
    # After this 'add' can work again
1296 ee2b99e3 Michael Hanselmann
    if self.__lock.is_owned():
1297 3b7ed473 Guido Trotter
      self.__lock.release()
1298 b2dabfd6 Guido Trotter
      self._del_owned()
1299 3b7ed473 Guido Trotter
1300 aaae9bc0 Guido Trotter
    for lockname in names:
1301 aaae9bc0 Guido Trotter
      # If we are sure the lock doesn't leave __lockdict without being
1302 aaae9bc0 Guido Trotter
      # exclusively held we can do this...
1303 aaae9bc0 Guido Trotter
      self.__lockdict[lockname].release()
1304 b2dabfd6 Guido Trotter
      self._del_owned(name=lockname)
1305 aaae9bc0 Guido Trotter
1306 aaae9bc0 Guido Trotter
  def add(self, names, acquired=0, shared=0):
1307 aaae9bc0 Guido Trotter
    """Add a new set of elements to the set
1308 aaae9bc0 Guido Trotter

1309 ec44d893 Guido Trotter
    @type names: list of strings
1310 c41eea6e Iustin Pop
    @param names: names of the new elements to add
1311 ec44d893 Guido Trotter
    @type acquired: integer (0/1) used as a boolean
1312 c41eea6e Iustin Pop
    @param acquired: pre-acquire the new resource?
1313 ec44d893 Guido Trotter
    @type shared: integer (0/1) used as a boolean
1314 c41eea6e Iustin Pop
    @param shared: is the pre-acquisition shared?
1315 aaae9bc0 Guido Trotter

1316 aaae9bc0 Guido Trotter
    """
1317 d2aff862 Guido Trotter
    # Check we don't already own locks at this level
1318 ee2b99e3 Michael Hanselmann
    assert not self.is_owned() or self.__lock.is_owned(shared=0), \
1319 7f93570a Iustin Pop
      ("Cannot add locks if the set %s is only partially owned, or shared" %
1320 7f93570a Iustin Pop
       self.name)
1321 3b7ed473 Guido Trotter
1322 aaae9bc0 Guido Trotter
    # Support passing in a single resource to add rather than many
1323 aaae9bc0 Guido Trotter
    if isinstance(names, basestring):
1324 aaae9bc0 Guido Trotter
      names = [names]
1325 aaae9bc0 Guido Trotter
1326 ab62526c Guido Trotter
    # If we don't already own the set-level lock acquired in an exclusive way
1327 3b7ed473 Guido Trotter
    # we'll get it and note we need to release it later.
1328 3b7ed473 Guido Trotter
    release_lock = False
1329 ee2b99e3 Michael Hanselmann
    if not self.__lock.is_owned():
1330 3b7ed473 Guido Trotter
      release_lock = True
1331 3b7ed473 Guido Trotter
      self.__lock.acquire()
1332 3b7ed473 Guido Trotter
1333 aaae9bc0 Guido Trotter
    try:
1334 0cf257c5 Guido Trotter
      invalid_names = set(self.__names()).intersection(names)
1335 aaae9bc0 Guido Trotter
      if invalid_names:
1336 aaae9bc0 Guido Trotter
        # This must be an explicit raise, not an assert, because assert is
1337 aaae9bc0 Guido Trotter
        # turned off when using optimization, and this can happen because of
1338 aaae9bc0 Guido Trotter
        # concurrency even if the user doesn't want it.
1339 7f93570a Iustin Pop
        raise errors.LockError("duplicate add(%s) on lockset %s" %
1340 7f93570a Iustin Pop
                               (invalid_names, self.name))
1341 aaae9bc0 Guido Trotter
1342 aaae9bc0 Guido Trotter
      for lockname in names:
1343 19b9ba9a Michael Hanselmann
        lock = SharedLock(self._GetLockName(lockname), monitor=self.__monitor)
1344 aaae9bc0 Guido Trotter
1345 aaae9bc0 Guido Trotter
        if acquired:
1346 887c7aa6 Michael Hanselmann
          # No need for priority or timeout here as this lock has just been
1347 887c7aa6 Michael Hanselmann
          # created
1348 aaae9bc0 Guido Trotter
          lock.acquire(shared=shared)
1349 aaae9bc0 Guido Trotter
          # now the lock cannot be deleted, we have it!
1350 aaae9bc0 Guido Trotter
          try:
1351 b2dabfd6 Guido Trotter
            self._add_owned(name=lockname)
1352 aaae9bc0 Guido Trotter
          except:
1353 aaae9bc0 Guido Trotter
            # We shouldn't have problems adding the lock to the owners list,
1354 aaae9bc0 Guido Trotter
            # but if we did we'll try to release this lock and re-raise
1355 aaae9bc0 Guido Trotter
            # exception.  Of course something is going to be really wrong,
1356 aaae9bc0 Guido Trotter
            # after this.  On the other hand the lock hasn't been added to the
1357 aaae9bc0 Guido Trotter
            # __lockdict yet so no other threads should be pending on it. This
1358 aaae9bc0 Guido Trotter
            # release is just a safety measure.
1359 aaae9bc0 Guido Trotter
            lock.release()
1360 aaae9bc0 Guido Trotter
            raise
1361 aaae9bc0 Guido Trotter
1362 aaae9bc0 Guido Trotter
        self.__lockdict[lockname] = lock
1363 aaae9bc0 Guido Trotter
1364 aaae9bc0 Guido Trotter
    finally:
1365 3b7ed473 Guido Trotter
      # Only release __lock if we were not holding it previously.
1366 3b7ed473 Guido Trotter
      if release_lock:
1367 3b7ed473 Guido Trotter
        self.__lock.release()
1368 aaae9bc0 Guido Trotter
1369 aaae9bc0 Guido Trotter
    return True
1370 aaae9bc0 Guido Trotter
1371 5e0a6daf Michael Hanselmann
  def remove(self, names):
1372 aaae9bc0 Guido Trotter
    """Remove elements from the lock set.
1373 aaae9bc0 Guido Trotter

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

1377 ec44d893 Guido Trotter
    @type names: list of strings
1378 c41eea6e Iustin Pop
    @param names: names of the resource to remove.
1379 aaae9bc0 Guido Trotter

1380 5aab242c Michael Hanselmann
    @return: a list of locks which we removed; the list is always
1381 c41eea6e Iustin Pop
        equal to the names list if we were holding all the locks
1382 c41eea6e Iustin Pop
        exclusively
1383 aaae9bc0 Guido Trotter

1384 aaae9bc0 Guido Trotter
    """
1385 aaae9bc0 Guido Trotter
    # Support passing in a single resource to remove rather than many
1386 aaae9bc0 Guido Trotter
    if isinstance(names, basestring):
1387 aaae9bc0 Guido Trotter
      names = [names]
1388 aaae9bc0 Guido Trotter
1389 aaae9bc0 Guido Trotter
    # If we own any subset of this lock it must be a superset of what we want
1390 aaae9bc0 Guido Trotter
    # to delete. The ownership must also be exclusive, but that will be checked
1391 aaae9bc0 Guido Trotter
    # by the lock itself.
1392 ee2b99e3 Michael Hanselmann
    assert not self.is_owned() or self.list_owned().issuperset(names), (
1393 7f93570a Iustin Pop
      "remove() on acquired lockset %s while not owning all elements" %
1394 7f93570a Iustin Pop
      self.name)
1395 aaae9bc0 Guido Trotter
1396 3f404fc5 Guido Trotter
    removed = []
1397 aaae9bc0 Guido Trotter
1398 aaae9bc0 Guido Trotter
    for lname in names:
1399 aaae9bc0 Guido Trotter
      # Calling delete() acquires the lock exclusively if we don't already own
1400 aaae9bc0 Guido Trotter
      # it, and causes all pending and subsequent lock acquires to fail. It's
1401 aaae9bc0 Guido Trotter
      # fine to call it out of order because delete() also implies release(),
1402 aaae9bc0 Guido Trotter
      # and the assertion above guarantees that if we either already hold
1403 aaae9bc0 Guido Trotter
      # everything we want to delete, or we hold none.
1404 aaae9bc0 Guido Trotter
      try:
1405 aaae9bc0 Guido Trotter
        self.__lockdict[lname].delete()
1406 3f404fc5 Guido Trotter
        removed.append(lname)
1407 aaae9bc0 Guido Trotter
      except (KeyError, errors.LockError):
1408 aaae9bc0 Guido Trotter
        # This cannot happen if we were already holding it, verify:
1409 ee2b99e3 Michael Hanselmann
        assert not self.is_owned(), ("remove failed while holding lockset %s" %
1410 ee2b99e3 Michael Hanselmann
                                     self.name)
1411 aaae9bc0 Guido Trotter
      else:
1412 aaae9bc0 Guido Trotter
        # If no LockError was raised we are the ones who deleted the lock.
1413 aaae9bc0 Guido Trotter
        # This means we can safely remove it from lockdict, as any further or
1414 aaae9bc0 Guido Trotter
        # pending delete() or acquire() will fail (and nobody can have the lock
1415 aaae9bc0 Guido Trotter
        # since before our call to delete()).
1416 aaae9bc0 Guido Trotter
        #
1417 aaae9bc0 Guido Trotter
        # This is done in an else clause because if the exception was thrown
1418 aaae9bc0 Guido Trotter
        # it's the job of the one who actually deleted it.
1419 aaae9bc0 Guido Trotter
        del self.__lockdict[lname]
1420 aaae9bc0 Guido Trotter
        # And let's remove it from our private list if we owned it.
1421 ee2b99e3 Michael Hanselmann
        if self.is_owned():
1422 b2dabfd6 Guido Trotter
          self._del_owned(name=lname)
1423 aaae9bc0 Guido Trotter
1424 3f404fc5 Guido Trotter
    return removed
1425 aaae9bc0 Guido Trotter
1426 7ee7c0c7 Guido Trotter
1427 7ee7c0c7 Guido Trotter
# Locking levels, must be acquired in increasing order.
1428 7ee7c0c7 Guido Trotter
# Current rules are:
1429 7ee7c0c7 Guido Trotter
#   - at level LEVEL_CLUSTER resides the Big Ganeti Lock (BGL) which must be
1430 7ee7c0c7 Guido Trotter
#   acquired before performing any operation, either in shared or in exclusive
1431 7ee7c0c7 Guido Trotter
#   mode. acquiring the BGL in exclusive mode is discouraged and should be
1432 7ee7c0c7 Guido Trotter
#   avoided.
1433 7ee7c0c7 Guido Trotter
#   - at levels LEVEL_NODE and LEVEL_INSTANCE reside node and instance locks.
1434 7ee7c0c7 Guido Trotter
#   If you need more than one node, or more than one instance, acquire them at
1435 7ee7c0c7 Guido Trotter
#   the same time.
1436 7ee7c0c7 Guido Trotter
LEVEL_CLUSTER = 0
1437 04e1bfaf Guido Trotter
LEVEL_INSTANCE = 1
1438 819ca990 Guido Trotter
LEVEL_NODEGROUP = 2
1439 819ca990 Guido Trotter
LEVEL_NODE = 3
1440 4e070776 Michael Hanselmann
LEVEL_NODE_RES = 4
1441 7ee7c0c7 Guido Trotter
1442 4e070776 Michael Hanselmann
LEVELS = [
1443 4e070776 Michael Hanselmann
  LEVEL_CLUSTER,
1444 4e070776 Michael Hanselmann
  LEVEL_INSTANCE,
1445 4e070776 Michael Hanselmann
  LEVEL_NODEGROUP,
1446 4e070776 Michael Hanselmann
  LEVEL_NODE,
1447 4e070776 Michael Hanselmann
  LEVEL_NODE_RES,
1448 4e070776 Michael Hanselmann
  ]
1449 7ee7c0c7 Guido Trotter
1450 7ee7c0c7 Guido Trotter
# Lock levels which are modifiable
1451 4e070776 Michael Hanselmann
LEVELS_MOD = frozenset([
1452 4e070776 Michael Hanselmann
  LEVEL_NODE_RES,
1453 4e070776 Michael Hanselmann
  LEVEL_NODE,
1454 4e070776 Michael Hanselmann
  LEVEL_NODEGROUP,
1455 4e070776 Michael Hanselmann
  LEVEL_INSTANCE,
1456 4e070776 Michael Hanselmann
  ])
1457 7ee7c0c7 Guido Trotter
1458 5d7a899e Michael Hanselmann
#: Lock level names (make sure to use singular form)
1459 ea205dbc Michael Hanselmann
LEVEL_NAMES = {
1460 ea205dbc Michael Hanselmann
  LEVEL_CLUSTER: "cluster",
1461 ea205dbc Michael Hanselmann
  LEVEL_INSTANCE: "instance",
1462 819ca990 Guido Trotter
  LEVEL_NODEGROUP: "nodegroup",
1463 ea205dbc Michael Hanselmann
  LEVEL_NODE: "node",
1464 5d7a899e Michael Hanselmann
  LEVEL_NODE_RES: "node-res",
1465 ea205dbc Michael Hanselmann
  }
1466 ea205dbc Michael Hanselmann
1467 08a6c581 Guido Trotter
# Constant for the big ganeti lock
1468 3ccb3a64 Michael Hanselmann
BGL = "BGL"
1469 7ee7c0c7 Guido Trotter
1470 7ee7c0c7 Guido Trotter
1471 7ee7c0c7 Guido Trotter
class GanetiLockManager:
1472 7ee7c0c7 Guido Trotter
  """The Ganeti Locking Library
1473 7ee7c0c7 Guido Trotter

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

1479 7ee7c0c7 Guido Trotter
  """
1480 7ee7c0c7 Guido Trotter
  _instance = None
1481 7ee7c0c7 Guido Trotter
1482 819ca990 Guido Trotter
  def __init__(self, nodes, nodegroups, instances):
1483 7ee7c0c7 Guido Trotter
    """Constructs a new GanetiLockManager object.
1484 7ee7c0c7 Guido Trotter

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

1488 c41eea6e Iustin Pop
    @param nodes: list of node names
1489 819ca990 Guido Trotter
    @param nodegroups: list of nodegroup uuids
1490 c41eea6e Iustin Pop
    @param instances: list of instance names
1491 7ee7c0c7 Guido Trotter

1492 7ee7c0c7 Guido Trotter
    """
1493 c41eea6e Iustin Pop
    assert self.__class__._instance is None, \
1494 c41eea6e Iustin Pop
           "double GanetiLockManager instance"
1495 c41eea6e Iustin Pop
1496 7ee7c0c7 Guido Trotter
    self.__class__._instance = self
1497 7ee7c0c7 Guido Trotter
1498 19b9ba9a Michael Hanselmann
    self._monitor = LockMonitor()
1499 19b9ba9a Michael Hanselmann
1500 7ee7c0c7 Guido Trotter
    # The keyring contains all the locks, at their level and in the correct
1501 7ee7c0c7 Guido Trotter
    # locking order.
1502 7ee7c0c7 Guido Trotter
    self.__keyring = {
1503 5d7a899e Michael Hanselmann
      LEVEL_CLUSTER: LockSet([BGL], "cluster", monitor=self._monitor),
1504 5d7a899e Michael Hanselmann
      LEVEL_NODE: LockSet(nodes, "node", monitor=self._monitor),
1505 5d7a899e Michael Hanselmann
      LEVEL_NODE_RES: LockSet(nodes, "node-res", monitor=self._monitor),
1506 5d7a899e Michael Hanselmann
      LEVEL_NODEGROUP: LockSet(nodegroups, "nodegroup", monitor=self._monitor),
1507 5d7a899e Michael Hanselmann
      LEVEL_INSTANCE: LockSet(instances, "instance",
1508 19b9ba9a Michael Hanselmann
                              monitor=self._monitor),
1509 19b9ba9a Michael Hanselmann
      }
1510 19b9ba9a Michael Hanselmann
1511 5d7a899e Michael Hanselmann
    assert compat.all(ls.name == LEVEL_NAMES[level]
1512 5d7a899e Michael Hanselmann
                      for (level, ls) in self.__keyring.items())
1513 5d7a899e Michael Hanselmann
1514 4c03b2b5 Michael Hanselmann
  def AddToLockMonitor(self, provider):
1515 4c03b2b5 Michael Hanselmann
    """Registers a new lock with the monitor.
1516 4c03b2b5 Michael Hanselmann

1517 4c03b2b5 Michael Hanselmann
    See L{LockMonitor.RegisterLock}.
1518 4c03b2b5 Michael Hanselmann

1519 4c03b2b5 Michael Hanselmann
    """
1520 4c03b2b5 Michael Hanselmann
    return self._monitor.RegisterLock(provider)
1521 4c03b2b5 Michael Hanselmann
1522 24d16f76 Michael Hanselmann
  def QueryLocks(self, fields):
1523 19b9ba9a Michael Hanselmann
    """Queries information from all locks.
1524 19b9ba9a Michael Hanselmann

1525 19b9ba9a Michael Hanselmann
    See L{LockMonitor.QueryLocks}.
1526 19b9ba9a Michael Hanselmann

1527 19b9ba9a Michael Hanselmann
    """
1528 24d16f76 Michael Hanselmann
    return self._monitor.QueryLocks(fields)
1529 24d16f76 Michael Hanselmann
1530 24d16f76 Michael Hanselmann
  def OldStyleQueryLocks(self, fields):
1531 24d16f76 Michael Hanselmann
    """Queries information from all locks, returning old-style data.
1532 24d16f76 Michael Hanselmann

1533 24d16f76 Michael Hanselmann
    See L{LockMonitor.OldStyleQueryLocks}.
1534 24d16f76 Michael Hanselmann

1535 24d16f76 Michael Hanselmann
    """
1536 24d16f76 Michael Hanselmann
    return self._monitor.OldStyleQueryLocks(fields)
1537 7ee7c0c7 Guido Trotter
1538 7ee7c0c7 Guido Trotter
  def _names(self, level):
1539 7ee7c0c7 Guido Trotter
    """List the lock names at the given level.
1540 7ee7c0c7 Guido Trotter

1541 c41eea6e Iustin Pop
    This can be used for debugging/testing purposes.
1542 c41eea6e Iustin Pop

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

1545 7ee7c0c7 Guido Trotter
    """
1546 7ee7c0c7 Guido Trotter
    assert level in LEVELS, "Invalid locking level %s" % level
1547 7ee7c0c7 Guido Trotter
    return self.__keyring[level]._names()
1548 7ee7c0c7 Guido Trotter
1549 ee2b99e3 Michael Hanselmann
  def is_owned(self, level):
1550 7ee7c0c7 Guido Trotter
    """Check whether we are owning locks at the given level
1551 7ee7c0c7 Guido Trotter

1552 7ee7c0c7 Guido Trotter
    """
1553 ee2b99e3 Michael Hanselmann
    return self.__keyring[level].is_owned()
1554 7ee7c0c7 Guido Trotter
1555 ee2b99e3 Michael Hanselmann
  def list_owned(self, level):
1556 7ee7c0c7 Guido Trotter
    """Get the set of owned locks at the given level
1557 7ee7c0c7 Guido Trotter

1558 7ee7c0c7 Guido Trotter
    """
1559 ee2b99e3 Michael Hanselmann
    return self.__keyring[level].list_owned()
1560 07cba1bc Michael Hanselmann
1561 c6a622cf Michael Hanselmann
  def check_owned(self, level, names, shared=-1):
1562 c6a622cf Michael Hanselmann
    """Check if locks at a certain level are owned in a specific mode.
1563 c6a622cf Michael Hanselmann

1564 c6a622cf Michael Hanselmann
    @see: L{LockSet.check_owned}
1565 c6a622cf Michael Hanselmann

1566 c6a622cf Michael Hanselmann
    """
1567 c6a622cf Michael Hanselmann
    return self.__keyring[level].check_owned(names, shared=shared)
1568 c6a622cf Michael Hanselmann
1569 7ee7c0c7 Guido Trotter
  def _upper_owned(self, level):
1570 7ee7c0c7 Guido Trotter
    """Check that we don't own any lock at a level greater than the given one.
1571 7ee7c0c7 Guido Trotter

1572 7ee7c0c7 Guido Trotter
    """
1573 7ee7c0c7 Guido Trotter
    # This way of checking only works if LEVELS[i] = i, which we check for in
1574 7ee7c0c7 Guido Trotter
    # the test cases.
1575 ee2b99e3 Michael Hanselmann
    return compat.any((self.is_owned(l) for l in LEVELS[level + 1:]))
1576 7ee7c0c7 Guido Trotter
1577 b459a848 Andrea Spadaccini
  def _BGL_owned(self): # pylint: disable=C0103
1578 7ee7c0c7 Guido Trotter
    """Check if the current thread owns the BGL.
1579 7ee7c0c7 Guido Trotter

1580 7ee7c0c7 Guido Trotter
    Both an exclusive or a shared acquisition work.
1581 7ee7c0c7 Guido Trotter

1582 7ee7c0c7 Guido Trotter
    """
1583 ee2b99e3 Michael Hanselmann
    return BGL in self.__keyring[LEVEL_CLUSTER].list_owned()
1584 7ee7c0c7 Guido Trotter
1585 c70d2d9b Iustin Pop
  @staticmethod
1586 b459a848 Andrea Spadaccini
  def _contains_BGL(level, names): # pylint: disable=C0103
1587 c41eea6e Iustin Pop
    """Check if the level contains the BGL.
1588 c41eea6e Iustin Pop

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

1592 7ee7c0c7 Guido Trotter
    """
1593 7ee7c0c7 Guido Trotter
    return level == LEVEL_CLUSTER and (names is None or BGL in names)
1594 7ee7c0c7 Guido Trotter
1595 b30d95b6 Michael Hanselmann
  def acquire(self, level, names, timeout=None, shared=0, priority=None):
1596 7ee7c0c7 Guido Trotter
    """Acquire a set of resource locks, at the same level.
1597 7ee7c0c7 Guido Trotter

1598 ec44d893 Guido Trotter
    @type level: member of locking.LEVELS
1599 ec44d893 Guido Trotter
    @param level: the level at which the locks shall be acquired
1600 ec44d893 Guido Trotter
    @type names: list of strings (or string)
1601 c41eea6e Iustin Pop
    @param names: the names of the locks which shall be acquired
1602 c41eea6e Iustin Pop
        (special lock names, or instance/node names)
1603 ec44d893 Guido Trotter
    @type shared: integer (0/1) used as a boolean
1604 c41eea6e Iustin Pop
    @param shared: whether to acquire in shared mode; by default
1605 c41eea6e Iustin Pop
        an exclusive lock will be acquired
1606 5e0a6daf Michael Hanselmann
    @type timeout: float
1607 5e0a6daf Michael Hanselmann
    @param timeout: Maximum time to acquire all locks
1608 b30d95b6 Michael Hanselmann
    @type priority: integer
1609 b30d95b6 Michael Hanselmann
    @param priority: Priority for acquiring lock
1610 7ee7c0c7 Guido Trotter

1611 7ee7c0c7 Guido Trotter
    """
1612 7ee7c0c7 Guido Trotter
    assert level in LEVELS, "Invalid locking level %s" % level
1613 7ee7c0c7 Guido Trotter
1614 7ee7c0c7 Guido Trotter
    # Check that we are either acquiring the Big Ganeti Lock or we already own
1615 7ee7c0c7 Guido Trotter
    # it. Some "legacy" opcodes need to be sure they are run non-concurrently
1616 7ee7c0c7 Guido Trotter
    # so even if we've migrated we need to at least share the BGL to be
1617 7ee7c0c7 Guido Trotter
    # compatible with them. Of course if we own the BGL exclusively there's no
1618 7ee7c0c7 Guido Trotter
    # point in acquiring any other lock, unless perhaps we are half way through
1619 7ee7c0c7 Guido Trotter
    # the migration of the current opcode.
1620 7ee7c0c7 Guido Trotter
    assert (self._contains_BGL(level, names) or self._BGL_owned()), (
1621 7ee7c0c7 Guido Trotter
            "You must own the Big Ganeti Lock before acquiring any other")
1622 7ee7c0c7 Guido Trotter
1623 7ee7c0c7 Guido Trotter
    # Check we don't own locks at the same or upper levels.
1624 21a6c826 Guido Trotter
    assert not self._upper_owned(level), ("Cannot acquire locks at a level"
1625 7ee7c0c7 Guido Trotter
           " while owning some at a greater one")
1626 7ee7c0c7 Guido Trotter
1627 7ee7c0c7 Guido Trotter
    # Acquire the locks in the set.
1628 b30d95b6 Michael Hanselmann
    return self.__keyring[level].acquire(names, shared=shared, timeout=timeout,
1629 b30d95b6 Michael Hanselmann
                                         priority=priority)
1630 7ee7c0c7 Guido Trotter
1631 3dbe3ddf Michael Hanselmann
  def downgrade(self, level, names=None):
1632 3dbe3ddf Michael Hanselmann
    """Downgrade a set of resource locks from exclusive to shared mode.
1633 3dbe3ddf Michael Hanselmann

1634 3dbe3ddf Michael Hanselmann
    You must have acquired the locks in exclusive mode.
1635 3dbe3ddf Michael Hanselmann

1636 3dbe3ddf Michael Hanselmann
    @type level: member of locking.LEVELS
1637 3dbe3ddf Michael Hanselmann
    @param level: the level at which the locks shall be downgraded
1638 3dbe3ddf Michael Hanselmann
    @type names: list of strings, or None
1639 3dbe3ddf Michael Hanselmann
    @param names: the names of the locks which shall be downgraded
1640 3dbe3ddf Michael Hanselmann
        (defaults to all the locks acquired at the level)
1641 3dbe3ddf Michael Hanselmann

1642 3dbe3ddf Michael Hanselmann
    """
1643 3dbe3ddf Michael Hanselmann
    assert level in LEVELS, "Invalid locking level %s" % level
1644 3dbe3ddf Michael Hanselmann
1645 3dbe3ddf Michael Hanselmann
    return self.__keyring[level].downgrade(names=names)
1646 3dbe3ddf Michael Hanselmann
1647 7ee7c0c7 Guido Trotter
  def release(self, level, names=None):
1648 7ee7c0c7 Guido Trotter
    """Release a set of resource locks, at the same level.
1649 7ee7c0c7 Guido Trotter

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

1653 ec44d893 Guido Trotter
    @type level: member of locking.LEVELS
1654 ec44d893 Guido Trotter
    @param level: the level at which the locks shall be released
1655 ec44d893 Guido Trotter
    @type names: list of strings, or None
1656 c41eea6e Iustin Pop
    @param names: the names of the locks which shall be released
1657 c41eea6e Iustin Pop
        (defaults to all the locks acquired at that level)
1658 7ee7c0c7 Guido Trotter

1659 7ee7c0c7 Guido Trotter
    """
1660 7ee7c0c7 Guido Trotter
    assert level in LEVELS, "Invalid locking level %s" % level
1661 7ee7c0c7 Guido Trotter
    assert (not self._contains_BGL(level, names) or
1662 7ee7c0c7 Guido Trotter
            not self._upper_owned(LEVEL_CLUSTER)), (
1663 7ee7c0c7 Guido Trotter
            "Cannot release the Big Ganeti Lock while holding something"
1664 e4335b5b Michael Hanselmann
            " at upper levels (%r)" %
1665 ee2b99e3 Michael Hanselmann
            (utils.CommaJoin(["%s=%r" % (LEVEL_NAMES[i], self.list_owned(i))
1666 1f864b60 Iustin Pop
                              for i in self.__keyring.keys()]), ))
1667 7ee7c0c7 Guido Trotter
1668 7ee7c0c7 Guido Trotter
    # Release will complain if we don't own the locks already
1669 7ee7c0c7 Guido Trotter
    return self.__keyring[level].release(names)
1670 7ee7c0c7 Guido Trotter
1671 7ee7c0c7 Guido Trotter
  def add(self, level, names, acquired=0, shared=0):
1672 7ee7c0c7 Guido Trotter
    """Add locks at the specified level.
1673 7ee7c0c7 Guido Trotter

1674 ec44d893 Guido Trotter
    @type level: member of locking.LEVELS_MOD
1675 ec44d893 Guido Trotter
    @param level: the level at which the locks shall be added
1676 ec44d893 Guido Trotter
    @type names: list of strings
1677 c41eea6e Iustin Pop
    @param names: names of the locks to acquire
1678 ec44d893 Guido Trotter
    @type acquired: integer (0/1) used as a boolean
1679 c41eea6e Iustin Pop
    @param acquired: whether to acquire the newly added locks
1680 ec44d893 Guido Trotter
    @type shared: integer (0/1) used as a boolean
1681 c41eea6e Iustin Pop
    @param shared: whether the acquisition will be shared
1682 c41eea6e Iustin Pop

1683 7ee7c0c7 Guido Trotter
    """
1684 7ee7c0c7 Guido Trotter
    assert level in LEVELS_MOD, "Invalid or immutable level %s" % level
1685 7ee7c0c7 Guido Trotter
    assert self._BGL_owned(), ("You must own the BGL before performing other"
1686 7ee7c0c7 Guido Trotter
           " operations")
1687 7ee7c0c7 Guido Trotter
    assert not self._upper_owned(level), ("Cannot add locks at a level"
1688 7ee7c0c7 Guido Trotter
           " while owning some at a greater one")
1689 7ee7c0c7 Guido Trotter
    return self.__keyring[level].add(names, acquired=acquired, shared=shared)
1690 7ee7c0c7 Guido Trotter
1691 5e0a6daf Michael Hanselmann
  def remove(self, level, names):
1692 7ee7c0c7 Guido Trotter
    """Remove locks from the specified level.
1693 7ee7c0c7 Guido Trotter

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

1697 ec44d893 Guido Trotter
    @type level: member of locking.LEVELS_MOD
1698 ec44d893 Guido Trotter
    @param level: the level at which the locks shall be removed
1699 ec44d893 Guido Trotter
    @type names: list of strings
1700 c41eea6e Iustin Pop
    @param names: the names of the locks which shall be removed
1701 c41eea6e Iustin Pop
        (special lock names, or instance/node names)
1702 7ee7c0c7 Guido Trotter

1703 7ee7c0c7 Guido Trotter
    """
1704 7ee7c0c7 Guido Trotter
    assert level in LEVELS_MOD, "Invalid or immutable level %s" % level
1705 7ee7c0c7 Guido Trotter
    assert self._BGL_owned(), ("You must own the BGL before performing other"
1706 7ee7c0c7 Guido Trotter
           " operations")
1707 f12eadb3 Iustin Pop
    # Check we either own the level or don't own anything from here
1708 f12eadb3 Iustin Pop
    # up. LockSet.remove() will check the case in which we don't own
1709 f12eadb3 Iustin Pop
    # all the needed resources, or we have a shared ownership.
1710 ee2b99e3 Michael Hanselmann
    assert self.is_owned(level) or not self._upper_owned(level), (
1711 7ee7c0c7 Guido Trotter
           "Cannot remove locks at a level while not owning it or"
1712 7ee7c0c7 Guido Trotter
           " owning some at a greater one")
1713 5e0a6daf Michael Hanselmann
    return self.__keyring[level].remove(names)
1714 19b9ba9a Michael Hanselmann
1715 19b9ba9a Michael Hanselmann
1716 44b4eddc Michael Hanselmann
def _MonitorSortKey((item, idx, num)):
1717 e4e35357 Michael Hanselmann
  """Sorting key function.
1718 e4e35357 Michael Hanselmann

1719 44b4eddc Michael Hanselmann
  Sort by name, registration order and then order of information. This provides
1720 44b4eddc Michael Hanselmann
  a stable sort order over different providers, even if they return the same
1721 44b4eddc Michael Hanselmann
  name.
1722 e4e35357 Michael Hanselmann

1723 e4e35357 Michael Hanselmann
  """
1724 e4e35357 Michael Hanselmann
  (name, _, _, _) = item
1725 e4e35357 Michael Hanselmann
1726 44b4eddc Michael Hanselmann
  return (utils.NiceSortKey(name), num, idx)
1727 e4e35357 Michael Hanselmann
1728 e4e35357 Michael Hanselmann
1729 19b9ba9a Michael Hanselmann
class LockMonitor(object):
1730 19b9ba9a Michael Hanselmann
  _LOCK_ATTR = "_lock"
1731 19b9ba9a Michael Hanselmann
1732 19b9ba9a Michael Hanselmann
  def __init__(self):
1733 19b9ba9a Michael Hanselmann
    """Initializes this class.
1734 19b9ba9a Michael Hanselmann

1735 19b9ba9a Michael Hanselmann
    """
1736 19b9ba9a Michael Hanselmann
    self._lock = SharedLock("LockMonitor")
1737 19b9ba9a Michael Hanselmann
1738 e4e35357 Michael Hanselmann
    # Counter for stable sorting
1739 e4e35357 Michael Hanselmann
    self._counter = itertools.count(0)
1740 e4e35357 Michael Hanselmann
1741 19b9ba9a Michael Hanselmann
    # Tracked locks. Weak references are used to avoid issues with circular
1742 19b9ba9a Michael Hanselmann
    # references and deletion.
1743 19b9ba9a Michael Hanselmann
    self._locks = weakref.WeakKeyDictionary()
1744 19b9ba9a Michael Hanselmann
1745 19b9ba9a Michael Hanselmann
  @ssynchronized(_LOCK_ATTR)
1746 44b4eddc Michael Hanselmann
  def RegisterLock(self, provider):
1747 19b9ba9a Michael Hanselmann
    """Registers a new lock.
1748 19b9ba9a Michael Hanselmann

1749 44b4eddc Michael Hanselmann
    @param provider: Object with a callable method named C{GetLockInfo}, taking
1750 44b4eddc Michael Hanselmann
      a single C{set} containing the requested information items
1751 44b4eddc Michael Hanselmann
    @note: It would be nicer to only receive the function generating the
1752 44b4eddc Michael Hanselmann
      requested information but, as it turns out, weak references to bound
1753 44b4eddc Michael Hanselmann
      methods (e.g. C{self.GetLockInfo}) are tricky; there are several
1754 44b4eddc Michael Hanselmann
      workarounds, but none of the ones I found works properly in combination
1755 44b4eddc Michael Hanselmann
      with a standard C{WeakKeyDictionary}
1756 44b4eddc Michael Hanselmann

1757 19b9ba9a Michael Hanselmann
    """
1758 44b4eddc Michael Hanselmann
    assert provider not in self._locks, "Duplicate registration"
1759 e4e35357 Michael Hanselmann
1760 e4e35357 Michael Hanselmann
    # There used to be a check for duplicate names here. As it turned out, when
1761 e4e35357 Michael Hanselmann
    # a lock is re-created with the same name in a very short timeframe, the
1762 e4e35357 Michael Hanselmann
    # previous instance might not yet be removed from the weakref dictionary.
1763 e4e35357 Michael Hanselmann
    # By keeping track of the order of incoming registrations, a stable sort
1764 e4e35357 Michael Hanselmann
    # ordering can still be guaranteed.
1765 e4e35357 Michael Hanselmann
1766 44b4eddc Michael Hanselmann
    self._locks[provider] = self._counter.next()
1767 19b9ba9a Michael Hanselmann
1768 24d16f76 Michael Hanselmann
  def _GetLockInfo(self, requested):
1769 44b4eddc Michael Hanselmann
    """Get information from all locks.
1770 19b9ba9a Michael Hanselmann

1771 19b9ba9a Michael Hanselmann
    """
1772 44b4eddc Michael Hanselmann
    # Must hold lock while getting consistent list of tracked items
1773 44b4eddc Michael Hanselmann
    self._lock.acquire(shared=1)
1774 44b4eddc Michael Hanselmann
    try:
1775 44b4eddc Michael Hanselmann
      items = self._locks.items()
1776 44b4eddc Michael Hanselmann
    finally:
1777 44b4eddc Michael Hanselmann
      self._lock.release()
1778 44b4eddc Michael Hanselmann
1779 44b4eddc Michael Hanselmann
    return [(info, idx, num)
1780 44b4eddc Michael Hanselmann
            for (provider, num) in items
1781 44b4eddc Michael Hanselmann
            for (idx, info) in enumerate(provider.GetLockInfo(requested))]
1782 19b9ba9a Michael Hanselmann
1783 24d16f76 Michael Hanselmann
  def _Query(self, fields):
1784 24d16f76 Michael Hanselmann
    """Queries information from all locks.
1785 19b9ba9a Michael Hanselmann

1786 24d16f76 Michael Hanselmann
    @type fields: list of strings
1787 24d16f76 Michael Hanselmann
    @param fields: List of fields to return
1788 24d16f76 Michael Hanselmann

1789 24d16f76 Michael Hanselmann
    """
1790 24d16f76 Michael Hanselmann
    qobj = query.Query(query.LOCK_FIELDS, fields)
1791 24d16f76 Michael Hanselmann
1792 e4e35357 Michael Hanselmann
    # Get all data with internal lock held and then sort by name and incoming
1793 e4e35357 Michael Hanselmann
    # order
1794 e4e35357 Michael Hanselmann
    lockinfo = sorted(self._GetLockInfo(qobj.RequestedData()),
1795 e4e35357 Michael Hanselmann
                      key=_MonitorSortKey)
1796 24d16f76 Michael Hanselmann
1797 e4e35357 Michael Hanselmann
    # Extract lock information and build query data
1798 eb62069e Iustin Pop
    return (qobj, query.LockQueryData(map(compat.fst, lockinfo)))
1799 19b9ba9a Michael Hanselmann
1800 24d16f76 Michael Hanselmann
  def QueryLocks(self, fields):
1801 19b9ba9a Michael Hanselmann
    """Queries information from all locks.
1802 19b9ba9a Michael Hanselmann

1803 19b9ba9a Michael Hanselmann
    @type fields: list of strings
1804 19b9ba9a Michael Hanselmann
    @param fields: List of fields to return
1805 19b9ba9a Michael Hanselmann

1806 19b9ba9a Michael Hanselmann
    """
1807 24d16f76 Michael Hanselmann
    (qobj, ctx) = self._Query(fields)
1808 19b9ba9a Michael Hanselmann
1809 24d16f76 Michael Hanselmann
    # Prepare query response
1810 24d16f76 Michael Hanselmann
    return query.GetQueryResponse(qobj, ctx)
1811 24d16f76 Michael Hanselmann
1812 24d16f76 Michael Hanselmann
  def OldStyleQueryLocks(self, fields):
1813 24d16f76 Michael Hanselmann
    """Queries information from all locks, returning old-style data.
1814 24d16f76 Michael Hanselmann

1815 24d16f76 Michael Hanselmann
    @type fields: list of strings
1816 24d16f76 Michael Hanselmann
    @param fields: List of fields to return
1817 24d16f76 Michael Hanselmann

1818 24d16f76 Michael Hanselmann
    """
1819 24d16f76 Michael Hanselmann
    (qobj, ctx) = self._Query(fields)
1820 19b9ba9a Michael Hanselmann
1821 24d16f76 Michael Hanselmann
    return qobj.OldStyleQuery(ctx)