Message9834

Author jmadden
Recipients jmadden
Date 2015-04-14.12:37:10
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1429015030.94.0.347815765349.issue2328@psf.upfronthosting.co.za>
In-reply-to
Content
The documentation for `threading.Lock` says (emphasis added):

    A factory function that returns a new primitive lock object. Once a thread has acquired it, subsequent attempts to acquire it block, until it is released; *any thread may release it*.

This is in contrast with `threading.RLock` which specifically says that a "reentrant lock must be released by the thread that acquired it."

Jython uses the same implementation for both types of locks (Lock.java), and it obeys the semantics of `RLock` by requiring that the releasing thread be the acquiring thread, raising an exception if that's not the case. This can cause problems for applications that make advanced use of locks. 

Consider this example in Jython:

  $ ../jython/dist/bin/jython
  Jython 2.7rc2+ (default:5064a5c5b1a3, Apr 14 2015, 06:28:43)
  [Java HotSpot(TM) 64-Bit Server VM (Oracle Corporation)] on java1.8.0_40
  Type "help", "copyright", "credits" or "license" for more information.
  >>> import threading
  >>> threading.Lock is threading.RLock
  True
  >>> lock = threading.Lock()
  >>> lock.acquire()
  True
  >>> t = threading.Thread(target=lock.release)
  >>> t.start(); t.join()
  >>> Exception in thread Thread-1:Traceback (most recent call last):
    File "//jython/dist/Lib/threading.py", line 222, in _Thread__bootstrap
      self.run()
    File "//jython/dist/Lib/threading.py", line 213, in run
      self._target(*self._args, **self._kwargs)
  RuntimeError: cannot release un-acquired lock
  >>> lock.locked()
  True
 
Compare to the CPython and PyPy behaviour:

  $ /opt/local/bin/python2.7
  Python 2.7.9 (default, Dec 13 2014, 15:13:49)
  [GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.56)] on darwin
  Type "help", "copyright", "credits" or "license" for more information.
  >>> import threading
  >>> threading.Lock is threading.RLock
  False
  >>> lock = threading.Lock()
  >>> lock.acquire()
  True
  >>> t = threading.Thread(target=lock.release)
  >>> t.start(); t.join()
  >>> lock.locked()
  False

One real-world example of this is ZODB, at least its test suite. Exceptions like the following are fairly common (fortunately releasing the lock is the last thing the thread was going to do, and then the lock gets destroyed anyway so it's not an immediate issue in this case---the failure has no consequences for the test):

  Exception in thread commit:Traceback (most recent call last):
  File "//jython/dist/Lib/threading.py", line 222, in _Thread__bootstrap
    self.run()
  File "//jython/dist/Lib/threading.py", line 213, in run
    self._target(*self._args, **self._kwargs)
  File "//ZODB/src/ZODB/tests/BasicStorage.py", line 343, in commit
    self._storage.tpc_finish(t, callback)
  File "//ZODB/src/ZODB/utils.py", line 293, in __call__
    return func(*args, **kw)
  File "//ZODB/src/ZODB/DemoStorage.py", line 349, in tpc_finish
    self.changes.tpc_finish(transaction, func)
  File "//ZODB/src/ZODB/utils.py", line 293, in __call__
    return func(*args, **kw)
  File "//ZODB/src/ZODB/MappingStorage.py", line 318, in tpc_finish
    self._commit_lock.release()
  RuntimeError: cannot release un-acquired lock
History
Date User Action Args
2015-04-14 12:37:10jmaddensetrecipients: + jmadden
2015-04-14 12:37:10jmaddensetmessageid: <1429015030.94.0.347815765349.issue2328@psf.upfronthosting.co.za>
2015-04-14 12:37:10jmaddenlinkissue2328 messages
2015-04-14 12:37:10jmaddencreate