From: <pj...@us...> - 2009-03-31 19:05:02
|
Revision: 6139 http://jython.svn.sourceforge.net/jython/?rev=6139&view=rev Author: pjenvey Date: 2009-03-31 19:04:48 +0000 (Tue, 31 Mar 2009) Log Message: ----------- o don't use custom __hash__'s for internal weakref identity as weakrefs are unique per referent o avoid calling custom __hash__'s until it's necessary incase they're in an indeterminate state pointed out by Mike Bayer o make the internal ref map thread safe Modified Paths: -------------- trunk/jython/src/org/python/modules/_weakref/AbstractReference.java trunk/jython/src/org/python/modules/_weakref/GlobalRef.java Added Paths: ----------- trunk/jython/Lib/test/test_weakref_jy.py Added: trunk/jython/Lib/test/test_weakref_jy.py =================================================================== --- trunk/jython/Lib/test/test_weakref_jy.py (rev 0) +++ trunk/jython/Lib/test/test_weakref_jy.py 2009-03-31 19:04:48 UTC (rev 6139) @@ -0,0 +1,46 @@ +"""Misc weakref tests + +Made for Jython. +""" +import unittest +import weakref +from test import test_support + +class ReferencesTestCase(unittest.TestCase): + + def test___eq__(self): + class Foo(object): + def __eq__(self, other): + return True + def __hash__(self): + return hash('foo') + foo1, foo2 = Foo(), Foo() + ref1, ref2 = weakref.ref(foo1), weakref.ref(foo2) + self.assertTrue(ref1() is foo1) + self.assertTrue(ref2() is foo2) + + def test___hash__call(self): + hash_called = [] + class Bar(object): + def __hash__(self): + hash = object.__hash__(self) + hash_called.append(hash) + return hash + bar = Bar() + ref = weakref.ref(bar) + self.assertFalse(hash_called) + + hash(ref) + self.assertEqual(len(hash_called), 1) + hash(ref) + self.assertEqual(len(hash_called), 1) + self.assertEqual(hash(bar), hash(ref)) + self.assertEqual(len(hash_called), 2) + + +def test_main(): + test_support.run_unittest(ReferencesTestCase) + + +if __name__ == '__main__': + test_main() Modified: trunk/jython/src/org/python/modules/_weakref/AbstractReference.java =================================================================== --- trunk/jython/src/org/python/modules/_weakref/AbstractReference.java 2009-03-31 16:35:51 UTC (rev 6138) +++ trunk/jython/src/org/python/modules/_weakref/AbstractReference.java 2009-03-31 19:04:48 UTC (rev 6139) @@ -42,10 +42,7 @@ } public int hashCode() { - if (gref.realHash) { - return gref.hash; - } - throw Py.TypeError("unhashable instance"); + return gref.pythonHashCode(); } public PyObject __eq__(PyObject other) { Modified: trunk/jython/src/org/python/modules/_weakref/GlobalRef.java =================================================================== --- trunk/jython/src/org/python/modules/_weakref/GlobalRef.java 2009-03-31 16:35:51 UTC (rev 6138) +++ trunk/jython/src/org/python/modules/_weakref/GlobalRef.java 2009-03-31 19:04:48 UTC (rev 6139) @@ -5,29 +5,38 @@ import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.Map; import java.util.List; +import java.util.concurrent.ConcurrentMap; import org.python.core.Py; -import org.python.core.PyException; import org.python.core.PyList; import org.python.core.PyObject; import org.python.util.Generic; public class GlobalRef extends WeakReference { - int hash; + /** + * This reference's hashCode: the System.identityHashCode of the referent. Only used + * internally. + */ + private int hashCode; - /** Whether the hash value was calculated by the underlying object. */ - boolean realHash; + /** + * The public hashCode for the Python AbstractReference wrapper. Derived from the + * referent's hashCode. + */ + private int pythonHashCode; + /** Whether pythonHashCode was already determined. */ + private boolean havePythonHashCode; + private List references = new ArrayList(); private static ReferenceQueue referenceQueue = new ReferenceQueue(); private static RefReaperThread reaperThread; - private static Map<GlobalRef, GlobalRef> objects = Generic.map(); + private static ConcurrentMap<GlobalRef, GlobalRef> objects = Generic.concurrentMap(); static { initReaperThread(); @@ -35,29 +44,9 @@ public GlobalRef(PyObject object) { super(object, referenceQueue); - calcHash(object); + hashCode = System.identityHashCode(object); } - /** - * Calculate a hash code to use for this object. If the PyObject we're - * referencing implements hashCode, we use that value. If not, we use - * System.identityHashCode(refedObject). This allows this object to be - * used in a Map while allowing Python ref objects to tell if the - * hashCode is actually valid for the object. - */ - private void calcHash(PyObject object) { - try { - hash = object.hashCode(); - realHash = true; - } catch (PyException pye) { - if (Py.matchException(pye, Py.TypeError)) { - hash = System.identityHashCode(object); - } else { - throw pye; - } - } - } - public synchronized void add(AbstractReference ref) { Reference r = new WeakReference(ref); references.add(r); @@ -148,16 +137,37 @@ if (t == u) { return true; } - return t.equals(u); + // Don't consult the objects' equals (__eq__) method, it can't be trusted + return false; } /** - * Allow GlobalRef's to be used as hashtable keys. + * Allows GlobalRef to be used as hashtable keys. + * + * @return a hashCode int value */ public int hashCode() { - return hash; + return hashCode; } + /** + * The publicly used hashCode, for the AbstractReference wrapper. + * + * @return a hashCode int value + */ + public int pythonHashCode() { + if (havePythonHashCode) { + return pythonHashCode; + } + Object referent = get(); + if (referent == null) { + throw Py.TypeError("weak object has gone away"); + } + pythonHashCode = referent.hashCode(); + havePythonHashCode = true; + return pythonHashCode; + } + private static void initReaperThread() { reaperThread = new RefReaperThread(); reaperThread.setDaemon(true); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |