mirror of https://github.com/borgbackup/borg.git
UpgradableLock: release exclusive lock in case of exceptions
also: add some comments about how to use the locks in the safest way
This commit is contained in:
parent
f19e95fcf7
commit
1093894be8
|
@ -99,7 +99,14 @@ class NotMyLock(LockErrorT):
|
||||||
|
|
||||||
|
|
||||||
class ExclusiveLock:
|
class ExclusiveLock:
|
||||||
"""An exclusive Lock based on mkdir fs operation being atomic"""
|
"""An exclusive Lock based on mkdir fs operation being atomic.
|
||||||
|
|
||||||
|
If possible, try to use the contextmanager here like:
|
||||||
|
with ExclusiveLock(...) as lock:
|
||||||
|
...
|
||||||
|
This makes sure the lock is released again if the block is left, no
|
||||||
|
matter how (e.g. if an exception occurred).
|
||||||
|
"""
|
||||||
def __init__(self, path, timeout=None, sleep=None, id=None):
|
def __init__(self, path, timeout=None, sleep=None, id=None):
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
self.sleep = sleep
|
self.sleep = sleep
|
||||||
|
@ -220,6 +227,12 @@ class UpgradableLock:
|
||||||
Typically, write access to a resource needs an exclusive lock (1 writer,
|
Typically, write access to a resource needs an exclusive lock (1 writer,
|
||||||
noone is allowed reading) and read access to a resource needs a shared
|
noone is allowed reading) and read access to a resource needs a shared
|
||||||
lock (multiple readers are allowed).
|
lock (multiple readers are allowed).
|
||||||
|
|
||||||
|
If possible, try to use the contextmanager here like:
|
||||||
|
with UpgradableLock(...) as lock:
|
||||||
|
...
|
||||||
|
This makes sure the lock is released again if the block is left, no
|
||||||
|
matter how (e.g. if an exception occurred).
|
||||||
"""
|
"""
|
||||||
def __init__(self, path, exclusive=False, sleep=None, timeout=None, id=None):
|
def __init__(self, path, exclusive=False, sleep=None, timeout=None, id=None):
|
||||||
self.path = path
|
self.path = path
|
||||||
|
@ -262,12 +275,18 @@ class UpgradableLock:
|
||||||
timer = TimeoutTimer(self.timeout, sleep).start()
|
timer = TimeoutTimer(self.timeout, sleep).start()
|
||||||
while True:
|
while True:
|
||||||
self._lock.acquire()
|
self._lock.acquire()
|
||||||
if remove is not None:
|
try:
|
||||||
self._roster.modify(remove, REMOVE)
|
if remove is not None:
|
||||||
remove = None
|
self._roster.modify(remove, REMOVE)
|
||||||
if len(self._roster.get(SHARED)) == 0:
|
remove = None
|
||||||
return # we are the only one and we keep the lock!
|
if len(self._roster.get(SHARED)) == 0:
|
||||||
self._lock.release()
|
return # we are the only one and we keep the lock!
|
||||||
|
except:
|
||||||
|
# avoid orphan lock when an exception happens here, e.g. Ctrl-C!
|
||||||
|
self._lock.release()
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
self._lock.release()
|
||||||
if timer.timed_out_or_sleep():
|
if timer.timed_out_or_sleep():
|
||||||
raise LockTimeout(self.path)
|
raise LockTimeout(self.path)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue