Issue2326

classification
Title: RuntimeError: WeakValueDictionary.valuerefs should not use self.itervalues
Type: Severity: normal
Components: Library Versions: Jython 2.7
Milestone:
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: jmadden, zyasoft
Priority: Keywords:

Created on 2015-04-14.02:10:04 by jmadden, last changed 2015-04-14.14:41:36 by zyasoft.

Messages
msg9824 (view) Author: Jason Madden (jmadden) Date: 2015-04-14.02:10:03
Currently, WeakValueDictionary.valuerefs looks like this:

 return [ref(value) for value in self.itervalues()]

But, as the CPython documentation says:

   Caution: Because a WeakKeyDictionary is built on top of a Python dictionary, it must not change size when iterating over it. This can be difficult to ensure for a WeakKeyDictionary because actions performed by the program during iteration may cause items in the dictionary to vanish “by magic” (as a side effect of garbage collection).

This is enforced by the Jython dictionary implementation, and together with of having Java's truly non-deterministic GC means that using itervalues is extremely dangerous. Practically speaking, this makes `valuerefs` impossible to use.

This came up in porting the widely used `transaction` package to work on Jython[1]. When the system is put under any strain, a RuntimeError results from this method. Here's an example from running the ZODB test suite; changing `itervalues` to `values` solves the problem:

      File "<doctest ZODB.tests.testblob.loadblob_tmpstore[12]>", line 1, in <module>
        database.close()
      File "//ZODB/src/ZODB/DB.py", line 629, in close
        @self._connectionMap
      File "//ZODB/src/ZODB/DB.py", line 505, in _connectionMap
        self.pool.map(f)
      File "//ZODB/src/ZODB/DB.py", line 205, in map
        self.all.map(f)
      File "//transaction-1.4.4.dev0-py2.7.egg/transaction/weakset.py", line 63, in map
        f(elt)
      File "//ZODB/src/ZODB/DB.py", line 631, in _
        c.transaction_manager.abort()
      File "//transaction-1.4.4.dev0-py2.7.egg/transaction/_manager.py", line 116, in abort
        return self.get().abort()
      File "//transaction-1.4.4.dev0-py2.7.egg/transaction/_transaction.py", line 443, in abort
        self._synchronizers.map(lambda s: s.beforeCompletion(self))
      File "//transaction-1.4.4.dev0-py2.7.egg/transaction/weakset.py", line 60, in map
        for wr in self.as_weakref_list():
      File "//transaction-1.4.4.dev0-py2.7.egg/transaction/weakset.py", line 87, in as_weakref_list
        refs = self.data.valuerefs()
      File "//jython2.7rc2/Lib/weakref.py", line 69, in valuerefs
        return [ref(value) for value in self.itervalues()]
    RuntimeError: dictionary changed size during iteration

The same reasoning for `WeakKeyDictionary.keyrefs`

[1] https://github.com/zopefoundation/transaction/pull/8
msg9826 (view) Author: Jim Baker (zyasoft) Date: 2015-04-14.02:54:01
Ack, we should relax this check in PyDictionary, this is unnecessary in general given concurrent modification support in ConcurrentHashMap. Something similar was already changed in PySet.
msg9827 (view) Author: Jim Baker (zyasoft) Date: 2015-04-14.02:55:46
The other unfortunate thing here is that this test

            if (getMap().size() != size) {
                throw Py.RuntimeError("dictionary changed size during iteration");
            }

is not even correct on weak maps in Guava, given eventual consistency.
msg9828 (view) Author: Jim Baker (zyasoft) Date: 2015-04-14.03:18:17
Should be fixed as of https://hg.python.org/jython/rev/5064a5c5b1a3, so please verify with your testing
msg9833 (view) Author: Jason Madden (jmadden) Date: 2015-04-14.11:44:45
With that commit, the ZODB tests run clean. Seems fixed to me.
History
Date User Action Args
2015-04-14 14:41:36zyasoftsetstatus: pending -> closed
2015-04-14 11:44:45jmaddensetmessages: + msg9833
2015-04-14 03:18:17zyasoftsetstatus: open -> pending
resolution: fixed
messages: + msg9828
2015-04-14 02:55:47zyasoftsetmessages: + msg9827
2015-04-14 02:54:01zyasoftsetnosy: + zyasoft
messages: + msg9826
2015-04-14 02:10:04jmaddencreate