diff --git a/platforms/android/test-app/runtime/src/main/java/com/tns/GcListener.java b/platforms/android/test-app/runtime/src/main/java/com/tns/GcListener.java index 0d5a6989..8dc8e7c1 100644 --- a/platforms/android/test-app/runtime/src/main/java/com/tns/GcListener.java +++ b/platforms/android/test-app/runtime/src/main/java/com/tns/GcListener.java @@ -5,11 +5,13 @@ import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue; import java.util.ArrayList; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; class GCSubscriber { ConcurrentHashMap, Integer> referencesMap = new ConcurrentHashMap<>(); public final ReferenceQueue referenceQueue = new ReferenceQueue<>(); + boolean unsubscribed; // guarded by this } public class GcListener { @@ -60,7 +62,11 @@ public void run() { for (int i =0; i < objIds.length; i++) { objIds[i] = collectedObjectIds.get(i); } - runtime.notifyGc(objIds); + synchronized (subscriber) { + if (!subscriber.unsubscribed) { + runtime.notifyGc(objIds); + } + } if (runtime.getLogger().isEnabled()) { runtime.getLogger().write("GC", "GC triggered for " + String.valueOf(objIds.length) + " objects"); runtime.getLogger().write("NS.GCListener","Objects referenced after GC: " + subscriber.referencesMap.size()); @@ -152,9 +158,17 @@ public static void subscribe(Runtime runtime) { } } + /** + * Once this returns, no GC thread is inside the runtime's notifyGc and none + * will enter it. Must not be called from inside that runtime's JS scope: + * delivery holds the subscriber's lock while it waits to enter the runtime. + */ public static void unsubscribe(Runtime runtime) { - synchronized (instance) { - instance.subscribers.remove(runtime); + GCSubscriber subscriber = instance.subscribers.remove(runtime); + if (subscriber != null) { + synchronized (subscriber) { + subscriber.unsubscribed = true; + } } } @@ -168,15 +182,18 @@ private void start() { } private void notifyGc() { - synchronized (instance) { -// ManualInstrumentation.Frame frame = ManualInstrumentation.start("GcListener.notifyGc"); - try { - for (Runtime runtime : instance.subscribers.keySet()) { - runtime.notifyGc(); +// ManualInstrumentation.Frame frame = ManualInstrumentation.start("GcListener.notifyGc"); + try { + for (Map.Entry entry : subscribers.entrySet()) { + GCSubscriber subscriber = entry.getValue(); + synchronized (subscriber) { + if (!subscriber.unsubscribed) { + entry.getKey().notifyGc(); + } } - } finally { -// frame.close(); } + } finally { +// frame.close(); } } }