blob: 59b23a89cd74a893ffef0663e6864378368e01b3 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
'''
multi_lock.py - this file is part of S3QL.
Copyright © 2008 Nikolaus Rath <Nikolaus@rath.org>
This work can be distributed under the terms of the GNU GPLv3.
'''
import threading
import logging
from contextlib import contextmanager
__all__ = [ "MultiLock" ]
log = logging.getLogger(__name__)
class MultiLock:
"""Provides locking for multiple objects.
This class provides locking for a dynamically changing set of objects: The
`acquire` and `release` methods have an additional argument, the locking
key. Only locks with the same key can actually see each other, so that
several threads can hold locks with different locking keys at the same time.
MultiLock instances can be used as context managers.
Note that it is actually possible for one thread to release a lock that has
been obtained by a different thread. This is not a bug but a feature.
"""
def __init__(self):
self.locked_keys = set()
self.cond = threading.Condition(threading.Lock())
@contextmanager
def __call__(self, *key):
self.acquire(*key)
try:
yield
finally:
self.release(*key)
def acquire(self, *key, timeout=None):
'''Acquire lock for given key
If timeout is exceeded, return False. Otherwise return True.
'''
with self.cond:
if not self.cond.wait_for(lambda: key not in self.locked_keys, timeout):
return False
self.locked_keys.add(key)
def release(self, *key, noerror=False):
"""Release lock on given key
If noerror is False, do not raise exception if *key* is
not locked.
"""
with self.cond:
if noerror:
self.locked_keys.discard(key)
else:
self.locked_keys.remove(key)
self.cond.notifyAll()
|