[jffi-next] 17/22: Implement magazine allocator for closures

Tim Potter tpot-guest at moszumanska.debian.org
Wed Mar 4 04:51:23 UTC 2015


This is an automated email from the git hooks/post-receive script.

tpot-guest pushed a commit to tag 0.6.0.1
in repository jffi-next.

commit 4aa2ee3568d8d3a1bc8a012926bc5e158cef577c
Author: Wayne Meissner <wmeissner at gmail.com>
Date:   Tue Oct 13 19:30:03 2009 +1000

    Implement magazine allocator for closures
---
 src/com/kenai/jffi/Closure.java     |   2 +-
 src/com/kenai/jffi/ClosurePool.java | 268 ++++++++++++++++++++++++++----------
 2 files changed, 198 insertions(+), 72 deletions(-)

diff --git a/src/com/kenai/jffi/Closure.java b/src/com/kenai/jffi/Closure.java
index 5681cc0..4807d01 100644
--- a/src/com/kenai/jffi/Closure.java
+++ b/src/com/kenai/jffi/Closure.java
@@ -176,7 +176,7 @@ public interface Closure {
          */
         void dispose();
 
-        @Deprecated
+        //@Deprecated
         void free();
     }
 }
diff --git a/src/com/kenai/jffi/ClosurePool.java b/src/com/kenai/jffi/ClosurePool.java
index 7fd2694..1c9c950 100644
--- a/src/com/kenai/jffi/ClosurePool.java
+++ b/src/com/kenai/jffi/ClosurePool.java
@@ -1,102 +1,230 @@
-
 package com.kenai.jffi;
 
+import java.lang.ref.WeakReference;
 import java.lang.reflect.Method;
-import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
 
-public class ClosurePool {
-    private static final Object lock = new Object();
+final class ClosurePool {
 
-    private final CallContext ctx;
+    private final List<MagazineHolder> partial = new LinkedList<MagazineHolder>();
+    private final List<MagazineHolder> full = new LinkedList<MagazineHolder>();
+    private final Set<Magazine> magazines = new HashSet<Magazine>();
+    //
+    // Since the CallContext native handle is used by the native pool code
+    // a strong reference to the call context needs to be kept.
+    //
+    private final CallContext callContext;
 
-    public ClosurePool(CallContext ctx) {
-        this.ctx = ctx;
+    ClosurePool(CallContext callContext) {
+        this.callContext = callContext;
     }
-    
-    Closure.Handle newClosureHandle(Closure closure) {
-        Proxy proxy = new Proxy(closure, ctx);
 
-        long handle = 0;
-        synchronized (lock) {
-            handle = Foreign.getInstance().newClosure(proxy, Proxy.METHOD,
-                ctx.nativeReturnType, ctx.nativeParameterTypes, ctx.flags);
+    synchronized void recycle(Magazine magazine) {
+        magazine.recycle();
+        if (!magazine.isEmpty()) {
+            MagazineHolder h = new MagazineHolder(this, magazine);
+            if (magazine.isFull()) {
+                full.add(h);
+            } else {
+                partial.add(h);
+            }
+        } else {
+            // If the magazine was empty during recycling, it means all the closures
+            // allocated from it set autorelease=false, so we cannot re-use it.
+            // Let GC clean it up.
+            magazines.remove(magazine);
         }
-        if (handle == 0) {
-            throw new RuntimeException("Failed to create native closure");
+    }
+
+    private synchronized MagazineHolder getMagazineHolder() {
+        if (!partial.isEmpty()) {
+            return partial.get(0);
+        } else if (!full.isEmpty()) {
+            MagazineHolder h = full.remove(0);
+            partial.add(h);
+            return h;
         }
+        Magazine m = new Magazine(callContext);
+        MagazineHolder h = new MagazineHolder(this, m);
+        partial.add(h);
+        magazines.add(m);
+        return h;
+    }
 
-        return new Handle(handle, ctx);
+    public synchronized Closure.Handle newClosureHandle(Closure closure) {
+        Magazine.Slot s = null;
+        MagazineHolder h = null;
+        do {
+            h = getMagazineHolder();
+            s = h.magazine.get();
+            if (s == null) {
+                partial.remove(0);
+            }
+        } while (s == null);
+        s.proxy.closure = closure;
+        return new Handle(s, h);
     }
 
-    /**
+        /**
      * Manages the lifecycle of a native closure.
      *
      * Implements {@link Closure.Handle} interface.
      */
     private static final class Handle implements Closure.Handle {
-        /** Store a reference to the MemoryIO accessor here for easy access */
-        private static final com.kenai.jffi.MemoryIO IO = com.kenai.jffi.MemoryIO.getInstance();
-
-        private final AtomicBoolean released = new AtomicBoolean(false);
-        private volatile boolean autorelease = true;
-
         /**
-         * The address of the native closure structure.
-         *
-         * <b>Note:</b> This is <b>NOT</b> the code address, but a pointer to the structure
-         * which contains the code address.
+         * Keep references to the closure pool so it does not get garbage collected
+         * until all closures using it do.
          */
-        final long handle;
+        private final MagazineHolder holder;
+        private final Magazine.Slot slot;
 
-        /** The code trampoline address */
-        final long cbAddress;
-
-        /**
-         * Keep references to the return and parameter types so they do not get
-         * garbage collected until the closure does.
-         */
-        private final CallContext ctx;
+        private volatile boolean disposed = false;
 
         /**
          * Creates a new Handle to lifecycle manager the native closure.
          *
          * @param handle The address of the native closure structure.
+         * @param pool The native pool the closure was allocated from.
          */
-        Handle(long handle, CallContext ctx) {
-            this.handle = handle;
-            cbAddress = IO.getAddress(handle);
-            this.ctx = ctx;
+        Handle(Magazine.Slot slot, MagazineHolder holder) {
+            this.slot = slot;
+            this.holder = holder;
         }
 
         public long getAddress() {
-            return cbAddress;
+            return slot.cbAddress;
         }
 
         public void setAutoRelease(boolean autorelease) {
-            this.autorelease = autorelease;
+            slot.autorelease = autorelease;
         }
 
-        public void free() { dispose(); }
+        @Deprecated
+        public void free() {
+            dispose();
+        }
 
-        public void dispose() {
-            if (released.getAndSet(true)) {
-                throw new IllegalStateException("Closure already released");
+        public synchronized void dispose() {
+            if (disposed) {
+                throw new IllegalStateException("closure already disposed");
             }
-            synchronized (lock) {
-                Foreign.getInstance().freeClosure(handle);
+            disposed = true;
+            slot.autorelease = true;
+        }
+    }
+
+    private static final class Magazine {
+        private static final int MAX_SLOTS = 200;
+
+        /** Store a reference to the MemoryIO accessor here for easy access */
+        private static final com.kenai.jffi.MemoryIO IO = com.kenai.jffi.MemoryIO.getInstance();
+
+        private final CallContext ctx;
+
+        private final List<Slot> free = new ArrayList<Slot>(MAX_SLOTS);
+        private final List<Slot> all = new ArrayList<Slot>(MAX_SLOTS);
+
+        Magazine(CallContext ctx) {
+            this.ctx = ctx;
+        }
+
+        Slot get() {
+            if (!free.isEmpty()) {
+                return free.remove(free.size() - 1);
+            }
+
+            return all.size() < MAX_SLOTS ? newSlot() : null;
+        }
+
+        private Slot newSlot() {
+            Proxy proxy = new Proxy(ctx);
+            long h = Foreign.getInstance().newClosure(proxy, Proxy.METHOD, ctx.nativeReturnType, ctx.nativeParameterTypes, ctx.flags);
+            if (h == 0) {
+                return null;
             }
+            Slot s = new Slot(h, proxy);
+            all.add(s);
+            return s;
+        }
+
+        boolean isFull() {
+            return free.size() == all.size();
+        }
+
+        boolean isEmpty() {
+            return free.isEmpty();
+        }
+
+        void recycle() {
+            free.clear();
+            for (Slot s : all) {
+                if (s.autorelease) {
+                    s.proxy.closure = NULL_CLOSURE;
+                    free.add(s);
+                }
+            }
+            all.retainAll(free);
         }
 
         @Override
         protected void finalize() throws Throwable {
             try {
-                if (autorelease && !released.getAndSet(true)) {
-                    synchronized (lock) {
-                        Foreign.getInstance().freeClosure(handle);
+                for (Slot s : all) {
+                    if (s.autorelease) {
+                        Foreign.getInstance().freeClosure(s.handle);
                     }
+
+                }
+            } finally {
+                super.finalize();
+            }
+        }
+
+        static final class Slot {
+            /**
+             * The address of the native closure structure.
+             *
+             * <b>Note:</b> This is <b>NOT</b> the code address, but a pointer to the structure
+             * which contains the code address.
+             */
+            final long handle;
+
+            /** The code trampoline address */
+            final long cbAddress;
+
+            final Proxy proxy;
+            volatile boolean autorelease;
+
+            public Slot(long handle, Proxy proxy) {
+                this.handle = handle;
+                this.proxy = proxy;
+                this.autorelease = true;
+                cbAddress = IO.getAddress(handle);
+            }
+        }
+    }
+
+    private static final class MagazineHolder {
+        private final WeakReference<ClosurePool> poolref;
+        private final Magazine magazine;
+
+        public MagazineHolder(ClosurePool pool, Magazine magazine) {
+            this.poolref = new WeakReference<ClosurePool>(pool);
+            this.magazine = magazine;
+        }
+
+
+        @Override
+        protected void finalize() throws Throwable {
+            try {
+                ClosurePool pool = poolref.get();
+                if (pool != null) {
+                    pool.recycle(magazine);
                 }
-            } catch (Throwable t) {
-                t.printStackTrace(System.err);
             } finally {
                 super.finalize();
             }
@@ -107,38 +235,31 @@ public class ClosurePool {
      * This is a proxy passed to the native code, to be called by the
      * native trampoline code.
      */
-    private static final class Proxy {
+    static final class Proxy {
         static final Method METHOD = getMethod();
-        final Closure closure;
-
         /**
          * Keep references to the return and parameter types so they do not get
          * garbage collected until the closure does.
          */
-        final CallContext ctx;
+        final CallContext callContext;
+
+        volatile Closure closure;
 
         /**
          * Gets the
          * @return
          */
-        private static  final Method getMethod() {
+        private static final Method getMethod() {
             try {
-                return Proxy.class.getDeclaredMethod("invoke", new Class[] { long.class, long.class });
+                return Proxy.class.getDeclaredMethod("invoke", new Class[]{long.class, long.class});
             } catch (Throwable ex) {
                 throw new RuntimeException(ex);
             }
         }
 
-        /**
-         * Creates a new <tt>Proxy</tt> instance.
-         *
-         * @param closure The closure to call when this proxy is invoked
-         * @param returnType The native return type of the closure
-         * @param parameterTypes The parameterTypes of the closure
-         */
-        Proxy(Closure closure, CallContext ctx) {
-            this.closure = closure;
-            this.ctx = ctx;
+        Proxy(CallContext callContext) {
+            this.closure = NULL_CLOSURE;
+            this.callContext = callContext;
         }
 
         /**
@@ -149,7 +270,12 @@ public class ClosurePool {
          * @param paramAddress The address of the native parameter buffer.
          */
         void invoke(long retvalAddress, long paramAddress) {
-            closure.invoke(new DirectClosureBuffer(ctx, retvalAddress, paramAddress));
+            closure.invoke(new DirectClosureBuffer(callContext, retvalAddress, paramAddress));
         }
     }
+    private static final Closure NULL_CLOSURE = new Closure() {
+
+        public void invoke(Buffer buffer) {
+        }
+    };
 }

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/jffi-next.git



More information about the pkg-java-commits mailing list