Statistics
| Branch: | Tag: | Revision:

root / lib / locking.py @ 162c1c1f

History | View | Annotate | Download (5.8 kB)

1
#
2
#
3

    
4
# Copyright (C) 2006, 2007 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21
"""Module implementing the Ganeti locking code."""
22

    
23
# pylint: disable-msg=W0613,W0201
24

    
25
import threading
26

    
27

    
28
class SharedLock:
29
  """Implements a shared lock.
30

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

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

39
  """
40
  def __init__(self):
41
    """Construct a new Shared Lock"""
42
    # we have two conditions, c_shr and c_exc, sharing the same lock.
43
    self.__lock = threading.Lock()
44
    self.__turn_shr = threading.Condition(self.__lock)
45
    self.__turn_exc = threading.Condition(self.__lock)
46

    
47
    # current lock holders
48
    self.__shr = set()
49
    self.__exc = None
50

    
51
    # lock waiters
52
    self.__nwait_exc = 0
53
    self.__nwait_shr = 0
54

    
55
  def __is_sharer(self):
56
    """Is the current thread sharing the lock at this time?"""
57
    return threading.currentThread() in self.__shr
58

    
59
  def __is_exclusive(self):
60
    """Is the current thread holding the lock exclusively at this time?"""
61
    return threading.currentThread() == self.__exc
62

    
63
  def __is_owned(self, shared=-1):
64
    """Is the current thread somehow owning the lock at this time?
65

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

69
    """
70
    if shared < 0:
71
      return self.__is_sharer() or self.__is_exclusive()
72
    elif shared:
73
      return self.__is_sharer()
74
    else:
75
      return self.__is_exclusive()
76

    
77
  def _is_owned(self, shared=-1):
78
    """Is the current thread somehow owning the lock at this time?
79

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

86
    """
87
    self.__lock.acquire()
88
    try:
89
      result = self.__is_owned(shared)
90
    finally:
91
      self.__lock.release()
92

    
93
    return result
94

    
95
  def acquire(self, blocking=1, shared=0):
96
    """Acquire a shared lock.
97

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

104
    """
105
    if not blocking:
106
      # We don't have non-blocking mode for now
107
      raise NotImplementedError
108

    
109
    self.__lock.acquire()
110
    try:
111
      # We cannot acquire the lock if we already have it
112
      assert not self.__is_owned(), "double acquire() on a non-recursive lock"
113

    
114
      if shared:
115
        self.__nwait_shr += 1
116
        try:
117
          # If there is an exclusive holder waiting we have to wait.  We'll
118
          # only do this once, though, when we start waiting for the lock. Then
119
          # we'll just wait while there are no exclusive holders.
120
          if self.__nwait_exc > 0:
121
            # TODO: if !blocking...
122
            self.__turn_shr.wait()
123

    
124
          while self.__exc is not None:
125
            # TODO: if !blocking...
126
            self.__turn_shr.wait()
127

    
128
          self.__shr.add(threading.currentThread())
129
        finally:
130
          self.__nwait_shr -= 1
131

    
132
      else:
133
        self.__nwait_exc += 1
134
        try:
135
          # This is to save ourselves from a nasty race condition that could
136
          # theoretically make the sharers starve.
137
          if self.__nwait_shr > 0 or self.__nwait_exc > 1:
138
            # TODO: if !blocking...
139
              self.__turn_exc.wait()
140

    
141
          while len(self.__shr) > 0 or self.__exc is not None:
142
            # TODO: if !blocking...
143
            self.__turn_exc.wait()
144

    
145
          self.__exc = threading.currentThread()
146
        finally:
147
          self.__nwait_exc -= 1
148

    
149
    finally:
150
      self.__lock.release()
151

    
152
    return True
153

    
154
  def release(self):
155
    """Release a Shared Lock.
156

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

160
    """
161
    self.__lock.acquire()
162
    try:
163
      # Autodetect release type
164
      if self.__is_exclusive():
165
        self.__exc = None
166

    
167
        # An exclusive holder has just had the lock, time to put it in shared
168
        # mode if there are shared holders waiting. Otherwise wake up the next
169
        # exclusive holder.
170
        if self.__nwait_shr > 0:
171
          self.__turn_shr.notifyAll()
172
        elif self.__nwait_exc > 0:
173
         self.__turn_exc.notify()
174

    
175
      elif self.__is_sharer():
176
        self.__shr.remove(threading.currentThread())
177

    
178
        # If there are shared holders waiting there *must* be an exclusive holder
179
        # waiting as well; otherwise what were they waiting for?
180
        assert (self.__nwait_shr == 0 or self.__nwait_exc > 0,
181
                "Lock sharers waiting while no exclusive is queueing")
182

    
183
        # If there are no more shared holders and some exclusive holders are
184
        # waiting let's wake one up.
185
        if len(self.__shr) == 0 and self.__nwait_exc > 0:
186
          self.__turn_exc.notify()
187

    
188
      else:
189
        assert False, "Cannot release non-owned lock"
190

    
191
    finally:
192
      self.__lock.release()
193