diff --git a/borg/locking.py b/borg/locking.py index 3a88d1504..1607d21fb 100644 --- a/borg/locking.py +++ b/borg/locking.py @@ -299,6 +299,8 @@ def release(self): self._roster.modify(SHARED, REMOVE) def upgrade(self): + # WARNING: if multiple read-lockers want to upgrade, it will deadlock because they + # all will wait until the other read locks go away - and that won't happen. if not self.is_exclusive: self.acquire(exclusive=True, remove=SHARED) @@ -306,6 +308,9 @@ def downgrade(self): if self.is_exclusive: self.acquire(exclusive=False, remove=EXCLUSIVE) + def got_exclusive_lock(self): + return self.is_exclusive and self._lock.is_locked() and self._lock.by_me() + def break_lock(self): self._roster.remove() self._lock.break_lock() diff --git a/borg/testsuite/locking.py b/borg/testsuite/locking.py index bc62650db..b219e98b1 100644 --- a/borg/testsuite/locking.py +++ b/borg/testsuite/locking.py @@ -86,6 +86,14 @@ def test_downgrade(self, lockpath): assert len(lock._roster.get(SHARED)) == 1 assert len(lock._roster.get(EXCLUSIVE)) == 0 + def test_got_exclusive_lock(self, lockpath): + lock = UpgradableLock(lockpath, exclusive=True, id=ID1) + assert not lock.got_exclusive_lock() + lock.acquire() + assert lock.got_exclusive_lock() + lock.release() + assert not lock.got_exclusive_lock() + def test_break(self, lockpath): lock = UpgradableLock(lockpath, exclusive=True, id=ID1).acquire() lock.break_lock()