Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

move Lock.token attribute into thread-local storage #491

Merged
merged 2 commits into from
Jun 6, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions redis/lock.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import threading
import time as mod_time
import uuid
from redis.exceptions import LockError, WatchError
Expand Down Expand Up @@ -44,7 +45,8 @@ def __init__(self, redis, name, timeout=None, sleep=0.1,
self.sleep = sleep
self.blocking = blocking
self.blocking_timeout = blocking_timeout
self.token = None
self.local = threading.local()
self.local.token = None
if self.timeout and self.sleep > self.timeout:
raise LockError("'sleep' must be less than 'timeout'")

Expand Down Expand Up @@ -79,7 +81,7 @@ def acquire(self, blocking=None, blocking_timeout=None):
stop_trying_at = mod_time.time() + self.blocking_timeout
while 1:
if self.do_acquire(token):
self.token = token
self.local.token = token
return True
if not blocking:
return False
Expand All @@ -98,10 +100,10 @@ def do_acquire(self, token):

def release(self):
"Releases the already acquired lock"
if self.token is None:
expected_token = self.local.token
if expected_token is None:
raise LockError("Cannot release an unlocked lock")
expected_token = self.token
self.token = None
self.local.token = None
self.do_release(expected_token)

def do_release(self, expected_token):
Expand All @@ -122,7 +124,7 @@ def extend(self, additional_time):
``additional_time`` can be specified as an integer or a float, both
representing the number of seconds to add.
"""
if self.token is None:
if self.local.token is None:
raise LockError("Cannot extend an unlocked lock")
if self.timeout is None:
raise LockError("Cannot extend a lock with no timeout")
Expand All @@ -132,7 +134,7 @@ def do_extend(self, additional_time):
pipe = self.redis.pipeline()
pipe.watch(self.name)
lock_value = pipe.get(self.name)
if lock_value != self.token:
if lock_value != self.local.token:
raise LockError("Cannot extend a lock that's no longer owned")
expiration = pipe.pttl(self.name)
if expiration is None or expiration < 0:
Expand Down Expand Up @@ -236,7 +238,7 @@ def do_release(self, expected_token):
def do_extend(self, additional_time):
additional_time = int(additional_time * 1000)
if not bool(self.lua_extend(keys=[self.name],
args=[self.token, additional_time],
args=[self.local.token, additional_time],
client=self.redis)):
raise LockError("Cannot extend a lock that's no longer owned")
return True
6 changes: 3 additions & 3 deletions tests/test_lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def get_lock(self, redis, *args, **kwargs):
def test_lock(self, sr):
lock = self.get_lock(sr, 'foo')
assert lock.acquire(blocking=False)
assert sr.get('foo') == lock.token
assert sr.get('foo') == lock.local.token
assert sr.ttl('foo') == -1
lock.release()
assert sr.get('foo') is None
Expand Down Expand Up @@ -56,7 +56,7 @@ def test_context_manager(self, sr):
# blocking_timeout prevents a deadlock if the lock can't be acquired
# for some reason
with self.get_lock(sr, 'foo', blocking_timeout=0.2) as lock:
assert sr.get('foo') == lock.token
assert sr.get('foo') == lock.local.token
assert sr.get('foo') is None

def test_high_sleep_raises_error(self, sr):
Expand All @@ -77,7 +77,7 @@ def test_releasing_lock_no_longer_owned_raises_error(self, sr):
with pytest.raises(LockError):
lock.release()
# even though we errored, the token is still cleared
assert lock.token is None
assert lock.local.token is None

def test_extend_lock(self, sr):
lock = self.get_lock(sr, 'foo', timeout=10)
Expand Down