forked from doctaphred/phrecipes
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlocksmith.py
92 lines (74 loc) · 2.63 KB
/
locksmith.py
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import time
class Locksmith:
"""Class which creates and distributes locks.
Not thread safe. Best served asynchronously, via asyncio or message
passing.
"""
def __init__(self, default_duration=1):
self._locks = {}
self.default_duration = default_duration
@classmethod
def _expired(cls, lock, timestamp=...):
if timestamp is ...:
timestamp = time.time()
return timestamp > lock['timestamp'] + lock['duration']
def _available(self, lock_id, requester_id, timestamp=...):
"""Check whether the lock is available to the requester.
Returns True if the lock is unassigned, expired, or already
assigned to the requester; False otherwise.
Because of race conditions, this method is probably not useful
externally. Instead, try `acquire` or `release` and check the
return value.
"""
try:
lock = self._locks[lock_id]
except KeyError:
return True
if lock['owner'] == requester_id:
return True
if timestamp is ...:
timestamp = time.time()
return self._expired(lock, timestamp)
def acquire(self, lock_id, requester_id, duration=...):
"""Attempt to assign a lock to a requester.
If the lock is available, re-assigns it to the requester and
returns True; otherwise, returns False.
"""
now = time.time()
if self.available(lock_id, requester_id, now):
if duration is ...:
duration = self.default_duration
self._locks[lock_id] = {
'owner': requester_id,
'timestamp': now,
'duration': duration,
}
return True
else:
return False
def release(self, lock_id, owner_id):
"""Attempt to release a lock.
If the lock previously belonged to the owner and was not already
expired, releases the lock and returns True; otherwise, returns
False.
"""
try:
lock = self._locks[lock_id]
except KeyError:
return False
if lock['owner'] != owner_id:
return False
# Do this first to avoid race conditions
expired = self._expired(lock)
# Not strictly necessary if it's expired, but may as well
del self._locks[lock_id]
return not expired
if __name__ == '__main__':
from zmqrpc import serve
smith = Locksmith()
procs = {
'CHECK': smith.check,
'ACQUIRE': smith.acquire,
'RELEASE': smith.release,
}
serve(procs, 9001, debug=True)