atomic 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Rakefile CHANGED
@@ -8,3 +8,29 @@ Rake::TestTask.new :test do |t|
8
8
  t.libs << "lib"
9
9
  t.test_files = FileList["test/**/*.rb"]
10
10
  end
11
+
12
+ if defined?(JRUBY_VERSION)
13
+ require 'ant'
14
+
15
+ directory "pkg/classes"
16
+
17
+ desc "Clean up build artifacts"
18
+ task :clean do
19
+ rm_rf "pkg/classes"
20
+ rm_rf "lib/refqueue.jar"
21
+ end
22
+
23
+ desc "Compile the extension"
24
+ task :compile => "pkg/classes" do |t|
25
+ ant.javac :srcdir => "ext", :destdir => t.prerequisites.first,
26
+ :source => "1.5", :target => "1.5", :debug => true,
27
+ :classpath => "${java.class.path}:${sun.boot.class.path}"
28
+ end
29
+
30
+ desc "Build the jar"
31
+ task :jar => :compile do
32
+ ant.jar :basedir => "pkg/classes", :destfile => "lib/atomic_reference.jar", :includes => "**/*.class"
33
+ end
34
+
35
+ task :package => :jar
36
+ end
data/atomic.gemspec CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{atomic}
5
- s.version = "0.0.2"
5
+ s.version = "0.0.3"
6
6
  s.authors = ["Charles Oliver Nutter", "MenTaLguY"]
7
7
  s.date = Time.now.strftime('YYYY-MM-DD')
8
8
  s.description = "An atomic reference implementation for JRuby and green or GIL-threaded impls"
9
9
  s.email = ["headius@headius.com", "mental@rydia.net"]
10
- s.files = Dir['{lib,examples,test}/**/*'] + Dir['{*.txt,*.gemspec,Rakefile}']
10
+ s.files = Dir['{lib,examples,test,ext}/**/*'] + Dir['{*.txt,*.gemspec,Rakefile}']
11
11
  s.homepage = "http://github.com/headius/ruby-atomic"
12
12
  s.require_paths = ["lib"]
13
13
  s.summary = "An atomic reference implementation for JRuby and green or GIL-threaded impls"
@@ -0,0 +1,12 @@
1
+ import java.io.IOException;
2
+
3
+ import org.jruby.Ruby;
4
+ import org.jruby.runtime.load.BasicLibraryService;
5
+
6
+ public class AtomicReferenceService implements BasicLibraryService {
7
+ public boolean basicLoad(final Ruby runtime) throws IOException {
8
+ new org.jruby.ext.atomic.AtomicReferenceLibrary().load(runtime, false);
9
+ return true;
10
+ }
11
+ }
12
+
@@ -0,0 +1,81 @@
1
+ package org.jruby.ext.atomic;
2
+
3
+ import java.io.IOException;
4
+ import java.util.concurrent.atomic.AtomicReference;
5
+ import org.jruby.Ruby;
6
+ import org.jruby.RubyClass;
7
+ import org.jruby.RubyModule;
8
+ import org.jruby.RubyObject;
9
+ import org.jruby.anno.JRubyClass;
10
+ import org.jruby.anno.JRubyMethod;
11
+ import org.jruby.runtime.ObjectAllocator;
12
+ import org.jruby.runtime.ThreadContext;
13
+ import org.jruby.runtime.builtin.IRubyObject;
14
+ import org.jruby.runtime.load.Library;
15
+
16
+ /**
17
+ * This library adds an atomic reference type to JRuby for use in the atomic
18
+ * library. We do a native version to avoid the implicit value coercion that
19
+ * normally happens through JI.
20
+ *
21
+ * @author headius
22
+ */
23
+ public class AtomicReferenceLibrary implements Library {
24
+ public void load(Ruby runtime, boolean wrap) throws IOException {
25
+ RubyModule atomicCls = runtime.getClass("Atomic");
26
+ RubyClass jrubyRefClass = runtime.defineClassUnder("InternalReference", runtime.getObject(), JRUBYREFERENCE_ALLOCATOR, atomicCls);
27
+ jrubyRefClass.setAllocator(JRUBYREFERENCE_ALLOCATOR);
28
+ jrubyRefClass.defineAnnotatedMethods(JRubyReference.class);
29
+ }
30
+
31
+ private static final ObjectAllocator JRUBYREFERENCE_ALLOCATOR = new ObjectAllocator() {
32
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
33
+ return new JRubyReference(runtime, klazz);
34
+ }
35
+ };
36
+
37
+ @JRubyClass(name="JRubyReference", parent="Object")
38
+ public static class JRubyReference extends RubyObject {
39
+ private final AtomicReference<IRubyObject> reference;
40
+
41
+ public JRubyReference(Ruby runtime, RubyClass klass) {
42
+ super(runtime, klass);
43
+ reference = new AtomicReference<IRubyObject>(runtime.getNil());
44
+ }
45
+
46
+ @JRubyMethod
47
+ public IRubyObject initialize(ThreadContext context) {
48
+ Ruby runtime = context.getRuntime();
49
+ reference.set(runtime.getNil());
50
+ return runtime.getNil();
51
+ }
52
+
53
+ @JRubyMethod
54
+ public IRubyObject initialize(ThreadContext context, IRubyObject value) {
55
+ Ruby runtime = context.getRuntime();
56
+ reference.set(value);
57
+ return runtime.getNil();
58
+ }
59
+
60
+ @JRubyMethod(name = {"get", "value"})
61
+ public IRubyObject get() {
62
+ return reference.get();
63
+ }
64
+
65
+ @JRubyMethod(name = {"set", "value="})
66
+ public IRubyObject set(IRubyObject newValue) {
67
+ reference.set(newValue);
68
+ return newValue;
69
+ }
70
+
71
+ @JRubyMethod
72
+ public IRubyObject compare_and_set(ThreadContext context, IRubyObject oldValue, IRubyObject newValue) {
73
+ return context.getRuntime().newBoolean(reference.compareAndSet(oldValue, newValue));
74
+ }
75
+
76
+ @JRubyMethod
77
+ public IRubyObject get_and_set(ThreadContext context, IRubyObject newValue) {
78
+ return reference.getAndSet(newValue);
79
+ }
80
+ }
81
+ }
data/lib/atomic.rb CHANGED
@@ -17,6 +17,10 @@ class Atomic
17
17
  new_value
18
18
  end
19
19
 
20
+ def swap(new_value)
21
+ @ref.get_and_set(new_value)
22
+ end
23
+
20
24
  # Pass the current value to the given block, replacing it
21
25
  # with the block's result. May retry if the value changes
22
26
  # during the block's execution.
@@ -34,13 +38,12 @@ class Atomic
34
38
  unless @ref.compare_and_set(old_value, new_value)
35
39
  raise ConcurrentUpdateError, "Update failed"
36
40
  end
37
- old_value
41
+ new_value
38
42
  end
39
43
  end
40
44
 
41
45
  if defined? RUBY_ENGINE && RUBY_ENGINE == "jruby"
42
- require 'java'
43
- Atomic::InternalReference = java.util.concurrent.atomic.AtomicReference
46
+ require 'atomic_reference'
44
47
  else
45
48
  class Atomic::InternalReference
46
49
  attr_accessor :value
@@ -51,6 +54,14 @@ else
51
54
  @value = value
52
55
  end
53
56
 
57
+ def get_and_set(new_value)
58
+ Thread.exclusive do
59
+ old_value = @value
60
+ @value = new_value
61
+ old_value
62
+ end
63
+ end
64
+
54
65
  def compare_and_set(old_value, new_value)
55
66
  Thread.exclusive do
56
67
  return false unless @value.equal? old_value
Binary file
Binary file
data/test/test_atomic.rb CHANGED
@@ -18,34 +18,46 @@ class TestAtomic < Test::Unit::TestCase
18
18
  end
19
19
 
20
20
  def test_update
21
- atomic = Atomic.new(0)
21
+ # use a number outside JRuby's fixnum cache range, to ensure identity is preserved
22
+ atomic = Atomic.new(1000)
22
23
  res = atomic.update {|v| v + 1}
23
24
 
24
- assert_equal 1, atomic.value
25
- assert_equal 0, res
25
+ assert_equal 1001, atomic.value
26
+ assert_equal 1001, res
26
27
  end
27
28
 
28
29
  def test_try_update
29
- atomic = Atomic.new(0)
30
+ # use a number outside JRuby's fixnum cache range, to ensure identity is preserved
31
+ atomic = Atomic.new(1000)
30
32
  res = atomic.try_update {|v| v + 1}
31
33
 
32
- assert_equal 1, atomic.value
33
- assert_equal 0, res
34
+ assert_equal 1001, atomic.value
35
+ assert_equal 1001, res
36
+ end
37
+
38
+ def test_swap
39
+ atomic = Atomic.new(1000)
40
+ res = atomic.swap(1001)
41
+
42
+ assert_equal 1001, atomic.value
43
+ assert_equal 1000, res
34
44
  end
35
45
 
36
46
  def test_try_update_fails
37
- atomic = Atomic.new(0)
47
+ # use a number outside JRuby's fixnum cache range, to ensure identity is preserved
48
+ atomic = Atomic.new(1000)
38
49
  assert_raise Atomic::ConcurrentUpdateError do
39
50
  # assigning within block exploits implementation detail for test
40
- atomic.try_update{|v| atomic.value = 1 ; v + 1}
51
+ atomic.try_update{|v| atomic.value = 1001 ; v + 1}
41
52
  end
42
53
  end
43
54
 
44
55
  def test_update_retries
45
56
  tries = 0
46
- atomic = Atomic.new(0)
57
+ # use a number outside JRuby's fixnum cache range, to ensure identity is preserved
58
+ atomic = Atomic.new(1000)
47
59
  # assigning within block exploits implementation detail for test
48
- atomic.update{|v| tries += 1 ; atomic.value = 1 ; v + 1}
60
+ atomic.update{|v| tries += 1 ; atomic.value = 1001 ; v + 1}
49
61
  assert_equal 2, tries
50
62
  end
51
63
  end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 2
9
- version: 0.0.2
8
+ - 3
9
+ version: 0.0.3
10
10
  platform: ruby
11
11
  authors:
12
12
  - Charles Oliver Nutter
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-06-08 00:29:12.585000 -04:00
18
+ date: 2010-06-08 08:04:17.179000 -04:00
19
19
  default_executable:
20
20
  dependencies: []
21
21
 
@@ -31,8 +31,12 @@ extra_rdoc_files: []
31
31
 
32
32
  files:
33
33
  - lib/atomic.rb
34
+ - lib/atomic_reference.jar
35
+ - lib/atomicreference.jar
34
36
  - examples/atomic_example.rb
35
37
  - test/test_atomic.rb
38
+ - ext/AtomicReferenceService.java
39
+ - ext/org/jruby/ext/atomic/AtomicReferenceLibrary.java
36
40
  - README.txt
37
41
  - atomic.gemspec
38
42
  - Rakefile