@@ -528,27 +528,42 @@ private boolean hasPin(DualState<?> s)
528528 /** Thread local record of when the PG thread is invoking callbacks. */
529529 private static final CleanupTracker s_inCleanup = new CleanupTracker ();
530530
531+ /** Flag for cleanup entry/exit from the Java side */
532+ private static final int CLEAN_JAVA = 1 ;
533+
534+ /** Flag for cleanup entry/exit from the native side */
535+ private static final int CLEAN_NATIVE = 2 ;
536+
531537 /** Thread local boolean with pairing enter/exit operations. */
532- static final class CleanupTracker extends ThreadLocal <Boolean >
538+ static final class CleanupTracker extends ThreadLocal <Integer >
533539 {
534- boolean enter ()
540+ boolean enter (int how )
535541 {
536542 assert Backend .threadMayEnterPG () : m ("inCleanup.enter thread" );
537- assert ! inCleanup () : m ("inCleanup.enter re-entered" );
538- set (Boolean .TRUE );
543+ int got = get ();
544+ assert (got &3 ) != how : m ("inCleanup.enter re-entered same how" );
545+ assert got < 4 : m ("inCleanup.enter too many entries" );
546+ set ((got << 2 ) | how );
539547 return true ;
540548 }
541549
542- boolean exit ()
550+ boolean exit (int how )
543551 {
544- assert inCleanup () : m ("inCleanup.exit mispaired" );
545- set (Boolean .FALSE );
552+ int got = get ();
553+ assert (got &3 ) == how : m ("inCleanup.exit mispaired" );
554+ set (got >>> 2 );
546555 return true ;
547556 }
548557
549558 boolean inCleanup ()
550559 {
551- return Boolean .TRUE == get ();
560+ return 0 != get ();
561+ }
562+
563+ @ Override
564+ protected Integer initialValue ()
565+ {
566+ return 0 ;
552567 }
553568 }
554569
@@ -1757,35 +1772,44 @@ private static void lifespanRelease(Lifespan lifespan)
17571772
17581773 DualState t = head .m_next ;
17591774 head .m_prev = head .m_next = head ;
1760- for ( DualState s = t ; s != head ; s = t )
1775+
1776+ assert s_inCleanup .enter (CLEAN_NATIVE ); //no-op when assertions disabled
1777+ try
17611778 {
1762- t = s .m_next ;
1763- s .m_prev = s .m_next = null ;
1764- ++ total ;
1765- /*
1766- * This lock() is part of DualState's contract with clients.
1767- * They are responsible for pinning the state instance
1768- * whenever they need the wrapped native state (which is verified
1769- * to still be valid at that time) and for the duration of whatever
1770- * operation needs access to that state. Taking this lock here
1771- * ensures the native state is blocked from vanishing while it is
1772- * actively in use.
1773- */
1774- int state = s .lock (false );
1775- try
1779+ for ( DualState s = t ; s != head ; s = t )
17761780 {
1777- if ( z (NATIVE_RELEASED & state ) )
1781+ t = s .m_next ;
1782+ s .m_prev = s .m_next = null ;
1783+ ++ total ;
1784+ /*
1785+ * This lock() is part of DualState's contract with clients.
1786+ * They are responsible for pinning the state instance whenever
1787+ * they need the wrapped native state (which is verified to
1788+ * still be valid at that time) and for the duration of whatever
1789+ * operation needs access to that state. Taking this lock here
1790+ * ensures the native state is blocked from vanishing while it
1791+ * is actively in use.
1792+ */
1793+ int state = s .lock (false );
1794+ try
17781795 {
1779- ++ release ;
1780- s .nativeStateReleased (
1781- z (JAVA_RELEASED & state ) && null != s .referent ());
1796+ if ( z (NATIVE_RELEASED & state ) )
1797+ {
1798+ ++ release ;
1799+ s .nativeStateReleased (
1800+ z (JAVA_RELEASED & state ) && null != s .referent ());
1801+ }
1802+ }
1803+ finally
1804+ {
1805+ s .unlock (state , true );//true->ensure NATIVE_RELEASED is set.
17821806 }
1783- }
1784- finally
1785- {
1786- s .unlock (state , true ); // true -> ensure NATIVE_RELEASED is set.
17871807 }
17881808 }
1809+ finally
1810+ {
1811+ assert s_inCleanup .exit (CLEAN_NATIVE );
1812+ }
17891813
17901814 s_stats .lifespanPoll (release , total );
17911815 }
@@ -1807,7 +1831,7 @@ private static void cleanEnqueuedInstances()
18071831 int nDeferred = s_deferredReleased .size ();
18081832 boolean isDeferred ;
18091833
1810- assert s_inCleanup .enter (); // no-op when assertions disabled
1834+ assert s_inCleanup .enter (CLEAN_JAVA ); // no-op when assertions disabled
18111835 try
18121836 {
18131837 for ( ;; )
@@ -1848,7 +1872,7 @@ else if ( z(NATIVE_RELEASED & state) )
18481872 }
18491873 finally
18501874 {
1851- assert s_inCleanup .exit ();
1875+ assert s_inCleanup .exit (CLEAN_JAVA );
18521876 }
18531877
18541878 s_stats .referenceQueueDrain (total - release , release , total , reDefer );
@@ -2363,6 +2387,55 @@ protected void javaStateUnreachable(boolean nativeStateLive)
23632387 private native void _heapFreeTuple (ByteBuffer tuple );
23642388 }
23652389
2390+ /**
2391+ * A {@code DualState} subclass whose only native resource releasing action
2392+ * needed is a JNI {@code DeleteGlobalRef} of a single pointer.
2393+ */
2394+ public static abstract class SingleDeleteGlobalRefP <T >
2395+ extends SingleGuardedLong <T >
2396+ {
2397+ protected SingleDeleteGlobalRefP (
2398+ T referent , Lifespan span , long dgrTarget )
2399+ {
2400+ super (referent , span , dgrTarget );
2401+ }
2402+
2403+ @ Override
2404+ public String formatString ()
2405+ {
2406+ return "%s DeleteGlobalRef(%x)" ;
2407+ }
2408+
2409+ /**
2410+ * When the Java state is released (it won't normally go unreachable,
2411+ * because of the global ref), a JNI {@code DeleteGlobalRef} call
2412+ * is made so the instance can be collected without having to wait
2413+ * for release of its containing context.
2414+ */
2415+ @ Override
2416+ protected void javaStateReleased (boolean nativeStateLive )
2417+ {
2418+ assert Backend .threadMayEnterPG ();
2419+ if ( nativeStateLive )
2420+ _deleteGlobalRefP (guardedLong ());
2421+ }
2422+
2423+ /**
2424+ * When the native state is released, a JNI {@code DeleteGlobalRef} call
2425+ * is made so the global ref stored in that to-be-released memory isn't
2426+ * leaked (left permanently live).
2427+ */
2428+ @ Override
2429+ protected void nativeStateReleased (boolean javaStateLive )
2430+ {
2431+ assert Backend .threadMayEnterPG ();
2432+ if ( javaStateLive )
2433+ _deleteGlobalRefP (guardedLong ());
2434+ }
2435+
2436+ private native void _deleteGlobalRefP (long pointer );
2437+ }
2438+
23662439 /**
23672440 * Bean exposing some {@code DualState} allocation and lifecycle statistics
23682441 * for viewing in a JMX management client.
0 commit comments