Skip to content

Question: is the thread_local=False works only when use the same Lock instance? #3540

Open
@shenxiangzhuang

Description

Question: is the thread_local=False works only when use the same Lock instance?
(Here the 'work' means that in thread1 release a lock owned by thread2.)

I'm trying to reproduce the behavior here in the doc:

redis-py/redis/lock.py

Lines 115 to 126 in 2fb2f47

time: 0, thread-1 acquires `my-lock`, with a timeout of 5 seconds.
thread-1 sets the token to "abc"
time: 1, thread-2 blocks trying to acquire `my-lock` using the
Lock instance.
time: 5, thread-1 has not yet completed. redis expires the lock
key.
time: 5, thread-2 acquired `my-lock` now that it's available.
thread-2 sets the token to "xyz"
time: 6, thread-1 finishes its work and calls release(). if the
token is *not* stored in thread local storage, then
thread-1 would see the token value as "xyz" and would be
able to successfully release the thread-2's lock.

Here is my first try(failed):

import redis
import threading
import time


r = redis.Redis()
lock_name = "lock:thread_local:example"


def thread1_function():
    print("Thread 1: Starting")
    lock = r.lock(lock_name, timeout=5, thread_local=False)
    if lock.acquire():
        print(f"Thread 1: Lock token: {lock.local.token}")
        time.sleep(7)
    print(f"Thread 1: Lock token: {lock.local.token}")
    try:
        lock.release()
    except Exception as e:
        print(f"Thread 1: {e}")


def thread2_function():
    print("Thread 2: Starting")
    lock = r.lock(lock_name, thread_local=False)
    lock.acquire()
    print(f"Thread 2: Lock token: {lock.local.token}")
    time.sleep(10)


# Create and start threads
t1 = threading.Thread(target=thread1_function)
t2 = threading.Thread(target=thread2_function)

t1.start()
time.sleep(1)
t2.start()

# Wait for threads to complete
t1.join()
t2.join()

# clean up
r.delete(lock_name)

I got the output:

Thread 1: Starting
Thread 1: Lock token: b'a1431dd2f8e111efb50f2fe1b06b7cb0'
Thread 2: Starting
Thread 2: Lock token: b'a1dbe1b6f8e111efb50f2fe1b06b7cb0'
Thread 1: Lock token: b'a1431dd2f8e111efb50f2fe1b06b7cb0'
Thread 1: Cannot release a lock that's no longer owned

So it seems that I can not release the lock from Thread1 and the reason from error message is clear.

Then, I try to use a global Lock instance, and it works:

import redis
import threading
import time


r = redis.Redis()
lock_name = "lock:thread_local:example"
lock = r.lock(lock_name, timeout=5, thread_local=False)


def thread1_function():
    print("Thread 1: Starting")
    if lock.acquire():
        print(f"Thread 1: Lock token: {lock.local.token}")
        time.sleep(7)
    print(f"Thread 1: Lock token: {lock.local.token}")
    lock.release()
    print("Thread 1: Lock released")


def thread2_function():
    print("Thread 2: Starting")
    print(f"Thread 2: Lock token: {lock.local.token}")
    lock.acquire()
    print(f"Thread 2: Lock token: {lock.local.token}")
    time.sleep(10)


# Create and start threads
t1 = threading.Thread(target=thread1_function)
t2 = threading.Thread(target=thread2_function)

t1.start()
time.sleep(1)
t2.start()

# Wait for threads to complete
t1.join()
t2.join()

# clean up
r.delete(lock_name)

The output:

Thread 1: Starting
Thread 1: Lock token: b'd5aec030f8e111ef8dd2cd2710b884db'
Thread 2: Starting
Thread 2: Lock token: b'd5aec030f8e111ef8dd2cd2710b884db'
Thread 2: Lock token: b'd64788ecf8e111ef8dd2cd2710b884db'
Thread 1: Lock token: b'd64788ecf8e111ef8dd2cd2710b884db'
Thread 1: Lock released

So I have the question at begin. I'm not sure the statement is accurate or not, any help will be appreciated!

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions