[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