Skip to content

Conversation

@devbyteai
Copy link

Summary

Fixes #3847 where cancelling release() during execution could leave the async lock in an inconsistent deadlock state.

Root Cause

The release() method cleared self.local.token synchronously before returning the awaitable do_release(). If the caller cancelled the await, the token was already gone but the Redis key still existed, causing:

  1. lock.owned() returns False (no token)
  2. lock.locked() returns True (Redis key exists)
  3. Lock cannot be released or acquired - permanent deadlock

Solution

Convert release() to async and clear token only AFTER successful release.

Edge Cases Handled

  1. Normal release - clear token after success
  2. LockNotOwnedError - clear token (lock gone from Redis)
  3. Network error - preserve token (lock might still exist)
  4. CancelledError - preserve token (release didn't complete)

Testing

  1. Added test_release_cancellation_preserves_lock_state regression test
  2. All 60 existing async lock tests pass
  3. Tested on local Redis instance

Backward Compatibility

  1. All existing callers already use await lock.release() - no changes needed
  2. Return type effectively unchanged (awaitable that returns None)

Fixes redis#3847 where cancelling release() during execution could leave
the async lock in an inconsistent deadlock state.

Root cause:
The release() method cleared self.local.token synchronously before
returning the awaitable do_release(). If the caller cancelled the await,
the token was already gone but the Redis key still existed, causing:
- lock.owned() returns False (no token)
- lock.locked() returns True (Redis key exists)
- Lock cannot be released or acquired - permanent deadlock

Solution:
Convert release() to async and clear token only AFTER successful release.
Special case: Also clear token on LockNotOwnedError since the lock
doesn't exist in Redis anyway.

Edge cases handled:
1. Normal release: clear token after success
2. LockNotOwnedError: clear token (lock gone from Redis)
3. Network error: preserve token (lock might still exist)
4. CancelledError: preserve token (release didn't complete)

Testing:
- Added test_release_cancellation_preserves_lock_state regression test
- All 60 existing async lock tests pass

Signed-off-by: Devbyteai <[email protected]>
@jit-ci
Copy link

jit-ci bot commented Dec 30, 2025

Hi, I’m Jit, a friendly security platform designed to help developers build secure applications from day zero with an MVS (Minimal viable security) mindset.

In case there are security findings, they will be communicated to you as a comment inside the PR.

Hope you’ll enjoy using Jit.

Questions? Comments? Want to learn more? Get in touch with us.

@petyaslavova
Copy link
Collaborator

Hi @devbyteai, thank you for your contribution! We will review it soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Async lock can enter a deadlock if it is cancelled while being released

2 participants