1717import org .python .util .Generic ;
1818import 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
0 commit comments