Statistics
| Branch: | Tag: | Revision:

root / lib / locking.py @ d6646186

History | View | Annotate | Download (5.8 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 162c1c1f Guido Trotter
27 162c1c1f Guido Trotter
28 162c1c1f Guido Trotter
class SharedLock:
29 162c1c1f Guido Trotter
  """Implements a shared lock.
30 162c1c1f Guido Trotter

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

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

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

66 162c1c1f Guido Trotter
    This is a private version of the function, which presumes you're holding
67 162c1c1f Guido Trotter
    the internal lock.
68 162c1c1f Guido Trotter

69 162c1c1f Guido Trotter
    """
70 162c1c1f Guido Trotter
    if shared < 0:
71 162c1c1f Guido Trotter
      return self.__is_sharer() or self.__is_exclusive()
72 162c1c1f Guido Trotter
    elif shared:
73 162c1c1f Guido Trotter
      return self.__is_sharer()
74 162c1c1f Guido Trotter
    else:
75 162c1c1f Guido Trotter
      return self.__is_exclusive()
76 162c1c1f Guido Trotter
77 162c1c1f Guido Trotter
  def _is_owned(self, shared=-1):
78 162c1c1f Guido Trotter
    """Is the current thread somehow owning the lock at this time?
79 162c1c1f Guido Trotter

80 162c1c1f Guido Trotter
    Args:
81 162c1c1f Guido Trotter
      shared:
82 162c1c1f Guido Trotter
        < 0: check for any type of ownership (default)
83 162c1c1f Guido Trotter
        0: check for exclusive ownership
84 162c1c1f Guido Trotter
        > 0: check for shared ownership
85 162c1c1f Guido Trotter

86 162c1c1f Guido Trotter
    """
87 162c1c1f Guido Trotter
    self.__lock.acquire()
88 162c1c1f Guido Trotter
    try:
89 162c1c1f Guido Trotter
      result = self.__is_owned(shared)
90 162c1c1f Guido Trotter
    finally:
91 162c1c1f Guido Trotter
      self.__lock.release()
92 162c1c1f Guido Trotter
93 162c1c1f Guido Trotter
    return result
94 162c1c1f Guido Trotter
95 162c1c1f Guido Trotter
  def acquire(self, blocking=1, shared=0):
96 162c1c1f Guido Trotter
    """Acquire a shared lock.
97 162c1c1f Guido Trotter

98 162c1c1f Guido Trotter
    Args:
99 162c1c1f Guido Trotter
      shared: whether to acquire in shared mode. By default an exclusive lock
100 162c1c1f Guido Trotter
              will be acquired.
101 162c1c1f Guido Trotter
      blocking: whether to block while trying to acquire or to operate in try-lock mode.
102 162c1c1f Guido Trotter
                this locking mode is not supported yet.
103 162c1c1f Guido Trotter

104 162c1c1f Guido Trotter
    """
105 162c1c1f Guido Trotter
    if not blocking:
106 162c1c1f Guido Trotter
      # We don't have non-blocking mode for now
107 162c1c1f Guido Trotter
      raise NotImplementedError
108 162c1c1f Guido Trotter
109 162c1c1f Guido Trotter
    self.__lock.acquire()
110 162c1c1f Guido Trotter
    try:
111 162c1c1f Guido Trotter
      # We cannot acquire the lock if we already have it
112 162c1c1f Guido Trotter
      assert not self.__is_owned(), "double acquire() on a non-recursive lock"
113 162c1c1f Guido Trotter
114 162c1c1f Guido Trotter
      if shared:
115 162c1c1f Guido Trotter
        self.__nwait_shr += 1
116 162c1c1f Guido Trotter
        try:
117 162c1c1f Guido Trotter
          # If there is an exclusive holder waiting we have to wait.  We'll
118 162c1c1f Guido Trotter
          # only do this once, though, when we start waiting for the lock. Then
119 162c1c1f Guido Trotter
          # we'll just wait while there are no exclusive holders.
120 162c1c1f Guido Trotter
          if self.__nwait_exc > 0:
121 162c1c1f Guido Trotter
            # TODO: if !blocking...
122 162c1c1f Guido Trotter
            self.__turn_shr.wait()
123 162c1c1f Guido Trotter
124 162c1c1f Guido Trotter
          while self.__exc is not None:
125 162c1c1f Guido Trotter
            # TODO: if !blocking...
126 162c1c1f Guido Trotter
            self.__turn_shr.wait()
127 162c1c1f Guido Trotter
128 162c1c1f Guido Trotter
          self.__shr.add(threading.currentThread())
129 162c1c1f Guido Trotter
        finally:
130 162c1c1f Guido Trotter
          self.__nwait_shr -= 1
131 162c1c1f Guido Trotter
132 162c1c1f Guido Trotter
      else:
133 162c1c1f Guido Trotter
        self.__nwait_exc += 1
134 162c1c1f Guido Trotter
        try:
135 162c1c1f Guido Trotter
          # This is to save ourselves from a nasty race condition that could
136 162c1c1f Guido Trotter
          # theoretically make the sharers starve.
137 162c1c1f Guido Trotter
          if self.__nwait_shr > 0 or self.__nwait_exc > 1:
138 162c1c1f Guido Trotter
            # TODO: if !blocking...
139 162c1c1f Guido Trotter
              self.__turn_exc.wait()
140 162c1c1f Guido Trotter
141 162c1c1f Guido Trotter
          while len(self.__shr) > 0 or self.__exc is not None:
142 162c1c1f Guido Trotter
            # TODO: if !blocking...
143 162c1c1f Guido Trotter
            self.__turn_exc.wait()
144 162c1c1f Guido Trotter
145 162c1c1f Guido Trotter
          self.__exc = threading.currentThread()
146 162c1c1f Guido Trotter
        finally:
147 162c1c1f Guido Trotter
          self.__nwait_exc -= 1
148 162c1c1f Guido Trotter
149 162c1c1f Guido Trotter
    finally:
150 162c1c1f Guido Trotter
      self.__lock.release()
151 162c1c1f Guido Trotter
152 162c1c1f Guido Trotter
    return True
153 162c1c1f Guido Trotter
154 162c1c1f Guido Trotter
  def release(self):
155 162c1c1f Guido Trotter
    """Release a Shared Lock.
156 162c1c1f Guido Trotter

157 162c1c1f Guido Trotter
    You must have acquired the lock, either in shared or in exclusive mode,
158 162c1c1f Guido Trotter
    before calling this function.
159 162c1c1f Guido Trotter

160 162c1c1f Guido Trotter
    """
161 162c1c1f Guido Trotter
    self.__lock.acquire()
162 162c1c1f Guido Trotter
    try:
163 162c1c1f Guido Trotter
      # Autodetect release type
164 162c1c1f Guido Trotter
      if self.__is_exclusive():
165 162c1c1f Guido Trotter
        self.__exc = None
166 162c1c1f Guido Trotter
167 162c1c1f Guido Trotter
        # An exclusive holder has just had the lock, time to put it in shared
168 162c1c1f Guido Trotter
        # mode if there are shared holders waiting. Otherwise wake up the next
169 162c1c1f Guido Trotter
        # exclusive holder.
170 162c1c1f Guido Trotter
        if self.__nwait_shr > 0:
171 162c1c1f Guido Trotter
          self.__turn_shr.notifyAll()
172 162c1c1f Guido Trotter
        elif self.__nwait_exc > 0:
173 162c1c1f Guido Trotter
         self.__turn_exc.notify()
174 162c1c1f Guido Trotter
175 162c1c1f Guido Trotter
      elif self.__is_sharer():
176 162c1c1f Guido Trotter
        self.__shr.remove(threading.currentThread())
177 162c1c1f Guido Trotter
178 162c1c1f Guido Trotter
        # If there are shared holders waiting there *must* be an exclusive holder
179 162c1c1f Guido Trotter
        # waiting as well; otherwise what were they waiting for?
180 162c1c1f Guido Trotter
        assert (self.__nwait_shr == 0 or self.__nwait_exc > 0,
181 162c1c1f Guido Trotter
                "Lock sharers waiting while no exclusive is queueing")
182 162c1c1f Guido Trotter
183 162c1c1f Guido Trotter
        # If there are no more shared holders and some exclusive holders are
184 162c1c1f Guido Trotter
        # waiting let's wake one up.
185 162c1c1f Guido Trotter
        if len(self.__shr) == 0 and self.__nwait_exc > 0:
186 162c1c1f Guido Trotter
          self.__turn_exc.notify()
187 162c1c1f Guido Trotter
188 162c1c1f Guido Trotter
      else:
189 162c1c1f Guido Trotter
        assert False, "Cannot release non-owned lock"
190 162c1c1f Guido Trotter
191 162c1c1f Guido Trotter
    finally:
192 162c1c1f Guido Trotter
      self.__lock.release()