Revision a95c53ea lib/locking.py
b/lib/locking.py | ||
---|---|---|
53 | 53 |
|
54 | 54 |
# Internal lock acquisition modes for L{LockSet} |
55 | 55 |
(_LS_ACQUIRE_EXACT, |
56 |
_LS_ACQUIRE_ALL) = range(1, 3) |
|
56 |
_LS_ACQUIRE_ALL, |
|
57 |
_LS_ACQUIRE_OPPORTUNISTIC) = range(1, 4) |
|
58 |
|
|
59 |
_LS_ACQUIRE_MODES = frozenset([ |
|
60 |
_LS_ACQUIRE_EXACT, |
|
61 |
_LS_ACQUIRE_ALL, |
|
62 |
_LS_ACQUIRE_OPPORTUNISTIC, |
|
63 |
]) |
|
57 | 64 |
|
58 | 65 |
|
59 | 66 |
def ssynchronized(mylock, shared=0): |
... | ... | |
917 | 924 |
ALL_SET = None |
918 | 925 |
|
919 | 926 |
|
927 |
def _TimeoutZero(): |
|
928 |
"""Returns the number zero. |
|
929 |
|
|
930 |
""" |
|
931 |
return 0 |
|
932 |
|
|
933 |
|
|
934 |
def _GetLsAcquireModeAndTimeouts(want_all, timeout, opportunistic): |
|
935 |
"""Determines modes and timeouts for L{LockSet.acquire}. |
|
936 |
|
|
937 |
@type want_all: boolean |
|
938 |
@param want_all: Whether all locks in set should be acquired |
|
939 |
@param timeout: Timeout in seconds or C{None} |
|
940 |
@param opportunistic: Whther locks should be acquired opportunistically |
|
941 |
@rtype: tuple |
|
942 |
@return: Tuple containing mode to be passed to L{LockSet.__acquire_inner} |
|
943 |
(one of L{_LS_ACQUIRE_MODES}), a function to calculate timeout for |
|
944 |
acquiring the lockset-internal lock (might be C{None}) and a function to |
|
945 |
calculate the timeout for acquiring individual locks |
|
946 |
|
|
947 |
""" |
|
948 |
# Short circuit when no running timeout is needed |
|
949 |
if opportunistic and not want_all: |
|
950 |
assert timeout is None, "Got timeout for an opportunistic acquisition" |
|
951 |
return (_LS_ACQUIRE_OPPORTUNISTIC, None, _TimeoutZero) |
|
952 |
|
|
953 |
# We need to keep track of how long we spent waiting for a lock. The |
|
954 |
# timeout passed to this function is over all lock acquisitions. |
|
955 |
running_timeout = utils.RunningTimeout(timeout, False) |
|
956 |
|
|
957 |
if want_all: |
|
958 |
mode = _LS_ACQUIRE_ALL |
|
959 |
ls_timeout_fn = running_timeout.Remaining |
|
960 |
else: |
|
961 |
mode = _LS_ACQUIRE_EXACT |
|
962 |
ls_timeout_fn = None |
|
963 |
|
|
964 |
if opportunistic: |
|
965 |
mode = _LS_ACQUIRE_OPPORTUNISTIC |
|
966 |
timeout_fn = _TimeoutZero |
|
967 |
else: |
|
968 |
timeout_fn = running_timeout.Remaining |
|
969 |
|
|
970 |
return (mode, ls_timeout_fn, timeout_fn) |
|
971 |
|
|
972 |
|
|
920 | 973 |
class _AcquireTimeout(Exception): |
921 | 974 |
"""Internal exception to abort an acquire on a timeout. |
922 | 975 |
|
... | ... | |
1114 | 1167 |
return set(result) |
1115 | 1168 |
|
1116 | 1169 |
def acquire(self, names, timeout=None, shared=0, priority=None, |
1117 |
test_notify=None): |
|
1170 |
opportunistic=False, test_notify=None):
|
|
1118 | 1171 |
"""Acquire a set of resource locks. |
1119 | 1172 |
|
1173 |
@note: When acquiring locks opportunistically, any number of locks might |
|
1174 |
actually be acquired, even zero. |
|
1175 |
|
|
1120 | 1176 |
@type names: list of strings (or string) |
1121 | 1177 |
@param names: the names of the locks which shall be acquired |
1122 | 1178 |
(special lock names, or instance/node names) |
... | ... | |
1124 | 1180 |
@param shared: whether to acquire in shared mode; by default an |
1125 | 1181 |
exclusive lock will be acquired |
1126 | 1182 |
@type timeout: float or None |
1127 |
@param timeout: Maximum time to acquire all locks |
|
1183 |
@param timeout: Maximum time to acquire all locks; for opportunistic |
|
1184 |
acquisitions, a timeout can only be given when C{names} is C{None}, in |
|
1185 |
which case it is exclusively used for acquiring the L{LockSet}-internal |
|
1186 |
lock; opportunistic acquisitions don't use a timeout for acquiring |
|
1187 |
individual locks |
|
1128 | 1188 |
@type priority: integer |
1129 | 1189 |
@param priority: Priority for acquiring locks |
1190 |
@type opportunistic: boolean |
|
1191 |
@param opportunistic: Acquire locks opportunistically; use the return value |
|
1192 |
to determine which locks were actually acquired |
|
1130 | 1193 |
@type test_notify: callable or None |
1131 | 1194 |
@param test_notify: Special callback function for unittesting |
1132 | 1195 |
|
... | ... | |
1146 | 1209 |
if priority is None: |
1147 | 1210 |
priority = _DEFAULT_PRIORITY |
1148 | 1211 |
|
1149 |
# We need to keep track of how long we spent waiting for a lock. The |
|
1150 |
# timeout passed to this function is over all lock acquires. |
|
1151 |
running_timeout = utils.RunningTimeout(timeout, False) |
|
1152 |
|
|
1153 | 1212 |
try: |
1154 | 1213 |
if names is not None: |
1214 |
assert timeout is None or not opportunistic, \ |
|
1215 |
("Opportunistic acquisitions can only use a timeout if no" |
|
1216 |
" names are given; see docstring for details") |
|
1217 |
|
|
1155 | 1218 |
# Support passing in a single resource to acquire rather than many |
1156 | 1219 |
if isinstance(names, basestring): |
1157 | 1220 |
names = [names] |
1158 | 1221 |
|
1159 |
return self.__acquire_inner(names, _LS_ACQUIRE_EXACT, shared, priority, |
|
1160 |
running_timeout.Remaining, test_notify) |
|
1222 |
(mode, _, timeout_fn) = \ |
|
1223 |
_GetLsAcquireModeAndTimeouts(False, timeout, opportunistic) |
|
1224 |
|
|
1225 |
return self.__acquire_inner(names, mode, shared, priority, |
|
1226 |
timeout_fn, test_notify) |
|
1161 | 1227 |
|
1162 | 1228 |
else: |
1229 |
(mode, ls_timeout_fn, timeout_fn) = \ |
|
1230 |
_GetLsAcquireModeAndTimeouts(True, timeout, opportunistic) |
|
1231 |
|
|
1163 | 1232 |
# If no names are given acquire the whole set by not letting new names |
1164 | 1233 |
# being added before we release, and getting the current list of names. |
1165 | 1234 |
# Some of them may then be deleted later, but we'll cope with this. |
... | ... | |
1170 | 1239 |
# anyway, though, so we'll get the list lock exclusively as well in |
1171 | 1240 |
# order to be able to do add() on the set while owning it. |
1172 | 1241 |
if not self.__lock.acquire(shared=shared, priority=priority, |
1173 |
timeout=running_timeout.Remaining()):
|
|
1242 |
timeout=ls_timeout_fn()):
|
|
1174 | 1243 |
raise _AcquireTimeout() |
1244 |
|
|
1175 | 1245 |
try: |
1176 | 1246 |
# note we own the set-lock |
1177 | 1247 |
self._add_owned() |
1178 | 1248 |
|
1179 |
return self.__acquire_inner(self.__names(), _LS_ACQUIRE_ALL, shared, |
|
1180 |
priority, running_timeout.Remaining, |
|
1181 |
test_notify) |
|
1249 |
return self.__acquire_inner(self.__names(), mode, shared, |
|
1250 |
priority, timeout_fn, test_notify) |
|
1182 | 1251 |
except: |
1183 | 1252 |
# We shouldn't have problems adding the lock to the owners list, but |
1184 | 1253 |
# if we did we'll try to release this lock and re-raise exception. |
... | ... | |
1194 | 1263 |
timeout_fn, test_notify): |
1195 | 1264 |
"""Inner logic for acquiring a number of locks. |
1196 | 1265 |
|
1266 |
Acquisition modes: |
|
1267 |
|
|
1268 |
- C{_LS_ACQUIRE_ALL}: C{names} contains names of all locks in set, but |
|
1269 |
deleted locks can be ignored as the whole set is being acquired with |
|
1270 |
its internal lock held |
|
1271 |
- C{_LS_ACQUIRE_EXACT}: The names listed in C{names} must be acquired; |
|
1272 |
timeouts and deleted locks are fatal |
|
1273 |
- C{_LS_ACQUIRE_OPPORTUNISTIC}: C{names} lists names of locks (potentially |
|
1274 |
all within the set) which should be acquired opportunistically, that is |
|
1275 |
failures are ignored |
|
1276 |
|
|
1197 | 1277 |
@param names: Names of the locks to be acquired |
1198 |
@param mode: Lock acquisition mode |
|
1278 |
@param mode: Lock acquisition mode (one of L{_LS_ACQUIRE_MODES})
|
|
1199 | 1279 |
@param shared: Whether to acquire in shared mode |
1200 |
@param timeout_fn: Function returning remaining timeout |
|
1280 |
@param timeout_fn: Function returning remaining timeout (C{None} for |
|
1281 |
opportunistic acquisitions) |
|
1201 | 1282 |
@param priority: Priority for acquiring locks |
1202 | 1283 |
@param test_notify: Special callback function for unittesting |
1203 | 1284 |
|
1204 | 1285 |
""" |
1205 |
assert mode in (_LS_ACQUIRE_EXACT, _LS_ACQUIRE_ALL)
|
|
1286 |
assert mode in _LS_ACQUIRE_MODES
|
|
1206 | 1287 |
|
1207 | 1288 |
acquire_list = [] |
1208 | 1289 |
|
... | ... | |
1246 | 1327 |
priority=priority, |
1247 | 1328 |
test_notify=test_notify_fn) |
1248 | 1329 |
except errors.LockError: |
1249 |
if mode == _LS_ACQUIRE_ALL:
|
|
1330 |
if mode in (_LS_ACQUIRE_ALL, _LS_ACQUIRE_OPPORTUNISTIC):
|
|
1250 | 1331 |
# We are acquiring the whole set, it doesn't matter if this |
1251 | 1332 |
# particular element is not there anymore. |
1252 | 1333 |
continue |
... | ... | |
1256 | 1337 |
|
1257 | 1338 |
if not acq_success: |
1258 | 1339 |
# Couldn't get lock or timeout occurred |
1340 |
if mode == _LS_ACQUIRE_OPPORTUNISTIC: |
|
1341 |
# Ignore timeouts on opportunistic acquisitions |
|
1342 |
continue |
|
1343 |
|
|
1259 | 1344 |
if timeout is None: |
1260 | 1345 |
# This shouldn't happen as SharedLock.acquire(timeout=None) is |
1261 | 1346 |
# blocking. |
Also available in: Unified diff