Skip to content
This repository was archived by the owner on Mar 15, 2022. It is now read-only.

Commit 91682ed

Browse files
author
Brian Durand
committed
Initial commit
0 parents  commit 91682ed

36 files changed

+1560
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
pkg
2+
tmp
3+
rdoc
4+
*.rbc

MIT_LICENSE

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Copyright (c) 2010 Brian Durand
2+
3+
Permission is hereby granted, free of charge, to any person obtaining
4+
a copy of this software and associated documentation files (the
5+
"Software"), to deal in the Software without restriction, including
6+
without limitation the rights to use, copy, modify, merge, publish,
7+
distribute, sublicense, and/or sell copies of the Software, and to
8+
permit persons to whom the Software is furnished to do so, subject to
9+
the following conditions:
10+
11+
The above copyright notice and this permission notice shall be
12+
included in all copies or substantial portions of the Software.
13+
14+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.rdoc

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
This library provides implementations of various types of object references for Ruby as well as some common utilities for working with references. Reference objects are pretty much pointers, but they come in three distinct flavors that interact differently with the garbage collector.
2+
3+
* References::StrongReference - This is a plain old pointer to another object.
4+
* References::WeakReference - This is a pointer to another object, but it is not seen by the garbage collector and the memory used by the object can be reclaimed at any time.
5+
* References::SoftReference - This is similar to a weak reference, but the garbage collector is not as eager to reclaim the referenced object.
6+
7+
All of these classes extend from a common References::Reference class.
8+
9+
Weak and soft references are useful when you have instantiated objects that you may want to use again but can recreate if necessary. Since the garbage collector determines when to reclaim the memory used by the objects, you don't need to worry about bloating the Ruby heap.
10+
11+
== Example Usage
12+
13+
ref = References::WeakReference.new("hello")
14+
ref.object # should be "hello"
15+
ObjectSpace.garbage_collect
16+
ref.object # should be nil (assuming the garbage collector reclaimed the reference)
17+
18+
== Goodies
19+
20+
This library also includes tools for some common uses of weak and soft references.
21+
22+
* References::WeakKeyMap - A map of keys to values where the keys are weak references
23+
* References::WeakValueMap - A map of keys to values where the values are weak references
24+
* References::SoftKeyMap - A map of keys to values where the keys are soft references
25+
* References::SoftValueMap - A map of keys to values where the values are soft references
26+
* References::ReferenceQueue - A thread safe implementation of a queue that will add references to itself as their objects are garbage collected.
27+
28+
== Problems with WeakRef
29+
30+
Ruby does come with the WeakRef class in the standard library. However, there are issues with this class across several different Ruby runtimes. This gem provides a common interface to weak references that works across MRI, Ruby Enterprise Edition, YARV, Jruby, Rubinius, and IronRuby.
31+
32+
1. MRI and REE 1.8 - WeakRef extends from Delegator which is a very heavy weight class under Ruby 1.8. Creating a WeakRef object will allocate thousands of other objects and use up hundreds of kilobytes of memory. This makes WeakRef all but unusable even if you only need several hundred of them.
33+
2. YARV 1.9 - WeakRef is unsafe to use because the garbage collector can run in a different system thread than a thread allocating memory. This exposes a bug where a WeakRef may end up pointing to a completely different object than it originally referenced.
34+
3. Jruby and IronRuby - Jruby and IronRuby using the Ruby 1.8 libraries suffers from the same performance issue with the Delegator class. Furthermore, these VM's don't implement the method used to load an object from the heap using an object id and so cannot use a pure Ruby method to implement weak references.
35+
4. Rubinius - Rubinius implements WeakRef with a lighter weight version of delegation and works very well.

Rakefile

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
require 'rake'
2+
require 'rake/rdoctask'
3+
require 'rake/testtask'
4+
require 'rake/gempackagetask'
5+
require File.expand_path('../lib/references', __FILE__)
6+
7+
desc 'Default: run unit tests.'
8+
task :default => :test
9+
10+
Rake::TestTask.new do |t|
11+
t.libs << 'test'
12+
t.pattern = 'test/**/*_test.rb'
13+
t.warning = true
14+
t.verbose = true
15+
end
16+
17+
desc 'Generate documentation.'
18+
Rake::RDocTask.new(:rdoc) do |rdoc|
19+
rdoc.rdoc_dir = 'rdoc'
20+
rdoc.options << '--title' << 'References' << '--line-numbers' << '--inline-source' << '--main' << 'README.rdoc'
21+
rdoc.rdoc_files.include('README.rdoc')
22+
rdoc.rdoc_files.include('lib/**/*.rb')
23+
end
24+
25+
spec = eval(File.read(File.expand_path('../references.gemspec', __FILE__)))
26+
27+
Rake::GemPackageTask.new(spec) do |p|
28+
p.gem_spec = spec
29+
end
30+
Rake.application["package"].prerequisites.unshift("java:build")
31+
32+
desc "Release to gemcutter"
33+
task :release => :package do
34+
require 'rake/gemcutter'
35+
Rake::Gemcutter::Tasks.new(spec).define
36+
Rake::Task['gem:push'].invoke
37+
end
38+
39+
namespace :java do
40+
desc "Build the jar files for Jruby support"
41+
task :build do
42+
base_dir = File.dirname(__FILE__)
43+
tmp_dir = File.join(base_dir, "tmp")
44+
classes_dir = File.join(tmp_dir, "classes")
45+
FileUtils.rm_rf(classes_dir)
46+
ext_dir = File.join(base_dir, "ext", "java")
47+
source_files = FileList["#{base_dir}/**/*.java"]
48+
FileUtils.mkdir_p(classes_dir)
49+
`#{ENV['JAVA_HOME']}/bin/javac -classpath '#{"#{ENV['JRUBY_HOME']}/lib/jruby.jar"}' -d '#{classes_dir}' -sourcepath '#{ext_dir}' '#{source_files.join("' '")}'`
50+
`#{ENV['JAVA_HOME']}/bin/jar cf '#{base_dir}/lib/references/java_support.jar' -C '#{classes_dir}' references`
51+
FileUtils.rm_rf(classes_dir)
52+
end
53+
end
54+
55+
namespace :test do
56+
namespace :performance do
57+
desc "Run a speed test on how long it takes to create 100000 weak references"
58+
task :weak_reference do
59+
puts "Testing performance of weak references..."
60+
t = Time.now
61+
100000.times do
62+
References::WeakReference.new(Object.new)
63+
end
64+
puts "Creating 100,000 weak references took #{Time.now - t} seconds"
65+
end
66+
67+
desc "Run a speed test on how long it takes to create 100000 soft references"
68+
task :soft_reference do
69+
puts "Testing performance of soft references..."
70+
t = Time.now
71+
100000.times do |i|
72+
References::SoftReference.new(Object.new)
73+
end
74+
puts "Creating 100,000 soft references took #{Time.now - t} seconds"
75+
end
76+
end
77+
end

VERSION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0.0.1
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package references;
2+
3+
import references.weak_reference.JavaImpl;
4+
5+
import java.io.IOException;
6+
import org.jruby.Ruby;
7+
import org.jruby.RubyClass;
8+
import org.jruby.RubyModule;
9+
import org.jruby.runtime.builtin.IRubyObject;
10+
import org.jruby.runtime.load.BasicLibraryService;
11+
12+
/**
13+
* This library adds native Java support for weak and soft references.
14+
*
15+
* @author Brian Durand
16+
*/
17+
public class JavaSupportService implements BasicLibraryService {
18+
public boolean basicLoad(Ruby runtime) throws IOException {
19+
RubyModule referencesModule = runtime.getModule("References");
20+
21+
RubyClass weakReferenceClass = referencesModule.getClass("WeakReference");
22+
RubyClass javaWeakReferenceClass = runtime.defineClassUnder("JavaImpl", runtime.getObject(), JavaImpl.ALLOCATOR, weakReferenceClass);
23+
javaWeakReferenceClass.defineAnnotatedMethods(JavaImpl.class);
24+
25+
RubyClass referenceClass = referencesModule.getClass("Reference");
26+
RubyClass rubySoftReferenceClass = runtime.defineClassUnder("SoftReference", referenceClass, RubySoftReference.ALLOCATOR, referencesModule);
27+
rubySoftReferenceClass.defineAnnotatedMethods(RubySoftReference.class);
28+
29+
return true;
30+
}
31+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package references;
2+
3+
import java.lang.ref.SoftReference;
4+
import org.jruby.Ruby;
5+
import org.jruby.RubyClass;
6+
import org.jruby.RubyObject;
7+
import org.jruby.anno.JRubyMethod;
8+
import org.jruby.runtime.builtin.IRubyObject;
9+
import org.jruby.runtime.ObjectAllocator;
10+
import org.jruby.runtime.ThreadContext;
11+
import org.jruby.runtime.Visibility;
12+
13+
public class RubySoftReference extends RubyObject {
14+
private SoftReference _ref;
15+
private static final String REFERENCED_OBJECT_ID_VARIABLE = "@referenced_object_id".intern();
16+
17+
public RubySoftReference(Ruby runtime, RubyClass klass) {
18+
super(runtime, klass);
19+
}
20+
21+
public static final ObjectAllocator ALLOCATOR = new ObjectAllocator() {
22+
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
23+
return new RubySoftReference(runtime, klass);
24+
}
25+
};
26+
27+
@JRubyMethod(name = "initialize", frame = true, visibility = Visibility.PRIVATE)
28+
public IRubyObject initialize(ThreadContext context, IRubyObject obj) {
29+
_ref = new SoftReference<IRubyObject>(obj);
30+
fastSetInstanceVariable(REFERENCED_OBJECT_ID_VARIABLE, obj.id());
31+
return context.getRuntime().getNil();
32+
}
33+
34+
@JRubyMethod(name = "object")
35+
public IRubyObject object() {
36+
IRubyObject obj = (IRubyObject)_ref.get();
37+
if (obj != null) {
38+
return obj;
39+
} else {
40+
return getRuntime().getNil();
41+
}
42+
}
43+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package references.weak_reference;
2+
3+
import java.lang.ref.WeakReference;
4+
import org.jruby.Ruby;
5+
import org.jruby.RubyClass;
6+
import org.jruby.RubyObject;
7+
import org.jruby.anno.JRubyMethod;
8+
import org.jruby.runtime.builtin.IRubyObject;
9+
import org.jruby.runtime.ObjectAllocator;
10+
import org.jruby.runtime.ThreadContext;
11+
import org.jruby.runtime.Visibility;
12+
13+
public class JavaImpl extends RubyObject {
14+
private WeakReference _ref;
15+
16+
public JavaImpl(Ruby runtime, RubyClass klass) {
17+
super(runtime, klass);
18+
}
19+
20+
public static final ObjectAllocator ALLOCATOR = new ObjectAllocator() {
21+
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
22+
return new JavaImpl(runtime, klass);
23+
}
24+
};
25+
26+
@JRubyMethod(name = "initialize", frame = true, visibility = Visibility.PRIVATE)
27+
public IRubyObject initialize(ThreadContext context, IRubyObject obj) {
28+
_ref = new WeakReference<IRubyObject>(obj);
29+
return context.getRuntime().getNil();
30+
}
31+
32+
@JRubyMethod(name = "object")
33+
public IRubyObject object() {
34+
IRubyObject obj = (IRubyObject)_ref.get();
35+
if (obj != null) {
36+
return obj;
37+
} else {
38+
return getRuntime().getNil();
39+
}
40+
}
41+
}

lib/references.rb

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
require 'monitor'
2+
3+
module References
4+
autoload :AbstractReferenceValueMap, File.join(File.dirname(__FILE__), "references", "abstract_reference_value_map.rb")
5+
autoload :AbstractReferenceKeyMap, File.join(File.dirname(__FILE__), "references", "abstract_reference_key_map.rb")
6+
autoload :Reference, File.join(File.dirname(__FILE__), "references", "reference.rb")
7+
autoload :ReferenceQueue, File.join(File.dirname(__FILE__), "references", "reference_queue.rb")
8+
autoload :SoftKeyMap, File.join(File.dirname(__FILE__), "references", "soft_key_map.rb")
9+
autoload :SoftValueMap, File.join(File.dirname(__FILE__), "references", "soft_value_map.rb")
10+
autoload :StrongReference, File.join(File.dirname(__FILE__), "references", "strong_reference.rb")
11+
autoload :WeakReference, File.join(File.dirname(__FILE__), "references", "weak_reference.rb")
12+
autoload :WeakKeyMap, File.join(File.dirname(__FILE__), "references", "weak_key_map.rb")
13+
autoload :WeakValueMap, File.join(File.dirname(__FILE__), "references", "weak_value_map.rb")
14+
15+
# Set the best implementation for weak references based on the runtime.
16+
if defined?(RUBY_PLATFORM) && RUBY_PLATFORM == 'java'
17+
begin
18+
$LOAD_PATH.unshift(File.dirname(__FILE__))
19+
require 'references/java_support'
20+
WeakReference.implementation = WeakReference::JavaImpl
21+
ensure
22+
$LOAD_PATH.shift if $LOAD_PATH.first == File.dirname(__FILE__)
23+
end
24+
else
25+
autoload :SoftReference, File.join(File.dirname(__FILE__), "references", "soft_reference.rb")
26+
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ironruby'
27+
WeakReference.implementation = WeakReference::IronRubyImpl
28+
elsif defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
29+
# If using Rubinius set the implementation to use WeakRef since it is very efficient.
30+
require 'weakref'
31+
WeakReference.implementation = WeakReference::WeakRefImpl
32+
else
33+
WeakReference.implementation = WeakReference::RubyImpl
34+
end
35+
end
36+
end

0 commit comments

Comments
 (0)