aboutsummaryrefslogtreecommitdiffstats
path: root/gl4java/utils/DirectBufferCleanup.java
diff options
context:
space:
mode:
Diffstat (limited to 'gl4java/utils/DirectBufferCleanup.java')
-rw-r--r--gl4java/utils/DirectBufferCleanup.java110
1 files changed, 110 insertions, 0 deletions
diff --git a/gl4java/utils/DirectBufferCleanup.java b/gl4java/utils/DirectBufferCleanup.java
new file mode 100644
index 0000000..378cd76
--- /dev/null
+++ b/gl4java/utils/DirectBufferCleanup.java
@@ -0,0 +1,110 @@
+package gl4java.utils;
+
+import java.lang.ref.PhantomReference;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.nio.*;
+import java.util.HashMap;
+import java.util.Map;
+
+/** Provides a cleanup mechanism for direct buffers instantiated via
+ the JNI entry point <code>NewDirectByteBuffer</code>. A direct
+ buffer can be <i>registered</i> with a DirectBufferCleanup. When
+ that buffer is reclaimed by the garbage collector, the callback
+ associated with that DirectBufferCleanup is called on the address
+ the direct buffer was associated with. */
+
+public class DirectBufferCleanup {
+
+ // Put DirectBufferCleanup.c into its own library if extracting just
+ // this mechanism
+ // static {
+ // System.loadLibrary("DirectBufferCleanup");
+ // }
+
+ public static interface Callback {
+ public void cleanup(long addr);
+ }
+
+ public DirectBufferCleanup(Callback cb) {
+ this.cb = cb;
+ refToAddrMap = new HashMap();
+ queue = new ReferenceQueue();
+ start();
+ }
+
+ /** The DirectBufferCleanup contains an internal thread which is
+ started automatically upon construction. This method starts the
+ thread again if it is manually stopped via the {@link #stop}
+ method. */
+ public synchronized void start() {
+ if (t == null) {
+ t = new Thread(new Runnable() {
+ public void run() {
+ while (!done) {
+ try {
+ Reference r = queue.remove();
+ Long addr = (Long) refToAddrMap.remove(r);
+ cb.cleanup(addr.longValue());
+ r.clear();
+ } catch (InterruptedException e) {
+ }
+ }
+ t = null;
+ }
+ });
+ done = false;
+ t.start();
+ }
+ }
+
+ /** Stops the internal thread of this DirectBufferCleanup. Should
+ not typically be necessary. */
+ public synchronized void stop() {
+ done = true;
+ while (t != null) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
+ /** Registers the given buffer (which must be a direct buffer) for
+ later cleanup when it is reclaimed by the garbage collector.
+
+ @throw IllegalArgumentException if the passed buffer is not
+ direct.
+ */
+
+ public void register(Buffer buf) throws IllegalArgumentException {
+ try {
+ long addr = getDirectBufferAddress(buf);
+ if (addr == 0) throw new IllegalArgumentException();
+ refToAddrMap.put(new PhantomReference(buf, queue),
+ new Long(addr));
+ } catch (ClassCastException e) {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ //----------------------------------------------------------------------
+ // Internals only below this point
+ //
+
+ private Callback cb;
+
+ // Maps PhantomReferences to addresses
+ private Map refToAddrMap;
+
+ // Reference queue which gets notified
+ private ReferenceQueue queue;
+
+ // Thread watching reference queue
+ private volatile Thread t;
+
+ // Native method providing direct buffer address via JNI
+ private static native long getDirectBufferAddress(Buffer buf);
+
+ private volatile boolean done = false;
+}