Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial commit.

  • Loading branch information...
commit 8214416d1e62bae0391b853430e746b0a133488b 0 parents
@headius headius authored
20 Rakefile
@@ -0,0 +1,20 @@
+require 'ant'
+
+directory "pkg/classes"
+
+task :clean do
+ rm_rf "pkg/classes"
+ rm_rf "lib/refqueue.jar"
+end
+
+task :compile => "pkg/classes" do |t|
+ ant.javac :srcdir => "ext", :destdir => t.prerequisites.first,
+ :source => "1.5", :target => "1.5", :debug => true,
+ :classpath => "${java.class.path}:${sun.boot.class.path}"
+end
+
+task :jar => :compile do
+ ant.jar :basedir => "pkg/classes", :destfile => "lib/refqueue.jar", :includes => "**/*.class"
+end
+
+task :package => :jar
24 examples/id_hash.rb
@@ -0,0 +1,24 @@
+require 'weakling'
+
+wh = WeakRef::IdHash.new
+
+ary = (1..10).to_a.map {Object.new}
+ids = ary.map {|o| wh.add(o)}
+
+puts "all items in weak_id_hash:"
+ids.each {|i| puts "#{i} = #{wh[i]}"}
+
+puts "dereferencing objects"
+ary = nil
+
+puts "forcing GC"
+begin
+ require 'java'
+ java.lang.System.gc
+rescue
+ GC.start
+end
+
+puts "all items in weak id hash:"
+ids.each {|i| puts "#{i} = #{wh[i]}"}
+puts "hash size: #{wh.hash.size}"
12 ext/RefqueueService.java
@@ -0,0 +1,12 @@
+import java.io.IOException;
+
+import org.jruby.Ruby;
+import org.jruby.runtime.load.BasicLibraryService;
+
+public class RefqueueService implements BasicLibraryService {
+ public boolean basicLoad(final Ruby runtime) throws IOException {
+ new org.jruby.ext.RefQueueLibrary().load(runtime, false);
+ return true;
+ }
+}
+
173 ext/org/jruby/ext/RefQueueLibrary.java
@@ -0,0 +1,173 @@
+package org.jruby.ext;
+
+import java.io.IOException;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyException;
+import org.jruby.RubyKernel;
+import org.jruby.RubyObject;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.anno.JRubyClass;
+import org.jruby.exceptions.RaiseException;
+import org.jruby.javasupport.util.RuntimeHelpers;
+import org.jruby.runtime.Block;
+import org.jruby.runtime.ObjectAllocator;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.Visibility;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.jruby.runtime.load.Library;
+
+/**
+ * This library adds reference queue support to JRuby's weakrefs by adding a
+ * RefQueue class that wraps a Java ReferenceQueue and replacing the built-in
+ * WeakRef impl with a new one that's aware of RefQueue.
+ *
+ * @author headius
+ */
+public class RefQueueLibrary implements Library {
+ public void load(Ruby runtime, boolean wrap) throws IOException {
+ RubyKernel.require(runtime.getKernel(), runtime.newString("weakref"), Block.NULL_BLOCK);
+
+ RubyClass weakrefClass = (RubyClass)runtime.getClassFromPath("WeakRef");
+ weakrefClass.setAllocator(WEAKREF_ALLOCATOR);
+ weakrefClass.defineAnnotatedMethods(WeakRef.class);
+
+ RubyClass refQueueClass = runtime.defineClassUnder("RefQueue", runtime.getObject(), REFQUEUE_ALLOCATOR, weakrefClass);
+ refQueueClass.defineAnnotatedMethods(RefQueue.class);
+ }
+
+ private static final ObjectAllocator WEAKREF_ALLOCATOR = new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ return new WeakRef(runtime, klazz);
+ }
+ };
+
+ private static final ObjectAllocator REFQUEUE_ALLOCATOR = new ObjectAllocator() {
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+ return new RefQueue(runtime, klazz);
+ }
+ };
+
+ @JRubyClass(name="RefQueue", parent="Object")
+ public static class RefQueue extends RubyObject {
+ private final ReferenceQueue queue;
+
+ public RefQueue(Ruby runtime, RubyClass klass) {
+ super(runtime, klass);
+ queue = new ReferenceQueue();
+ }
+
+ public ReferenceQueue getQueue() {
+ return queue;
+ }
+
+ @JRubyMethod
+ public IRubyObject poll() {
+ return returnable(queue.poll());
+ }
+
+ @JRubyMethod
+ public IRubyObject remove() {
+ try {
+ return returnable(queue.remove());
+ } catch (InterruptedException ie) {
+ // ignore
+ return getRuntime().getNil();
+ }
+ }
+
+ @JRubyMethod
+ public IRubyObject remove(IRubyObject timeout) {
+ try {
+ return returnable(queue.remove(timeout.convertToInteger().getLongValue()));
+ } catch (InterruptedException ie) {
+ // ignore
+ return getRuntime().getNil();
+ }
+ }
+
+ private IRubyObject returnable(Object result) {
+ RubyWeakReference ref = (RubyWeakReference)result;
+ if (ref == null) return getRuntime().getNil();
+ return ref.getWeakRef();
+ }
+ }
+
+ public static class RubyWeakReference extends WeakReference<IRubyObject> {
+ private final WeakRef ref;
+ public RubyWeakReference(IRubyObject obj, WeakRef ref) {
+ super(obj);
+ this.ref = ref;
+ }
+ public RubyWeakReference(IRubyObject obj, WeakRef ref, ReferenceQueue queue) {
+ super(obj, queue);
+ this.ref = ref;
+ }
+ public WeakRef getWeakRef() {
+ return ref;
+ }
+ }
+
+ public static class WeakRef extends RubyObject {
+ private RubyWeakReference ref;
+
+ public WeakRef(Ruby runtime, RubyClass klazz) {
+ super(runtime, klazz);
+ }
+
+ @JRubyMethod(name = "__getobj__")
+ public IRubyObject getobj() {
+ IRubyObject obj = ref.get();
+
+ if (obj == null) {
+ // FIXME weakref.rb also does caller(2) here for the backtrace
+ throw newRefError("Illegal Reference - probably recycled");
+ }
+
+ return obj;
+ }
+
+ // This is only here to replace the "new" in JRuby's weakref, which
+ // doesn't really need to be there.
+ @JRubyMethod(name = "new", required = 1, optional = 1, meta = true)
+ public static IRubyObject newInstance(IRubyObject clazz, IRubyObject[] args) {
+ WeakRef weakRef = (WeakRef)((RubyClass)clazz).allocate();
+
+ weakRef.callInit(args, Block.NULL_BLOCK);
+
+ return weakRef;
+ }
+
+ @JRubyMethod(name = "initialize", frame = true, visibility = Visibility.PRIVATE)
+ public IRubyObject initialize(ThreadContext context, IRubyObject obj) {
+ ref = new RubyWeakReference(obj, this);
+
+ return RuntimeHelpers.invokeSuper(context, this, obj, Block.NULL_BLOCK);
+ }
+
+ @JRubyMethod(name = "initialize", frame = true, visibility = Visibility.PRIVATE)
+ public IRubyObject initialize(ThreadContext context, IRubyObject obj, IRubyObject queue) {
+ if (!(queue instanceof RefQueue)) {
+ throw getRuntime().newTypeError("WeakRef can only queue into a RefQueue");
+ }
+ ref = new RubyWeakReference(obj, this, ((RefQueue)queue).getQueue());
+
+ return RuntimeHelpers.invokeSuper(context, this, obj, Block.NULL_BLOCK);
+ }
+
+ @JRubyMethod(name = "weakref_alive?")
+ public IRubyObject weakref_alive_p() {
+ return ref.get() != null ? getRuntime().getTrue() : getRuntime().getFalse();
+ }
+
+ private RaiseException newRefError(String message) {
+ RubyException exception =
+ (RubyException)getRuntime().getClass("RefError").newInstance(getRuntime().getCurrentContext(),
+ new IRubyObject[] {getRuntime().newString(message)}, Block.NULL_BLOCK);
+
+ return new RaiseException(exception);
+ }
+ }
+}
4 lib/weakling.rb
@@ -0,0 +1,4 @@
+require 'weakref'
+require 'refqueue'
+
+require 'weakling/collections.rb'
43 lib/weakling/collections.rb
@@ -0,0 +1,43 @@
+require 'weakref'
+require 'refqueue'
+
+class WeakRef::IdHash
+ attr_accessor :hash
+
+ def initialize
+ @hash = Hash.new
+ @queue = WeakRef::RefQueue.new
+ end
+
+ class IdWeakRef < WeakRef
+ attr_accessor :id
+ def initialize(obj, queue)
+ super(obj, queue)
+ @id = obj.__id__
+ end
+ end
+
+ def [](id)
+ _cleanup
+ if wr = @hash[id]
+ return wr.__getobj__ rescue nil
+ end
+
+ return nil
+ end
+
+ def add(object)
+ _cleanup
+ wr = IdWeakRef.new(object, @queue)
+
+ @hash[wr.id] = wr
+
+ return wr.id
+ end
+
+ def _cleanup
+ while ref = @queue.poll
+ @hash.delete(ref.id)
+ end
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.