Statistics
| Branch: | Tag: | Revision:

root / lib / locking.py @ 6c8af3d0

History | View | Annotate | Download (7.7 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()