Skip to content

Commit ddf93ae

Browse files
committed
Added necessary hooks for weak reference-support in JyNI.
1 parent 561ee3d commit ddf93ae

11 files changed

Lines changed: 144 additions & 41 deletions

src/org/python/modules/_weakref/AbstractReference.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ public abstract class AbstractReference extends PyObject implements Traverseproc
1616

1717
PyObject callback;
1818

19-
protected GlobalRef gref;
19+
protected ReferenceBackend gref;
2020

21-
public AbstractReference(PyType subType, GlobalRef gref, PyObject callback) {
21+
public AbstractReference(PyType subType, ReferenceBackend gref, PyObject callback) {
2222
super(subType);
2323
this.gref = gref;
2424
this.callback = callback;
@@ -49,6 +49,10 @@ public boolean equals(Object ob_other) {
4949
return ob_other == this;
5050
}
5151

52+
public boolean hasCallback() {
53+
return callback != null;
54+
}
55+
5256
public int hashCode() {
5357
return gref.pythonHashCode();
5458
}
@@ -80,15 +84,16 @@ public PyObject __ne__(PyObject other) {
8084
protected PyObject get() {
8185
PyObject result = gref.get();
8286
if (result == null && gc.delayedWeakrefCallbacksEnabled()) {
83-
if (gref.cleared) {
87+
if (gref.isCleared()) {
8488
return null;
8589
}
8690
if ((gc.getJythonGCFlags() & gc.VERBOSE_WEAKREF) != 0) {
8791
gc.writeDebug("gc", "pending in get of abstract ref "+this+": "+
8892
Thread.currentThread().getId());
8993
}
90-
JyAttribute.setAttr(this, JyAttribute.WEAKREF_PENDING_GET_ATTR, Thread.currentThread());
91-
while (!gref.cleared && result == null) {
94+
JyAttribute.setAttr(this, JyAttribute.WEAKREF_PENDING_GET_ATTR,
95+
Thread.currentThread());
96+
while (!gref.isCleared() && result == null) {
9297
try {
9398
Thread.sleep(2000);
9499
} catch (InterruptedException ie) {}
@@ -98,7 +103,7 @@ protected PyObject get() {
98103
if ((gc.getJythonGCFlags() & gc.VERBOSE_WEAKREF) != 0) {
99104
gc.writeDebug("gc", "pending of "+this+" resolved: "+
100105
Thread.currentThread().getId());
101-
if (gref.cleared) {
106+
if (gref.isCleared()) {
102107
gc.writeDebug("gc", "reference was cleared.");
103108
} else if (result != null){
104109
gc.writeDebug("gc", "reference was restored.");

src/org/python/modules/_weakref/CallableProxyType.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public class CallableProxyType extends ProxyType {
1414

1515
public static final PyType TYPE = PyType.fromClass(CallableProxyType.class);
1616

17-
public CallableProxyType(GlobalRef ref, PyObject callback) {
17+
public CallableProxyType(ReferenceBackend ref, PyObject callback) {
1818
super(TYPE, ref, callback);
1919
}
2020

src/org/python/modules/_weakref/GlobalRef.java

Lines changed: 94 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,13 @@
1717
import org.python.util.Generic;
1818
import org.python.modules.gc;
1919

20-
public class GlobalRef extends WeakReference<PyObject> {
20+
public class GlobalRef extends WeakReference<PyObject> implements ReferenceBackend {
21+
22+
/**
23+
* This is a hook for JyNI to insert a native-objects-aware implementation
24+
* of ReferenceBackend.
25+
*/
26+
public static ReferenceBackendFactory factory = null;
2127

2228
/**
2329
* This reference's hashCode: The {@code System.identityHashCode} of the referent.
@@ -54,7 +60,7 @@ public class GlobalRef extends WeakReference<PyObject> {
5460
private static Thread reaperThread;
5561
private static ReentrantReadWriteLock reaperLock = new ReentrantReadWriteLock();
5662

57-
private static ConcurrentMap<GlobalRef, GlobalRef> objects = Generic.concurrentMap();
63+
private static ConcurrentMap<GlobalRef, ReferenceBackend> objects = Generic.concurrentMap();
5864
private static List<GlobalRef> delayedCallbacks;
5965

6066
public GlobalRef(PyObject object) {
@@ -76,7 +82,7 @@ private final AbstractReference getReferenceAt(int idx) {
7682
* Search for a reusable reference. To be reused, it must be of the
7783
* same class and it must not have a callback.
7884
*/
79-
synchronized AbstractReference find(Class<?> cls) {
85+
public synchronized AbstractReference find(Class<?> cls) {
8086
for (int i = references.size() - 1; i >= 0; i--) {
8187
AbstractReference r = getReferenceAt(i);
8288
if (r == null) {
@@ -107,6 +113,12 @@ synchronized void call() {
107113
r.call();
108114
}
109115
}
116+
ReferenceBackend ref2 = objects.get(this);
117+
if (ref2.isCleared()) {
118+
objects.remove(this);
119+
} else if (factory != null && ref2 != this) {
120+
factory.notifyClear(ref2, this);
121+
}
110122
}
111123
}
112124

@@ -135,6 +147,7 @@ public static void processDelayedCallbacks() {
135147
* determined whether a resurrection restored the reference.
136148
*
137149
* @see gc#PRESERVE_WEAKREFS_ON_RESURRECTION
150+
* @see gc#FORCE_DELAYED_WEAKREF_CALLBACKS
138151
*/
139152
private static void delayedCallback(GlobalRef cl) {
140153
if (delayedCallbacks == null) {
@@ -149,6 +162,10 @@ public static boolean hasDelayedCallbacks() {
149162
return delayedCallbacks != null && !delayedCallbacks.isEmpty();
150163
}
151164

165+
public boolean isCleared() {
166+
return cleared;
167+
}
168+
152169
synchronized public int count() {
153170
for (int i = references.size() - 1; i >= 0; i--) {
154171
AbstractReference r = getReferenceAt(i);
@@ -173,26 +190,70 @@ synchronized public PyList refs() {
173190
}
174191

175192
/**
176-
* Create a new tracked {@code GlobalRef}.
193+
* Returns null if nothing is changed. If a factory exists
194+
* and produces a result different from {@code this}, this
195+
* result is returned. Also, this result is then installed
196+
* in all weak-references, in the referent's JyAttribute and
197+
* in the objects-map to act as a proxy for this GlobalRef,
198+
* which will still serve as a backend for the proxy. This
199+
* method is most likely used exclusively by JyNI.
200+
*/
201+
synchronized protected ReferenceBackend retryFactory() {
202+
if (factory == null) {
203+
return null;
204+
}
205+
ReferenceBackend result = factory.makeBackend(this, null);
206+
if (result != this) {
207+
objects.put(this, result);
208+
for (int i = references.size() - 1; i >= 0; i--) {
209+
AbstractReference r = getReferenceAt(i);
210+
if (r == null) {
211+
references.remove(i);
212+
} else {
213+
r.gref = result;
214+
}
215+
}
216+
PyObject referent = result.get();
217+
JyAttribute.setAttr(referent, JyAttribute.WEAK_REF_ATTR, result);
218+
return result;
219+
}
220+
return null;
221+
}
222+
223+
/**
224+
* Create a new tracked {@code ReferenceBackend}.
225+
* If no {@code ReferenceBackendFactory} is registered, it actually
226+
* returns a {@code GlobalRef}.
177227
*
178228
* @param object a {@link org.python.core.PyObject} to reference
179-
* @return a new tracked {@code GlobalRef}
229+
* @return a new tracked {@code ReferenceBackend}
180230
*/
181-
public static GlobalRef newInstance(PyObject object) {
231+
public static ReferenceBackend newInstance(PyObject object) {
182232
createReaperThreadIfAbsent();
183-
184233
GlobalRef newRef = new GlobalRef(object);
185-
GlobalRef ref = objects.putIfAbsent(newRef, newRef);
186-
if (ref == null) {
187-
ref = newRef;
188-
JyAttribute.setAttr(object, JyAttribute.WEAK_REF_ATTR, ref);
189-
} else {
190-
// We clear the not-needed Global ref so that it won't
191-
// pop up in ref-reaper thread's activity.
192-
newRef.clear();
193-
newRef.cleared = true;
234+
/*
235+
* Note: Before factory was introduced, the following used to be
236+
* objects.putIfAbsent(newRef, newRef), which is an atomic
237+
* operation and was used to prevent multiple threads from
238+
* creating multiple GlobalRefs for the same referent. With
239+
* factory we cannot use objects.putIfAbsent any more, so
240+
* we use a synchronized block instead. (Maybe objects could
241+
* now be replaced by an ordinary map rather than concurrent.)
242+
*/
243+
synchronized (objects) {
244+
ReferenceBackend ref = objects.get(newRef);
245+
if (ref == null) {
246+
ref = factory == null ? newRef : factory.makeBackend(newRef, object);
247+
objects.put(newRef, ref);
248+
JyAttribute.setAttr(object, JyAttribute.WEAK_REF_ATTR, ref);
249+
} else {
250+
// We clear the not-needed Global ref so that it won't
251+
// pop up in ref-reaper thread's activity.
252+
newRef.clear();
253+
newRef.cleared = true;
254+
}
255+
return ref;
194256
}
195-
return ref;
196257
}
197258

198259
/**
@@ -210,7 +271,10 @@ public static GlobalRef newInstance(PyObject object) {
210271
*/
211272
public synchronized void restore(PyObject formerReferent) {
212273
/* This method is synchronized to avoid concurrent invocation of call(). */
213-
if (JyAttribute.getAttr(formerReferent, JyAttribute.WEAK_REF_ATTR) != this) {
274+
ReferenceBackend formerBackend = (ReferenceBackend)
275+
JyAttribute.getAttr(formerReferent, JyAttribute.WEAK_REF_ATTR);
276+
ReferenceBackend proxy = objects.get(this);
277+
if (formerBackend != this && formerBackend != proxy) {
214278
throw new IllegalArgumentException(
215279
"Argument is not former referent of this GlobalRef.");
216280
}
@@ -222,16 +286,23 @@ public synchronized void restore(PyObject formerReferent) {
222286
clear();
223287
createReaperThreadIfAbsent();
224288
GlobalRef restore = new GlobalRef(formerReferent);
289+
if (proxy != this && factory != null) {
290+
factory.updateBackend(proxy, restore);
291+
} else {
292+
JyAttribute.setAttr(formerReferent, JyAttribute.WEAK_REF_ATTR, restore);
293+
}
225294
restore.references = references;
226295
objects.remove(this);
227-
objects.put(restore, restore);
296+
objects.put(restore, proxy == this ? restore : proxy);
228297
AbstractReference aref;
229298
for (int i = references.size() - 1; i >= 0; i--) {
230299
aref = getReferenceAt(i);
231300
if (aref == null) {
232301
references.remove(i);
233302
} else {
234-
aref.gref = restore;
303+
if (this == proxy) {
304+
aref.gref = restore;
305+
}
235306
Thread pendingGet = (Thread) JyAttribute.getAttr(
236307
aref, JyAttribute.WEAKREF_PENDING_GET_ATTR);
237308
if (pendingGet != null) {
@@ -244,7 +315,7 @@ public synchronized void restore(PyObject formerReferent) {
244315
* (The remove from delayed callback list might happen before
245316
* the insert.) However we can only set cleared = true after
246317
* all gref-variables were updated, otherwise some refs might
247-
* break. To avoid callback-rocessing in the unsafe state
318+
* break. To avoid callback-processing in the unsafe state
248319
* between these actions, this method is synchronized (as is call()).
249320
*/
250321
cleared = true;
@@ -278,7 +349,7 @@ private static void createReaperThreadIfAbsent() {
278349
* @return an int reference count
279350
*/
280351
public static int getCount(PyObject object) {
281-
GlobalRef ref = objects.get(new GlobalRef(object));
352+
ReferenceBackend ref = objects.get(new GlobalRef(object));
282353
return ref == null ? 0 : ref.count();
283354
}
284355

@@ -290,7 +361,7 @@ public static int getCount(PyObject object) {
290361
* @return a {@link org.python.core.PyList} of references. May be empty.
291362
*/
292363
public static PyList getRefs(PyObject object) {
293-
GlobalRef ref = objects.get(new GlobalRef(object));
364+
ReferenceBackend ref = objects.get(new GlobalRef(object));
294365
return ref == null ? new PyList() : ref.refs();
295366
}
296367

@@ -366,8 +437,6 @@ public void collect() throws InterruptedException {
366437
} else {
367438
delayedCallback(gr);
368439
}
369-
objects.remove(gr);
370-
gr = null;
371440
}
372441

373442
@Override

src/org/python/modules/_weakref/ProxyType.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ public class ProxyType extends AbstractReference {
1717

1818
public static final PyType TYPE = PyType.fromClass(ProxyType.class);
1919

20-
public ProxyType(PyType subType, GlobalRef ref, PyObject callback) {
20+
public ProxyType(PyType subType, ReferenceBackend ref, PyObject callback) {
2121
super(subType, ref, callback);
2222
}
2323

24-
public ProxyType(GlobalRef ref, PyObject callback) {
24+
public ProxyType(ReferenceBackend ref, PyObject callback) {
2525
this(TYPE, ref, callback);
2626
}
2727

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package org.python.modules._weakref;
2+
3+
import org.python.core.PyObject;
4+
import org.python.core.PyList;
5+
6+
public interface ReferenceBackend {
7+
public PyObject get();
8+
public void add(AbstractReference ref);
9+
public boolean isCleared();
10+
public AbstractReference find(Class<?> cls);
11+
public int pythonHashCode();
12+
public PyList refs();
13+
public void restore(PyObject formerReferent);
14+
public int count();
15+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package org.python.modules._weakref;
2+
3+
import org.python.core.PyObject;
4+
5+
/**
6+
* Reserved for use by JyNI.
7+
*/
8+
public interface ReferenceBackendFactory {
9+
10+
public ReferenceBackend makeBackend(GlobalRef caller, PyObject referent);
11+
public void notifyClear(ReferenceBackend ref, GlobalRef caller);
12+
public void updateBackend(ReferenceBackend ref, GlobalRef caller);
13+
}

src/org/python/modules/_weakref/ReferenceType.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ public class ReferenceType extends AbstractReference {
1515

1616
public static final PyType TYPE = PyType.fromClass(ReferenceType.class);
1717

18-
public ReferenceType(PyType subType, GlobalRef gref, PyObject callback) {
18+
public ReferenceType(PyType subType, ReferenceBackend gref, PyObject callback) {
1919
super(subType, gref, callback);
2020
}
2121

22-
public ReferenceType(GlobalRef gref, PyObject callback) {
22+
public ReferenceType(ReferenceBackend gref, PyObject callback) {
2323
this(TYPE, gref, callback);
2424
}
2525

@@ -33,7 +33,7 @@ static final PyObject weakref___new__(PyNewWrapper new_, boolean init, PyType su
3333
callback = null;
3434
}
3535

36-
GlobalRef gref = GlobalRef.newInstance(ob);
36+
ReferenceBackend gref = GlobalRef.newInstance(ob);
3737
if (new_.for_type == subtype) {
3838
// NOTE: CPython disallows weakrefs to many builtin types (e.g. dict, list)
3939
// and would check weakrefability here. We aren't as strict since the JVM can

src/org/python/modules/_weakref/ReferenceTypeDerived.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public void delDict() {
7373
dict=new PyStringMap();
7474
}
7575

76-
public ReferenceTypeDerived(PyType subtype,GlobalRef gref,PyObject callback) {
76+
public ReferenceTypeDerived(PyType subtype,ReferenceBackend gref,PyObject callback) {
7777
super(subtype,gref,callback);
7878
slots=new PyObject[subtype.getNumSlots()];
7979
dict=subtype.instDict();

src/org/python/modules/_weakref/WeakrefModule.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public static void classDictInit(PyObject dict)
2828
}
2929

3030
public static ProxyType proxy(PyObject object) {
31-
GlobalRef gref = GlobalRef.newInstance(object);
31+
ReferenceBackend gref = GlobalRef.newInstance(object);
3232
boolean callable = object.isCallable();
3333
ProxyType ret = (ProxyType)gref.find(callable ? CallableProxyType.class : ProxyType.class);
3434
if (ret != null) {

src/org/python/modules/gc.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.python.core.Untraversable;
2626
import org.python.core.finalization.FinalizeTrigger;
2727
import org.python.modules._weakref.GlobalRef;
28+
import org.python.modules._weakref.ReferenceBackend;
2829

2930
//These imports belong to the out-commented section on MXBean-based
3031
//gc-sync far below. That section is kept to document this failed
@@ -902,7 +903,7 @@ public static void restoreFinalizer(PyObject obj) {
902903
* @see #PRESERVE_WEAKREFS_ON_RESURRECTION
903904
*/
904905
public static void restoreWeakReferences(PyObject rst) {
905-
GlobalRef toRestore = (GlobalRef)
906+
ReferenceBackend toRestore = (ReferenceBackend)
906907
JyAttribute.getAttr(rst, JyAttribute.WEAK_REF_ATTR);
907908
if (toRestore != null) {
908909
toRestore.restore(rst);

0 commit comments

Comments
 (0)