From 08053c0095c3b3264b7fcd5b13660b5e5660c757 Mon Sep 17 00:00:00 2001 From: obethery Date: Sun, 2 Nov 2014 23:02:51 +0100 Subject: [PATCH] Correction for https://github.com/ochafik/nativelibs4java/issues/534 --- .../BridJ/src/main/java/org/bridj/BridJ.java | 10 +- .../org/bridj/KnownNativeObjectSupport.java | 29 ++++ .../java/org/bridj/WeakReferenceMonitor.java | 157 ++++++++++++++++++ 3 files changed, 189 insertions(+), 7 deletions(-) create mode 100644 libraries/BridJ/src/main/java/org/bridj/KnownNativeObjectSupport.java create mode 100644 libraries/BridJ/src/main/java/org/bridj/WeakReferenceMonitor.java diff --git a/libraries/BridJ/src/main/java/org/bridj/BridJ.java b/libraries/BridJ/src/main/java/org/bridj/BridJ.java index 318906caf..2383f78d1 100644 --- a/libraries/BridJ/src/main/java/org/bridj/BridJ.java +++ b/libraries/BridJ/src/main/java/org/bridj/BridJ.java @@ -245,18 +245,14 @@ public static boolean isCastingNativeObjectInCurrentThread() { public static boolean isCastingNativeObjectReturnTypeInCurrentThread() { return currentlyCastingNativeObject.get().peek() == CastingType.CastingNativeObjectReturnType; } - private static WeakHashMap knownNativeObjects = new WeakHashMap(); + private static KnownNativeObjectSupport knownNativeObjectSupport = new KnownNativeObjectSupport(); public static synchronized void setJavaObjectFromNativePeer(long peer, O object) { - if (object == null) { - knownNativeObjects.remove(peer); - } else { - knownNativeObjects.put(peer, object); - } + knownNativeObjectSupport.setJavaObjectFromNativePeer(peer, object); } public static synchronized Object getJavaObjectFromNativePeer(long peer) { - return knownNativeObjects.get(peer); + return knownNativeObjectSupport.getJavaObjectFromNativePeer(peer); } private static O createNativeObjectFromPointer(Pointer pointer, Type type, CastingType castingType) { diff --git a/libraries/BridJ/src/main/java/org/bridj/KnownNativeObjectSupport.java b/libraries/BridJ/src/main/java/org/bridj/KnownNativeObjectSupport.java new file mode 100644 index 000000000..f32cdc6fa --- /dev/null +++ b/libraries/BridJ/src/main/java/org/bridj/KnownNativeObjectSupport.java @@ -0,0 +1,29 @@ +package org.bridj; + +import java.util.HashMap; +import java.util.Map; + +import org.bridj.WeakReferenceMonitor.ReleaseListener; + +public class KnownNativeObjectSupport { + + private Map knownNativeObjects = new HashMap(); + + public void setJavaObjectFromNativePeer(final long peer, O object) { + if (object == null) { + knownNativeObjects.remove(peer); + } else { + knownNativeObjects.put(peer, object); + WeakReferenceMonitor.monitor(object, new ReleaseListener() { + public void released() { + knownNativeObjects.remove(peer); + } + }); + + } + } + + public Object getJavaObjectFromNativePeer(long peer) { + return knownNativeObjects.get(peer); + } +} diff --git a/libraries/BridJ/src/main/java/org/bridj/WeakReferenceMonitor.java b/libraries/BridJ/src/main/java/org/bridj/WeakReferenceMonitor.java new file mode 100644 index 000000000..201633893 --- /dev/null +++ b/libraries/BridJ/src/main/java/org/bridj/WeakReferenceMonitor.java @@ -0,0 +1,157 @@ +package org.bridj; +/* +* Copyright 2002-2007 the original author or authors. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * Track references to arbitrary objects using proxy and weak references. To + * monitor a handle, one should call {@link #monitor(Object, ReleaseListener)}, + * with the given handle object usually being a holder that uses the target + * object underneath, and the release listener performing cleanup of the + * target object once the handle is not strongly referenced anymore. + * + *

When a given handle becomes weakly reachable, the specified listener + * will be called by a background thread. This thread will only be started + * lazily and will be stopped once no handles are registered for monitoring + * anymore, to be restarted if further handles are added. + * + *

Thanks to Tomasz Wysocki for the suggestion and the original + * implementation of this class! + * + * @author Colin Sampaleanu + * @author Juergen Hoeller + * + * @since 1.2 + * @see #monitor + */ +public class WeakReferenceMonitor { + + // Queue receiving reachability events + private static final ReferenceQueue handleQueue = new ReferenceQueue(); + + // All tracked entries (WeakReference => ReleaseListener) + private static final Map,ReleaseListener> trackedEntries = Collections.synchronizedMap(new HashMap()); + + // Thread polling handleQueue, lazy initialized + private static Thread monitoringThread = null; + + + /** + * Start to monitor given handle object for becoming weakly reachable. + * When the handle isn't used anymore, the given listener will be called. + * @param handle the object that will be monitored + * @param listener the listener that will be called upon release of the handle + */ + public static void monitor(Object handle, ReleaseListener listener) { + + // Make weak reference to this handle, so we can say when + // handle is not used any more by polling on handleQueue. + WeakReference weakRef = new WeakReference(handle, handleQueue); + + // Add monitored entry to internal map of all monitored entries. + addEntry(weakRef, listener); + } + + /** + * Add entry to internal map of tracked entries. + * Internal monitoring thread is started if not already running. + * @param ref reference to tracked handle + * @param entry the associated entry + */ + private static void addEntry(Reference ref, ReleaseListener entry) { + // Add entry, the key is given reference. + trackedEntries.put(ref, entry); + + // Start monitoring thread lazily. + synchronized (WeakReferenceMonitor.class) { + if (!isMonitoringThreadRunning()) { + monitoringThread = new Thread(new MonitoringProcess(), WeakReferenceMonitor.class.getName()); + monitoringThread.setDaemon(true); + monitoringThread.start(); + } + } + } + + /** + * Remove entry from internal map of tracked entries. + * @param reference the reference that should be removed + * @return entry object associated with given reference + */ + private static ReleaseListener removeEntry(Reference reference) { + return (ReleaseListener) trackedEntries.remove(reference); + } + + /** + * Check if monitoring thread is currently running. + */ + private static boolean isMonitoringThreadRunning() { + synchronized (WeakReferenceMonitor.class) { + return (monitoringThread != null); + } + } + + + /** + * Thread implementation that performs the actual monitoring. + */ + private static class MonitoringProcess implements Runnable { + + public void run() { + try { + // Check if there are any tracked entries left. + while (!trackedEntries.isEmpty()) { + try { + Reference reference = handleQueue.remove(); + // Stop tracking this reference. + ReleaseListener entry = removeEntry(reference); + if (entry != null) { + // Invoke listener callback. + entry.released(); + } + } + catch (InterruptedException ex) { + break; + } + } + } + finally { + synchronized (WeakReferenceMonitor.class) { + monitoringThread = null; + } + } + } + } + + + /** + * Listener that is notified when the handle is being released. + * To be implemented by users of this reference monitor. + */ + public static interface ReleaseListener { + + /** + * This callback method is invoked once the associated handle has been released, + * i.e. once there are no monitored strong references to the handle anymore. + */ + void released(); + } + +} \ No newline at end of file