This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: threading.RLock exception handling while waiting
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 2.7
process
Status: closed Resolution: out of date
Dependencies: Superseder:
Assigned To: Nosy List: Omer Bartal, iritkatriel, pitrou
Priority: normal Keywords:

Created on 2018-12-03 19:04 by Omer Bartal, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Messages (3)
msg330972 - (view) Author: Omer Bartal (Omer Bartal) Date: 2018-12-03 19:04
(tested on python 2.7)

Created a threading.Condition(threading.RLock())
A piece of code acquires the lock using a with block and waits (for a notify)
While wait() is running a KeyboardInterrupt is raised
An exception is raised while exiting the lock's with block:
  File "/usr/lib/python2.7/threading.py", line 289, in __exit__
    return self.__lock.__exit__(*args)
  File "/usr/lib/python2.7/threading.py", line 216, in __exit__
    self.release()
  File "/usr/lib/python2.7/threading.py", line 204, in release
    raise RuntimeError("cannot release un-acquired lock")

example code running on the main thread:
def waiting(lock): # (the lock was created using Condition(RLock()))
  with lock:
    lock.wait(timeout=xxx) # while waiting a keyboard interrupt is raised

The issue is caused because threading.RLock doesn't handle the exception correctly:
    def _acquire_restore(self, count_owner):
        count, owner = count_owner
        self.__block.acquire()
        self.__count = count
        self.__owner = owner
        if __debug__:
            self._note("%s._acquire_restore()", self)

The exception is raised after the acquire() returns and the count and owner are not restored.

The problem was solved using the following fix (added try, finally):
    def _acquire_restore(self, count_owner):
        count, owner = count_owner
        try:
            self.__block.acquire()
        finally:
            self.__count = count
            self.__owner = owner
            if __debug__:
                self._note("%s._acquire_restore()", self)


Looking at the code, this issue exists in python 3.8 as well.
msg400499 - (view) Author: Irit Katriel (iritkatriel) * (Python committer) Date: 2021-08-28 19:59
Have you reproduced the issue on 3.8?

I am unable to reproduce this on main (on a Mac). Adding Antoine since I think he fixed a few issues in this area.
msg400503 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2021-08-28 21:01
RLock is implemented in C nowadays so this problem doesn't occur anymore:
https://github.com/python/cpython/blob/main/Modules/_threadmodule.c#L436-L459

You can of course, however, import the pure Python RLock under the name "_PyRLock", which is still vulnerable to this issue:
https://github.com/python/cpython/blob/main/Lib/threading.py#L204-L206
History
Date User Action Args
2022-04-11 14:59:08adminsetgithub: 79572
2021-08-28 21:01:34pitrousetstatus: open -> closed
resolution: out of date
stage: resolved
2021-08-28 21:01:13pitrousettype: crash -> behavior
messages: + msg400503
versions: - Python 3.4, Python 3.5, Python 3.6, Python 3.7, Python 3.8
2021-08-28 19:59:44iritkatrielsetnosy: + iritkatriel, pitrou
messages: + msg400499
2018-12-03 19:04:51Omer Bartalcreate