Statistics
| Branch: | Tag: | Revision:

root / lib / locking.py @ e6c200d6

History | View | Annotate | Download (18.1 kB)

1 162c1c1f Guido Trotter
#
2 162c1c1f Guido Trotter
#
3 162c1c1f Guido Trotter
4 162c1c1f Guido Trotter
# Copyright (C) 2006, 2007 Google Inc.
5 162c1c1f Guido Trotter
#
6 162c1c1f Guido Trotter
# This program is free software; you can redistribute it and/or modify
7 162c1c1f Guido Trotter
# it under the terms of the GNU General Public License as published by
8 162c1c1f Guido Trotter
# the Free Software Foundation; either version 2 of the License, or
9 162c1c1f Guido Trotter
# (at your option) any later version.
10 162c1c1f Guido Trotter
#
11 162c1c1f Guido Trotter
# This program is distributed in the hope that it will be useful, but
12 162c1c1f Guido Trotter
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 162c1c1f Guido Trotter
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 162c1c1f Guido Trotter
# General Public License for more details.
15 162c1c1f Guido Trotter
#
16 162c1c1f Guido Trotter
# You should have received a copy of the GNU General Public License
17 162c1c1f Guido Trotter
# along with this program; if not, write to the Free Software
18 162c1c1f Guido Trotter
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 162c1c1f Guido Trotter
# 02110-1301, USA.
20 162c1c1f Guido Trotter
21 162c1c1f Guido Trotter
"""Module implementing the Ganeti locking code."""
22 162c1c1f Guido Trotter
23 162c1c1f Guido Trotter
# pylint: disable-msg=W0613,W0201
24 162c1c1f Guido Trotter
25 162c1c1f Guido Trotter
import threading
26 a95fd5d7 Guido Trotter
# Wouldn't it be better to define LockingError in the locking module?
27 a95fd5d7 Guido Trotter
# Well, for now that's how the rest of the code does it...
28 a95fd5d7 Guido Trotter
from ganeti import errors
29 162c1c1f Guido Trotter
30 162c1c1f Guido Trotter
31 162c1c1f Guido Trotter
class SharedLock:
32 162c1c1f Guido Trotter
  """Implements a shared lock.
33 162c1c1f Guido Trotter

34 162c1c1f Guido Trotter
  Multiple threads can acquire the lock in a shared way, calling
35 162c1c1f Guido Trotter
  acquire_shared().  In order to acquire the lock in an exclusive way threads
36 162c1c1f Guido Trotter
  can call acquire_exclusive().
37 162c1c1f Guido Trotter

38 162c1c1f Guido Trotter
  The lock prevents starvation but does not guarantee that threads will acquire
39 162c1c1f Guido Trotter
  the shared lock in the order they queued for it, just that they will
40 162c1c1f Guido Trotter
  eventually do so.
41 162c1c1f Guido Trotter

42 162c1c1f Guido Trotter
  """
43 162c1c1f Guido Trotter
  def __init__(self):
44 d6646186 Guido Trotter
    """Construct a new SharedLock"""
45 162c1c1f Guido Trotter
    # we have two conditions, c_shr and c_exc, sharing the same lock.
46 162c1c1f Guido Trotter
    self.__lock = threading.Lock()
47 162c1c1f Guido Trotter
    self.__turn_shr = threading.Condition(self.__lock)
48 162c1c1f Guido Trotter
    self.__turn_exc = threading.Condition(self.__lock)
49 162c1c1f Guido Trotter
50 162c1c1f Guido Trotter
    # current lock holders
51 162c1c1f Guido Trotter
    self.__shr = set()
52 162c1c1f Guido Trotter
    self.__exc = None
53 162c1c1f Guido Trotter
54 162c1c1f Guido Trotter
    # lock waiters
55 162c1c1f Guido Trotter
    self.__nwait_exc = 0
56 162c1c1f Guido Trotter
    self.__nwait_shr = 0
57 162c1c1f Guido Trotter
58 a95fd5d7 Guido Trotter
    # is this lock in the deleted state?
59 a95fd5d7 Guido Trotter
    self.__deleted = False
60 a95fd5d7 Guido Trotter
61 162c1c1f Guido Trotter
  def __is_sharer(self):
62 162c1c1f Guido Trotter
    """Is the current thread sharing the lock at this time?"""
63 162c1c1f Guido Trotter
    return threading.currentThread() in self.__shr
64 162c1c1f Guido Trotter
65 162c1c1f Guido Trotter
  def __is_exclusive(self):
66 162c1c1f Guido Trotter
    """Is the current thread holding the lock exclusively at this time?"""
67 162c1c1f Guido Trotter
    return threading.currentThread() == self.__exc
68 162c1c1f Guido Trotter
69 162c1c1f Guido Trotter
  def __is_owned(self, shared=-1):
70 162c1c1f Guido Trotter
    """Is the current thread somehow owning the lock at this time?
71 162c1c1f Guido Trotter

72 162c1c1f Guido Trotter
    This is a private version of the function, which presumes you're holding
73 162c1c1f Guido Trotter
    the internal lock.
74 162c1c1f Guido Trotter

75 162c1c1f Guido Trotter
    """
76 162c1c1f Guido Trotter
    if shared < 0:
77 162c1c1f Guido Trotter
      return self.__is_sharer() or self.__is_exclusive()
78 162c1c1f Guido Trotter
    elif shared:
79 162c1c1f Guido Trotter
      return self.__is_sharer()
80 162c1c1f Guido Trotter
    else:
81 162c1c1f Guido Trotter
      return self.__is_exclusive()
82 162c1c1f Guido Trotter
83 162c1c1f Guido Trotter
  def _is_owned(self, shared=-1):
84 162c1c1f Guido Trotter
    """Is the current thread somehow owning the lock at this time?
85 162c1c1f Guido Trotter

86 162c1c1f Guido Trotter
    Args:
87 162c1c1f Guido Trotter
      shared:
88 162c1c1f Guido Trotter
        < 0: check for any type of ownership (default)
89 162c1c1f Guido Trotter
        0: check for exclusive ownership
90 162c1c1f Guido Trotter
        > 0: check for shared ownership
91 162c1c1f Guido Trotter

92 162c1c1f Guido Trotter
    """
93 162c1c1f Guido Trotter
    self.__lock.acquire()
94 162c1c1f Guido Trotter
    try:
95 162c1c1f Guido Trotter
      result = self.__is_owned(shared)
96 162c1c1f Guido Trotter
    finally:
97 162c1c1f Guido Trotter
      self.__lock.release()
98 162c1c1f Guido Trotter
99 162c1c1f Guido Trotter
    return result
100 162c1c1f Guido Trotter
101 a95fd5d7 Guido Trotter
  def __wait(self,c):
102 a95fd5d7 Guido Trotter
    """Wait on the given condition, and raise an exception if the current lock
103 a95fd5d7 Guido Trotter
    is declared deleted in the meantime.
104 a95fd5d7 Guido Trotter

105 a95fd5d7 Guido Trotter
    Args:
106 a95fd5d7 Guido Trotter
      c: condition to wait on
107 a95fd5d7 Guido Trotter

108 a95fd5d7 Guido Trotter
    """
109 a95fd5d7 Guido Trotter
    c.wait()
110 a95fd5d7 Guido Trotter
    if self.__deleted:
111 a95fd5d7 Guido Trotter
      raise errors.LockError('deleted lock')
112 a95fd5d7 Guido Trotter
113 a95fd5d7 Guido Trotter
  def __exclusive_acquire(self):
114 a95fd5d7 Guido Trotter
    """Acquire the lock exclusively.
115 a95fd5d7 Guido Trotter

116 a95fd5d7 Guido Trotter
    This is a private function that presumes you are already holding the
117 a95fd5d7 Guido Trotter
    internal lock. It's defined separately to avoid code duplication between
118 a95fd5d7 Guido Trotter
    acquire() and delete()
119 a95fd5d7 Guido Trotter

120 a95fd5d7 Guido Trotter
    """
121 a95fd5d7 Guido Trotter
    self.__nwait_exc += 1
122 a95fd5d7 Guido Trotter
    try:
123 a95fd5d7 Guido Trotter
      # This is to save ourselves from a nasty race condition that could
124 a95fd5d7 Guido Trotter
      # theoretically make the sharers starve.
125 a95fd5d7 Guido Trotter
      if self.__nwait_shr > 0 or self.__nwait_exc > 1:
126 a95fd5d7 Guido Trotter
        self.__wait(self.__turn_exc)
127 a95fd5d7 Guido Trotter
128 a95fd5d7 Guido Trotter
      while len(self.__shr) > 0 or self.__exc is not None:
129 a95fd5d7 Guido Trotter
        self.__wait(self.__turn_exc)
130 a95fd5d7 Guido Trotter
131 a95fd5d7 Guido Trotter
      self.__exc = threading.currentThread()
132 a95fd5d7 Guido Trotter
    finally:
133 a95fd5d7 Guido Trotter
      self.__nwait_exc -= 1
134 a95fd5d7 Guido Trotter
135 a95fd5d7 Guido Trotter
136 162c1c1f Guido Trotter
  def acquire(self, blocking=1, shared=0):
137 162c1c1f Guido Trotter
    """Acquire a shared lock.
138 162c1c1f Guido Trotter

139 162c1c1f Guido Trotter
    Args:
140 162c1c1f Guido Trotter
      shared: whether to acquire in shared mode. By default an exclusive lock
141 162c1c1f Guido Trotter
              will be acquired.
142 162c1c1f Guido Trotter
      blocking: whether to block while trying to acquire or to operate in try-lock mode.
143 162c1c1f Guido Trotter
                this locking mode is not supported yet.
144 162c1c1f Guido Trotter

145 162c1c1f Guido Trotter
    """
146 162c1c1f Guido Trotter
    if not blocking:
147 162c1c1f Guido Trotter
      # We don't have non-blocking mode for now
148 162c1c1f Guido Trotter
      raise NotImplementedError
149 162c1c1f Guido Trotter
150 162c1c1f Guido Trotter
    self.__lock.acquire()
151 162c1c1f Guido Trotter
    try:
152 a95fd5d7 Guido Trotter
      if self.__deleted:
153 a95fd5d7 Guido Trotter
        raise errors.LockError('deleted lock')
154 a95fd5d7 Guido Trotter
155 162c1c1f Guido Trotter
      # We cannot acquire the lock if we already have it
156 162c1c1f Guido Trotter
      assert not self.__is_owned(), "double acquire() on a non-recursive lock"
157 162c1c1f Guido Trotter
158 162c1c1f Guido Trotter
      if shared:
159 162c1c1f Guido Trotter
        self.__nwait_shr += 1
160 162c1c1f Guido Trotter
        try:
161 162c1c1f Guido Trotter
          # If there is an exclusive holder waiting we have to wait.  We'll
162 162c1c1f Guido Trotter
          # only do this once, though, when we start waiting for the lock. Then
163 162c1c1f Guido Trotter
          # we'll just wait while there are no exclusive holders.
164 162c1c1f Guido Trotter
          if self.__nwait_exc > 0:
165 162c1c1f Guido Trotter
            # TODO: if !blocking...
166 a95fd5d7 Guido Trotter
            self.__wait(self.__turn_shr)
167 162c1c1f Guido Trotter
168 162c1c1f Guido Trotter
          while self.__exc is not None:
169 162c1c1f Guido Trotter
            # TODO: if !blocking...
170 a95fd5d7 Guido Trotter
            self.__wait(self.__turn_shr)
171 162c1c1f Guido Trotter
172 162c1c1f Guido Trotter
          self.__shr.add(threading.currentThread())
173 162c1c1f Guido Trotter
        finally:
174 162c1c1f Guido Trotter
          self.__nwait_shr -= 1
175 162c1c1f Guido Trotter
176 162c1c1f Guido Trotter
      else:
177 a95fd5d7 Guido Trotter
        # TODO: if !blocking...
178 a95fd5d7 Guido Trotter
        # (or modify __exclusive_acquire for non-blocking mode)
179 a95fd5d7 Guido Trotter
        self.__exclusive_acquire()
180 162c1c1f Guido Trotter
181 162c1c1f Guido Trotter
    finally:
182 162c1c1f Guido Trotter
      self.__lock.release()
183 162c1c1f Guido Trotter
184 162c1c1f Guido Trotter
    return True
185 162c1c1f Guido Trotter
186 162c1c1f Guido Trotter
  def release(self):
187 162c1c1f Guido Trotter
    """Release a Shared Lock.
188 162c1c1f Guido Trotter

189 162c1c1f Guido Trotter
    You must have acquired the lock, either in shared or in exclusive mode,
190 162c1c1f Guido Trotter
    before calling this function.
191 162c1c1f Guido Trotter

192 162c1c1f Guido Trotter
    """
193 162c1c1f Guido Trotter
    self.__lock.acquire()
194 162c1c1f Guido Trotter
    try:
195 162c1c1f Guido Trotter
      # Autodetect release type
196 162c1c1f Guido Trotter
      if self.__is_exclusive():
197 162c1c1f Guido Trotter
        self.__exc = None
198 162c1c1f Guido Trotter
199 162c1c1f Guido Trotter
        # An exclusive holder has just had the lock, time to put it in shared
200 162c1c1f Guido Trotter
        # mode if there are shared holders waiting. Otherwise wake up the next
201 162c1c1f Guido Trotter
        # exclusive holder.
202 162c1c1f Guido Trotter
        if self.__nwait_shr > 0:
203 162c1c1f Guido Trotter
          self.__turn_shr.notifyAll()
204 162c1c1f Guido Trotter
        elif self.__nwait_exc > 0:
205 162c1c1f Guido Trotter
         self.__turn_exc.notify()
206 162c1c1f Guido Trotter
207 162c1c1f Guido Trotter
      elif self.__is_sharer():
208 162c1c1f Guido Trotter
        self.__shr.remove(threading.currentThread())
209 162c1c1f Guido Trotter
210 162c1c1f Guido Trotter
        # If there are shared holders waiting there *must* be an exclusive holder
211 162c1c1f Guido Trotter
        # waiting as well; otherwise what were they waiting for?
212 162c1c1f Guido Trotter
        assert (self.__nwait_shr == 0 or self.__nwait_exc > 0,
213 162c1c1f Guido Trotter
                "Lock sharers waiting while no exclusive is queueing")
214 162c1c1f Guido Trotter
215 162c1c1f Guido Trotter
        # If there are no more shared holders and some exclusive holders are
216 162c1c1f Guido Trotter
        # waiting let's wake one up.
217 162c1c1f Guido Trotter
        if len(self.__shr) == 0 and self.__nwait_exc > 0:
218 162c1c1f Guido Trotter
          self.__turn_exc.notify()
219 162c1c1f Guido Trotter
220 162c1c1f Guido Trotter
      else:
221 162c1c1f Guido Trotter
        assert False, "Cannot release non-owned lock"
222 162c1c1f Guido Trotter
223 162c1c1f Guido Trotter
    finally:
224 162c1c1f Guido Trotter
      self.__lock.release()
225 162c1c1f Guido Trotter
226 a95fd5d7 Guido Trotter
  def delete(self, blocking=1):
227 a95fd5d7 Guido Trotter
    """Delete a Shared Lock.
228 a95fd5d7 Guido Trotter

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

233 a95fd5d7 Guido Trotter
    Args:
234 a95fd5d7 Guido Trotter
      blocking: whether to block while trying to acquire or to operate in
235 a95fd5d7 Guido Trotter
                try-lock mode.  this locking mode is not supported yet unless
236 a95fd5d7 Guido Trotter
                you are already holding exclusively the lock.
237 a95fd5d7 Guido Trotter

238 a95fd5d7 Guido Trotter
    """
239 a95fd5d7 Guido Trotter
    self.__lock.acquire()
240 a95fd5d7 Guido Trotter
    try:
241 a95fd5d7 Guido Trotter
      assert not self.__is_sharer(), "cannot delete() a lock while sharing it"
242 a95fd5d7 Guido Trotter
243 a95fd5d7 Guido Trotter
      if self.__deleted:
244 a95fd5d7 Guido Trotter
        raise errors.LockError('deleted lock')
245 a95fd5d7 Guido Trotter
246 a95fd5d7 Guido Trotter
      if not self.__is_exclusive():
247 a95fd5d7 Guido Trotter
        if not blocking:
248 a95fd5d7 Guido Trotter
          # We don't have non-blocking mode for now
249 a95fd5d7 Guido Trotter
          raise NotImplementedError
250 a95fd5d7 Guido Trotter
        self.__exclusive_acquire()
251 a95fd5d7 Guido Trotter
252 a95fd5d7 Guido Trotter
      self.__deleted = True
253 a95fd5d7 Guido Trotter
      self.__exc = None
254 a95fd5d7 Guido Trotter
      # Wake up everybody, they will fail acquiring the lock and
255 a95fd5d7 Guido Trotter
      # raise an exception instead.
256 a95fd5d7 Guido Trotter
      self.__turn_exc.notifyAll()
257 a95fd5d7 Guido Trotter
      self.__turn_shr.notifyAll()
258 a95fd5d7 Guido Trotter
259 a95fd5d7 Guido Trotter
    finally:
260 a95fd5d7 Guido Trotter
      self.__lock.release()
261 a95fd5d7 Guido Trotter
262 aaae9bc0 Guido Trotter
263 aaae9bc0 Guido Trotter
class LockSet:
264 aaae9bc0 Guido Trotter
  """Implements a set of locks.
265 aaae9bc0 Guido Trotter

266 aaae9bc0 Guido Trotter
  This abstraction implements a set of shared locks for the same resource type,
267 aaae9bc0 Guido Trotter
  distinguished by name. The user can lock a subset of the resources and the
268 aaae9bc0 Guido Trotter
  LockSet will take care of acquiring the locks always in the same order, thus
269 aaae9bc0 Guido Trotter
  preventing deadlock.
270 aaae9bc0 Guido Trotter

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

273 aaae9bc0 Guido Trotter
  """
274 aaae9bc0 Guido Trotter
  def __init__(self, members=None):
275 aaae9bc0 Guido Trotter
    """Constructs a new LockSet.
276 aaae9bc0 Guido Trotter

277 aaae9bc0 Guido Trotter
    Args:
278 aaae9bc0 Guido Trotter
      members: initial members of the set
279 aaae9bc0 Guido Trotter

280 aaae9bc0 Guido Trotter
    """
281 aaae9bc0 Guido Trotter
    # Used internally to guarantee coherency.
282 aaae9bc0 Guido Trotter
    self.__lock = SharedLock()
283 aaae9bc0 Guido Trotter
284 aaae9bc0 Guido Trotter
    # The lockdict indexes the relationship name -> lock
285 aaae9bc0 Guido Trotter
    # The order-of-locking is implied by the alphabetical order of names
286 aaae9bc0 Guido Trotter
    self.__lockdict = {}
287 aaae9bc0 Guido Trotter
288 aaae9bc0 Guido Trotter
    if members is not None:
289 aaae9bc0 Guido Trotter
      for name in members:
290 aaae9bc0 Guido Trotter
        self.__lockdict[name] = SharedLock()
291 aaae9bc0 Guido Trotter
292 aaae9bc0 Guido Trotter
    # The owner dict contains the set of locks each thread owns. For
293 aaae9bc0 Guido Trotter
    # performance each thread can access its own key without a global lock on
294 aaae9bc0 Guido Trotter
    # this structure. It is paramount though that *no* other type of access is
295 aaae9bc0 Guido Trotter
    # done to this structure (eg. no looping over its keys). *_owner helper
296 aaae9bc0 Guido Trotter
    # function are defined to guarantee access is correct, but in general never
297 aaae9bc0 Guido Trotter
    # do anything different than __owners[threading.currentThread()], or there
298 aaae9bc0 Guido Trotter
    # will be trouble.
299 aaae9bc0 Guido Trotter
    self.__owners = {}
300 aaae9bc0 Guido Trotter
301 aaae9bc0 Guido Trotter
  def _is_owned(self):
302 aaae9bc0 Guido Trotter
    """Is the current thread a current level owner?"""
303 aaae9bc0 Guido Trotter
    return threading.currentThread() in self.__owners
304 aaae9bc0 Guido Trotter
305 aaae9bc0 Guido Trotter
  def _add_owned(self, name):
306 aaae9bc0 Guido Trotter
    """Note the current thread owns the given lock"""
307 aaae9bc0 Guido Trotter
    if self._is_owned():
308 aaae9bc0 Guido Trotter
      self.__owners[threading.currentThread()].add(name)
309 aaae9bc0 Guido Trotter
    else:
310 aaae9bc0 Guido Trotter
       self.__owners[threading.currentThread()] = set([name])
311 aaae9bc0 Guido Trotter
312 aaae9bc0 Guido Trotter
  def _del_owned(self, name):
313 aaae9bc0 Guido Trotter
    """Note the current thread owns the given lock"""
314 aaae9bc0 Guido Trotter
    self.__owners[threading.currentThread()].remove(name)
315 aaae9bc0 Guido Trotter
316 aaae9bc0 Guido Trotter
    if not self.__owners[threading.currentThread()]:
317 aaae9bc0 Guido Trotter
      del self.__owners[threading.currentThread()]
318 aaae9bc0 Guido Trotter
319 aaae9bc0 Guido Trotter
  def _list_owned(self):
320 aaae9bc0 Guido Trotter
    """Get the set of resource names owned by the current thread"""
321 aaae9bc0 Guido Trotter
    if self._is_owned():
322 aaae9bc0 Guido Trotter
      return self.__owners[threading.currentThread()].copy()
323 aaae9bc0 Guido Trotter
    else:
324 aaae9bc0 Guido Trotter
      return set()
325 aaae9bc0 Guido Trotter
326 aaae9bc0 Guido Trotter
  def __names(self):
327 aaae9bc0 Guido Trotter
    """Return the current set of names.
328 aaae9bc0 Guido Trotter

329 aaae9bc0 Guido Trotter
    Only call this function while holding __lock and don't iterate on the
330 aaae9bc0 Guido Trotter
    result after releasing the lock.
331 aaae9bc0 Guido Trotter

332 aaae9bc0 Guido Trotter
    """
333 aaae9bc0 Guido Trotter
    return set(self.__lockdict.keys())
334 aaae9bc0 Guido Trotter
335 aaae9bc0 Guido Trotter
  def _names(self):
336 aaae9bc0 Guido Trotter
    """Return a copy of the current set of elements.
337 aaae9bc0 Guido Trotter

338 aaae9bc0 Guido Trotter
    Used only for debugging purposes.
339 aaae9bc0 Guido Trotter
    """
340 aaae9bc0 Guido Trotter
    self.__lock.acquire(shared=1)
341 aaae9bc0 Guido Trotter
    try:
342 aaae9bc0 Guido Trotter
      result = self.__names()
343 aaae9bc0 Guido Trotter
    finally:
344 aaae9bc0 Guido Trotter
      self.__lock.release()
345 aaae9bc0 Guido Trotter
    return result
346 aaae9bc0 Guido Trotter
347 aaae9bc0 Guido Trotter
  def acquire(self, names, blocking=1, shared=0):
348 aaae9bc0 Guido Trotter
    """Acquire a set of resource locks.
349 aaae9bc0 Guido Trotter

350 aaae9bc0 Guido Trotter
    Args:
351 aaae9bc0 Guido Trotter
      names: the names of the locks which shall be acquired.
352 aaae9bc0 Guido Trotter
             (special lock names, or instance/node names)
353 aaae9bc0 Guido Trotter
      shared: whether to acquire in shared mode. By default an exclusive lock
354 aaae9bc0 Guido Trotter
              will be acquired.
355 aaae9bc0 Guido Trotter
      blocking: whether to block while trying to acquire or to operate in try-lock mode.
356 aaae9bc0 Guido Trotter
                this locking mode is not supported yet.
357 aaae9bc0 Guido Trotter

358 aaae9bc0 Guido Trotter
    Returns:
359 aaae9bc0 Guido Trotter
      True: when all the locks are successfully acquired
360 aaae9bc0 Guido Trotter

361 aaae9bc0 Guido Trotter
    Raises:
362 aaae9bc0 Guido Trotter
      errors.LockError: when any lock we try to acquire has been deleted
363 aaae9bc0 Guido Trotter
      before we succeed. In this case none of the locks requested will be
364 aaae9bc0 Guido Trotter
      acquired.
365 aaae9bc0 Guido Trotter

366 aaae9bc0 Guido Trotter
    """
367 aaae9bc0 Guido Trotter
    if not blocking:
368 aaae9bc0 Guido Trotter
      # We don't have non-blocking mode for now
369 aaae9bc0 Guido Trotter
      raise NotImplementedError
370 aaae9bc0 Guido Trotter
371 aaae9bc0 Guido Trotter
    # Check we don't already own locks at this level
372 aaae9bc0 Guido Trotter
    assert not self._is_owned(), "Cannot acquire locks in the same set twice"
373 aaae9bc0 Guido Trotter
374 aaae9bc0 Guido Trotter
    # Support passing in a single resource to acquire rather than many
375 aaae9bc0 Guido Trotter
    if isinstance(names, basestring):
376 aaae9bc0 Guido Trotter
      names = [names]
377 aaae9bc0 Guido Trotter
    else:
378 aaae9bc0 Guido Trotter
      names.sort()
379 aaae9bc0 Guido Trotter
380 e6c200d6 Guido Trotter
    acquire_list = []
381 e6c200d6 Guido Trotter
    # First we look the locks up on __lockdict. We have no way of being sure
382 e6c200d6 Guido Trotter
    # they will still be there after, but this makes it a lot faster should
383 e6c200d6 Guido Trotter
    # just one of them be the already wrong
384 aaae9bc0 Guido Trotter
    try:
385 aaae9bc0 Guido Trotter
      for lname in names:
386 aaae9bc0 Guido Trotter
        lock = self.__lockdict[lname] # raises KeyError if the lock is not there
387 e6c200d6 Guido Trotter
        acquire_list.append((lname, lock))
388 e6c200d6 Guido Trotter
    except (KeyError):
389 e6c200d6 Guido Trotter
      raise errors.LockError('non-existing lock in set (%s)' % lname)
390 e6c200d6 Guido Trotter
391 e6c200d6 Guido Trotter
    # Now acquire_list contains a sorted list of resources and locks we want.
392 e6c200d6 Guido Trotter
    # In order to get them we loop on this (private) list and acquire() them.
393 e6c200d6 Guido Trotter
    # We gave no real guarantee they will still exist till this is done but
394 e6c200d6 Guido Trotter
    # .acquire() itself is safe and will alert us if the lock gets deleted.
395 e6c200d6 Guido Trotter
    try:
396 e6c200d6 Guido Trotter
      for (lname, lock) in acquire_list:
397 aaae9bc0 Guido Trotter
        lock.acquire(shared=shared) # raises LockError if the lock is deleted
398 aaae9bc0 Guido Trotter
        try:
399 aaae9bc0 Guido Trotter
          # now the lock cannot be deleted, we have it!
400 aaae9bc0 Guido Trotter
          self._add_owned(lname)
401 aaae9bc0 Guido Trotter
        except:
402 aaae9bc0 Guido Trotter
          # We shouldn't have problems adding the lock to the owners list, but
403 aaae9bc0 Guido Trotter
          # if we did we'll try to release this lock and re-raise exception.
404 aaae9bc0 Guido Trotter
          # Of course something is going to be really wrong, after this.
405 aaae9bc0 Guido Trotter
          lock.release()
406 aaae9bc0 Guido Trotter
          raise
407 aaae9bc0 Guido Trotter
408 e6c200d6 Guido Trotter
    except (errors.LockError):
409 aaae9bc0 Guido Trotter
      name_fail = lname
410 aaae9bc0 Guido Trotter
      for lname in self._list_owned():
411 aaae9bc0 Guido Trotter
        self.__lockdict[lname].release()
412 aaae9bc0 Guido Trotter
        self._del_owned(lname)
413 aaae9bc0 Guido Trotter
      raise errors.LockError('non-existing lock in set (%s)' % name_fail)
414 aaae9bc0 Guido Trotter
415 aaae9bc0 Guido Trotter
    return True
416 aaae9bc0 Guido Trotter
417 aaae9bc0 Guido Trotter
  def release(self, names=None):
418 aaae9bc0 Guido Trotter
    """Release a set of resource locks, at the same level.
419 aaae9bc0 Guido Trotter

420 aaae9bc0 Guido Trotter
    You must have acquired the locks, either in shared or in exclusive mode,
421 aaae9bc0 Guido Trotter
    before releasing them.
422 aaae9bc0 Guido Trotter

423 aaae9bc0 Guido Trotter
    Args:
424 aaae9bc0 Guido Trotter
      names: the names of the locks which shall be released.
425 aaae9bc0 Guido Trotter
             (defaults to all the locks acquired at that level).
426 aaae9bc0 Guido Trotter

427 aaae9bc0 Guido Trotter
    """
428 aaae9bc0 Guido Trotter
429 aaae9bc0 Guido Trotter
    assert self._is_owned(), "release() on lock set while not owner"
430 aaae9bc0 Guido Trotter
431 aaae9bc0 Guido Trotter
    # Support passing in a single resource to release rather than many
432 aaae9bc0 Guido Trotter
    if isinstance(names, basestring):
433 aaae9bc0 Guido Trotter
      names = [names]
434 aaae9bc0 Guido Trotter
435 aaae9bc0 Guido Trotter
    if names is None:
436 aaae9bc0 Guido Trotter
      names = self._list_owned()
437 aaae9bc0 Guido Trotter
    else:
438 aaae9bc0 Guido Trotter
      names = set(names)
439 aaae9bc0 Guido Trotter
      assert self._list_owned().issuperset(names), (
440 aaae9bc0 Guido Trotter
               "release() on unheld resources %s" %
441 aaae9bc0 Guido Trotter
               names.difference(self._list_owned()))
442 aaae9bc0 Guido Trotter
443 aaae9bc0 Guido Trotter
    for lockname in names:
444 aaae9bc0 Guido Trotter
      # If we are sure the lock doesn't leave __lockdict without being
445 aaae9bc0 Guido Trotter
      # exclusively held we can do this...
446 aaae9bc0 Guido Trotter
      self.__lockdict[lockname].release()
447 aaae9bc0 Guido Trotter
      self._del_owned(lockname)
448 aaae9bc0 Guido Trotter
449 aaae9bc0 Guido Trotter
  def add(self, names, acquired=0, shared=0):
450 aaae9bc0 Guido Trotter
    """Add a new set of elements to the set
451 aaae9bc0 Guido Trotter

452 aaae9bc0 Guido Trotter
    Args:
453 aaae9bc0 Guido Trotter
      names: names of the new elements to add
454 aaae9bc0 Guido Trotter
      acquired: pre-acquire the new resource?
455 aaae9bc0 Guido Trotter
      shared: is the pre-acquisition shared?
456 aaae9bc0 Guido Trotter

457 aaae9bc0 Guido Trotter
    """
458 aaae9bc0 Guido Trotter
    # Support passing in a single resource to add rather than many
459 aaae9bc0 Guido Trotter
    if isinstance(names, basestring):
460 aaae9bc0 Guido Trotter
      names = [names]
461 aaae9bc0 Guido Trotter
462 aaae9bc0 Guido Trotter
    # Acquire the internal lock in an exclusive way, so there cannot be a
463 aaae9bc0 Guido Trotter
    # conflicting add()
464 aaae9bc0 Guido Trotter
    self.__lock.acquire()
465 aaae9bc0 Guido Trotter
    try:
466 aaae9bc0 Guido Trotter
      invalid_names = self.__names().intersection(names)
467 aaae9bc0 Guido Trotter
      if invalid_names:
468 aaae9bc0 Guido Trotter
        # This must be an explicit raise, not an assert, because assert is
469 aaae9bc0 Guido Trotter
        # turned off when using optimization, and this can happen because of
470 aaae9bc0 Guido Trotter
        # concurrency even if the user doesn't want it.
471 aaae9bc0 Guido Trotter
        raise errors.LockError("duplicate add() (%s)" % invalid_names)
472 aaae9bc0 Guido Trotter
473 aaae9bc0 Guido Trotter
      for lockname in names:
474 aaae9bc0 Guido Trotter
        lock = SharedLock()
475 aaae9bc0 Guido Trotter
476 aaae9bc0 Guido Trotter
        if acquired:
477 aaae9bc0 Guido Trotter
          lock.acquire(shared=shared)
478 aaae9bc0 Guido Trotter
          # now the lock cannot be deleted, we have it!
479 aaae9bc0 Guido Trotter
          try:
480 aaae9bc0 Guido Trotter
            self._add_owned(lockname)
481 aaae9bc0 Guido Trotter
          except:
482 aaae9bc0 Guido Trotter
            # We shouldn't have problems adding the lock to the owners list,
483 aaae9bc0 Guido Trotter
            # but if we did we'll try to release this lock and re-raise
484 aaae9bc0 Guido Trotter
            # exception.  Of course something is going to be really wrong,
485 aaae9bc0 Guido Trotter
            # after this.  On the other hand the lock hasn't been added to the
486 aaae9bc0 Guido Trotter
            # __lockdict yet so no other threads should be pending on it. This
487 aaae9bc0 Guido Trotter
            # release is just a safety measure.
488 aaae9bc0 Guido Trotter
            lock.release()
489 aaae9bc0 Guido Trotter
            raise
490 aaae9bc0 Guido Trotter
491 aaae9bc0 Guido Trotter
        self.__lockdict[lockname] = lock
492 aaae9bc0 Guido Trotter
493 aaae9bc0 Guido Trotter
    finally:
494 aaae9bc0 Guido Trotter
      self.__lock.release()
495 aaae9bc0 Guido Trotter
496 aaae9bc0 Guido Trotter
    return True
497 aaae9bc0 Guido Trotter
498 aaae9bc0 Guido Trotter
  def remove(self, names, blocking=1):
499 aaae9bc0 Guido Trotter
    """Remove elements from the lock set.
500 aaae9bc0 Guido Trotter

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

504 aaae9bc0 Guido Trotter
    Args:
505 aaae9bc0 Guido Trotter
      names: names of the resource to remove.
506 aaae9bc0 Guido Trotter
      blocking: whether to block while trying to acquire or to operate in
507 aaae9bc0 Guido Trotter
                try-lock mode.  this locking mode is not supported yet unless
508 aaae9bc0 Guido Trotter
                you are already holding exclusively the locks.
509 aaae9bc0 Guido Trotter

510 aaae9bc0 Guido Trotter
    Returns:
511 aaae9bc0 Guido Trotter
      A list of lock which we failed to delete. The list is always empty if we
512 aaae9bc0 Guido Trotter
      were holding all the locks exclusively.
513 aaae9bc0 Guido Trotter

514 aaae9bc0 Guido Trotter
    """
515 aaae9bc0 Guido Trotter
    if not blocking and not self._is_owned():
516 aaae9bc0 Guido Trotter
      # We don't have non-blocking mode for now
517 aaae9bc0 Guido Trotter
      raise NotImplementedError
518 aaae9bc0 Guido Trotter
519 aaae9bc0 Guido Trotter
    # Support passing in a single resource to remove rather than many
520 aaae9bc0 Guido Trotter
    if isinstance(names, basestring):
521 aaae9bc0 Guido Trotter
      names = [names]
522 aaae9bc0 Guido Trotter
523 aaae9bc0 Guido Trotter
    # If we own any subset of this lock it must be a superset of what we want
524 aaae9bc0 Guido Trotter
    # to delete. The ownership must also be exclusive, but that will be checked
525 aaae9bc0 Guido Trotter
    # by the lock itself.
526 aaae9bc0 Guido Trotter
    assert not self._is_owned() or self._list_owned().issuperset(names), (
527 aaae9bc0 Guido Trotter
      "remove() on acquired lockset while not owning all elements")
528 aaae9bc0 Guido Trotter
529 aaae9bc0 Guido Trotter
    delete_failed=[]
530 aaae9bc0 Guido Trotter
531 aaae9bc0 Guido Trotter
    for lname in names:
532 aaae9bc0 Guido Trotter
      # Calling delete() acquires the lock exclusively if we don't already own
533 aaae9bc0 Guido Trotter
      # it, and causes all pending and subsequent lock acquires to fail. It's
534 aaae9bc0 Guido Trotter
      # fine to call it out of order because delete() also implies release(),
535 aaae9bc0 Guido Trotter
      # and the assertion above guarantees that if we either already hold
536 aaae9bc0 Guido Trotter
      # everything we want to delete, or we hold none.
537 aaae9bc0 Guido Trotter
      try:
538 aaae9bc0 Guido Trotter
        self.__lockdict[lname].delete()
539 aaae9bc0 Guido Trotter
      except (KeyError, errors.LockError):
540 aaae9bc0 Guido Trotter
        delete_failed.append(lname)
541 aaae9bc0 Guido Trotter
        # This cannot happen if we were already holding it, verify:
542 aaae9bc0 Guido Trotter
        assert not self._is_owned(), "remove failed while holding lockset"
543 aaae9bc0 Guido Trotter
      else:
544 aaae9bc0 Guido Trotter
        # If no LockError was raised we are the ones who deleted the lock.
545 aaae9bc0 Guido Trotter
        # This means we can safely remove it from lockdict, as any further or
546 aaae9bc0 Guido Trotter
        # pending delete() or acquire() will fail (and nobody can have the lock
547 aaae9bc0 Guido Trotter
        # since before our call to delete()).
548 aaae9bc0 Guido Trotter
        #
549 aaae9bc0 Guido Trotter
        # This is done in an else clause because if the exception was thrown
550 aaae9bc0 Guido Trotter
        # it's the job of the one who actually deleted it.
551 aaae9bc0 Guido Trotter
        del self.__lockdict[lname]
552 aaae9bc0 Guido Trotter
        # And let's remove it from our private list if we owned it.
553 aaae9bc0 Guido Trotter
        if self._is_owned():
554 aaae9bc0 Guido Trotter
          self._del_owned(lname)
555 aaae9bc0 Guido Trotter
556 aaae9bc0 Guido Trotter
    return delete_failed