concurrent-ruby 1.1.5

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.
Files changed (143) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +478 -0
  3. data/Gemfile +41 -0
  4. data/LICENSE.md +23 -0
  5. data/README.md +381 -0
  6. data/Rakefile +327 -0
  7. data/ext/concurrent-ruby/ConcurrentRubyService.java +17 -0
  8. data/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java +175 -0
  9. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java +248 -0
  10. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java +93 -0
  11. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +113 -0
  12. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +159 -0
  13. data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +307 -0
  14. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java +31 -0
  15. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java +3863 -0
  16. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java +203 -0
  17. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java +342 -0
  18. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java +3800 -0
  19. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java +204 -0
  20. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java +291 -0
  21. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java +199 -0
  22. data/lib/concurrent-ruby.rb +1 -0
  23. data/lib/concurrent.rb +134 -0
  24. data/lib/concurrent/agent.rb +587 -0
  25. data/lib/concurrent/array.rb +66 -0
  26. data/lib/concurrent/async.rb +459 -0
  27. data/lib/concurrent/atom.rb +222 -0
  28. data/lib/concurrent/atomic/abstract_thread_local_var.rb +66 -0
  29. data/lib/concurrent/atomic/atomic_boolean.rb +126 -0
  30. data/lib/concurrent/atomic/atomic_fixnum.rb +143 -0
  31. data/lib/concurrent/atomic/atomic_markable_reference.rb +164 -0
  32. data/lib/concurrent/atomic/atomic_reference.rb +204 -0
  33. data/lib/concurrent/atomic/count_down_latch.rb +100 -0
  34. data/lib/concurrent/atomic/cyclic_barrier.rb +128 -0
  35. data/lib/concurrent/atomic/event.rb +109 -0
  36. data/lib/concurrent/atomic/java_count_down_latch.rb +42 -0
  37. data/lib/concurrent/atomic/java_thread_local_var.rb +37 -0
  38. data/lib/concurrent/atomic/mutex_atomic_boolean.rb +62 -0
  39. data/lib/concurrent/atomic/mutex_atomic_fixnum.rb +75 -0
  40. data/lib/concurrent/atomic/mutex_count_down_latch.rb +44 -0
  41. data/lib/concurrent/atomic/mutex_semaphore.rb +115 -0
  42. data/lib/concurrent/atomic/read_write_lock.rb +254 -0
  43. data/lib/concurrent/atomic/reentrant_read_write_lock.rb +379 -0
  44. data/lib/concurrent/atomic/ruby_thread_local_var.rb +161 -0
  45. data/lib/concurrent/atomic/semaphore.rb +145 -0
  46. data/lib/concurrent/atomic/thread_local_var.rb +104 -0
  47. data/lib/concurrent/atomic_reference/mutex_atomic.rb +56 -0
  48. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +28 -0
  49. data/lib/concurrent/atomics.rb +10 -0
  50. data/lib/concurrent/collection/copy_on_notify_observer_set.rb +107 -0
  51. data/lib/concurrent/collection/copy_on_write_observer_set.rb +111 -0
  52. data/lib/concurrent/collection/java_non_concurrent_priority_queue.rb +84 -0
  53. data/lib/concurrent/collection/lock_free_stack.rb +158 -0
  54. data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +927 -0
  55. data/lib/concurrent/collection/map/mri_map_backend.rb +66 -0
  56. data/lib/concurrent/collection/map/non_concurrent_map_backend.rb +140 -0
  57. data/lib/concurrent/collection/map/synchronized_map_backend.rb +82 -0
  58. data/lib/concurrent/collection/non_concurrent_priority_queue.rb +143 -0
  59. data/lib/concurrent/collection/ruby_non_concurrent_priority_queue.rb +150 -0
  60. data/lib/concurrent/concern/deprecation.rb +34 -0
  61. data/lib/concurrent/concern/dereferenceable.rb +73 -0
  62. data/lib/concurrent/concern/logging.rb +32 -0
  63. data/lib/concurrent/concern/obligation.rb +220 -0
  64. data/lib/concurrent/concern/observable.rb +110 -0
  65. data/lib/concurrent/concurrent_ruby.jar +0 -0
  66. data/lib/concurrent/configuration.rb +184 -0
  67. data/lib/concurrent/constants.rb +8 -0
  68. data/lib/concurrent/dataflow.rb +81 -0
  69. data/lib/concurrent/delay.rb +199 -0
  70. data/lib/concurrent/errors.rb +69 -0
  71. data/lib/concurrent/exchanger.rb +352 -0
  72. data/lib/concurrent/executor/abstract_executor_service.rb +134 -0
  73. data/lib/concurrent/executor/cached_thread_pool.rb +62 -0
  74. data/lib/concurrent/executor/executor_service.rb +185 -0
  75. data/lib/concurrent/executor/fixed_thread_pool.rb +206 -0
  76. data/lib/concurrent/executor/immediate_executor.rb +66 -0
  77. data/lib/concurrent/executor/indirect_immediate_executor.rb +44 -0
  78. data/lib/concurrent/executor/java_executor_service.rb +91 -0
  79. data/lib/concurrent/executor/java_single_thread_executor.rb +29 -0
  80. data/lib/concurrent/executor/java_thread_pool_executor.rb +123 -0
  81. data/lib/concurrent/executor/ruby_executor_service.rb +78 -0
  82. data/lib/concurrent/executor/ruby_single_thread_executor.rb +22 -0
  83. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +362 -0
  84. data/lib/concurrent/executor/safe_task_executor.rb +35 -0
  85. data/lib/concurrent/executor/serial_executor_service.rb +34 -0
  86. data/lib/concurrent/executor/serialized_execution.rb +107 -0
  87. data/lib/concurrent/executor/serialized_execution_delegator.rb +28 -0
  88. data/lib/concurrent/executor/simple_executor_service.rb +100 -0
  89. data/lib/concurrent/executor/single_thread_executor.rb +56 -0
  90. data/lib/concurrent/executor/thread_pool_executor.rb +87 -0
  91. data/lib/concurrent/executor/timer_set.rb +173 -0
  92. data/lib/concurrent/executors.rb +20 -0
  93. data/lib/concurrent/future.rb +141 -0
  94. data/lib/concurrent/hash.rb +59 -0
  95. data/lib/concurrent/immutable_struct.rb +93 -0
  96. data/lib/concurrent/ivar.rb +207 -0
  97. data/lib/concurrent/map.rb +337 -0
  98. data/lib/concurrent/maybe.rb +229 -0
  99. data/lib/concurrent/mutable_struct.rb +229 -0
  100. data/lib/concurrent/mvar.rb +242 -0
  101. data/lib/concurrent/options.rb +42 -0
  102. data/lib/concurrent/promise.rb +579 -0
  103. data/lib/concurrent/promises.rb +2167 -0
  104. data/lib/concurrent/re_include.rb +58 -0
  105. data/lib/concurrent/scheduled_task.rb +318 -0
  106. data/lib/concurrent/set.rb +66 -0
  107. data/lib/concurrent/settable_struct.rb +129 -0
  108. data/lib/concurrent/synchronization.rb +30 -0
  109. data/lib/concurrent/synchronization/abstract_lockable_object.rb +98 -0
  110. data/lib/concurrent/synchronization/abstract_object.rb +24 -0
  111. data/lib/concurrent/synchronization/abstract_struct.rb +160 -0
  112. data/lib/concurrent/synchronization/condition.rb +60 -0
  113. data/lib/concurrent/synchronization/jruby_lockable_object.rb +13 -0
  114. data/lib/concurrent/synchronization/jruby_object.rb +45 -0
  115. data/lib/concurrent/synchronization/lock.rb +36 -0
  116. data/lib/concurrent/synchronization/lockable_object.rb +74 -0
  117. data/lib/concurrent/synchronization/mri_object.rb +44 -0
  118. data/lib/concurrent/synchronization/mutex_lockable_object.rb +76 -0
  119. data/lib/concurrent/synchronization/object.rb +183 -0
  120. data/lib/concurrent/synchronization/rbx_lockable_object.rb +65 -0
  121. data/lib/concurrent/synchronization/rbx_object.rb +49 -0
  122. data/lib/concurrent/synchronization/truffleruby_object.rb +47 -0
  123. data/lib/concurrent/synchronization/volatile.rb +36 -0
  124. data/lib/concurrent/thread_safe/synchronized_delegator.rb +50 -0
  125. data/lib/concurrent/thread_safe/util.rb +16 -0
  126. data/lib/concurrent/thread_safe/util/adder.rb +74 -0
  127. data/lib/concurrent/thread_safe/util/cheap_lockable.rb +118 -0
  128. data/lib/concurrent/thread_safe/util/data_structures.rb +63 -0
  129. data/lib/concurrent/thread_safe/util/power_of_two_tuple.rb +38 -0
  130. data/lib/concurrent/thread_safe/util/striped64.rb +246 -0
  131. data/lib/concurrent/thread_safe/util/volatile.rb +75 -0
  132. data/lib/concurrent/thread_safe/util/xor_shift_random.rb +50 -0
  133. data/lib/concurrent/timer_task.rb +334 -0
  134. data/lib/concurrent/tuple.rb +86 -0
  135. data/lib/concurrent/tvar.rb +258 -0
  136. data/lib/concurrent/utility/at_exit.rb +97 -0
  137. data/lib/concurrent/utility/engine.rb +56 -0
  138. data/lib/concurrent/utility/monotonic_time.rb +58 -0
  139. data/lib/concurrent/utility/native_extension_loader.rb +79 -0
  140. data/lib/concurrent/utility/native_integer.rb +53 -0
  141. data/lib/concurrent/utility/processor_counter.rb +158 -0
  142. data/lib/concurrent/version.rb +3 -0
  143. metadata +193 -0
@@ -0,0 +1,17 @@
1
+ import org.jruby.Ruby;
2
+ import org.jruby.runtime.load.BasicLibraryService;
3
+
4
+ import java.io.IOException;
5
+
6
+ public class ConcurrentRubyService implements BasicLibraryService {
7
+
8
+ public boolean basicLoad(final Ruby runtime) throws IOException {
9
+ new com.concurrent_ruby.ext.AtomicReferenceLibrary().load(runtime, false);
10
+ new com.concurrent_ruby.ext.JavaAtomicBooleanLibrary().load(runtime, false);
11
+ new com.concurrent_ruby.ext.JavaAtomicFixnumLibrary().load(runtime, false);
12
+ new com.concurrent_ruby.ext.JavaSemaphoreLibrary().load(runtime, false);
13
+ new com.concurrent_ruby.ext.SynchronizationLibrary().load(runtime, false);
14
+ new com.concurrent_ruby.ext.JRubyMapBackendLibrary().load(runtime, false);
15
+ return true;
16
+ }
17
+ }
@@ -0,0 +1,175 @@
1
+ package com.concurrent_ruby.ext;
2
+
3
+ import java.lang.reflect.Field;
4
+ import java.io.IOException;
5
+ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
6
+ import org.jruby.Ruby;
7
+ import org.jruby.RubyClass;
8
+ import org.jruby.RubyModule;
9
+ import org.jruby.RubyNumeric;
10
+ import org.jruby.RubyObject;
11
+ import org.jruby.anno.JRubyClass;
12
+ import org.jruby.anno.JRubyMethod;
13
+ import org.jruby.runtime.ObjectAllocator;
14
+ import org.jruby.runtime.ThreadContext;
15
+ import org.jruby.runtime.builtin.IRubyObject;
16
+ import org.jruby.runtime.load.Library;
17
+
18
+ /**
19
+ * This library adds an atomic reference type to JRuby for use in the atomic
20
+ * library. We do a native version to avoid the implicit value coercion that
21
+ * normally happens through JI.
22
+ *
23
+ * @author headius
24
+ */
25
+ public class AtomicReferenceLibrary implements Library {
26
+ public void load(Ruby runtime, boolean wrap) throws IOException {
27
+ RubyModule concurrentMod = runtime.defineModule("Concurrent");
28
+ RubyClass atomicCls = concurrentMod.defineClassUnder("JavaAtomicReference", runtime.getObject(), JRUBYREFERENCE_ALLOCATOR);
29
+ try {
30
+ sun.misc.Unsafe.class.getMethod("getAndSetObject", Object.class);
31
+ atomicCls.setAllocator(JRUBYREFERENCE8_ALLOCATOR);
32
+ } catch (Exception e) {
33
+ // leave it as Java 6/7 version
34
+ }
35
+ atomicCls.defineAnnotatedMethods(JRubyReference.class);
36
+ }
37
+
38
+ private static final ObjectAllocator JRUBYREFERENCE_ALLOCATOR = new ObjectAllocator() {
39
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
40
+ return new JRubyReference(runtime, klazz);
41
+ }
42
+ };
43
+
44
+ private static final ObjectAllocator JRUBYREFERENCE8_ALLOCATOR = new ObjectAllocator() {
45
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
46
+ return new JRubyReference8(runtime, klazz);
47
+ }
48
+ };
49
+
50
+ @JRubyClass(name="JRubyReference", parent="Object")
51
+ public static class JRubyReference extends RubyObject {
52
+ volatile IRubyObject reference;
53
+
54
+ static final sun.misc.Unsafe UNSAFE;
55
+ static final long referenceOffset;
56
+
57
+ static {
58
+ try {
59
+ UNSAFE = UnsafeHolder.U;
60
+ Class k = JRubyReference.class;
61
+ referenceOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("reference"));
62
+ } catch (Exception e) {
63
+ throw new RuntimeException(e);
64
+ }
65
+ }
66
+
67
+ public JRubyReference(Ruby runtime, RubyClass klass) {
68
+ super(runtime, klass);
69
+ }
70
+
71
+ @JRubyMethod
72
+ public IRubyObject initialize(ThreadContext context) {
73
+ UNSAFE.putObject(this, referenceOffset, context.nil);
74
+ return context.nil;
75
+ }
76
+
77
+ @JRubyMethod
78
+ public IRubyObject initialize(ThreadContext context, IRubyObject value) {
79
+ UNSAFE.putObject(this, referenceOffset, value);
80
+ return context.nil;
81
+ }
82
+
83
+ @JRubyMethod(name = {"get", "value"})
84
+ public IRubyObject get() {
85
+ return reference;
86
+ }
87
+
88
+ @JRubyMethod(name = {"set", "value="})
89
+ public IRubyObject set(IRubyObject newValue) {
90
+ UNSAFE.putObjectVolatile(this, referenceOffset, newValue);
91
+ return newValue;
92
+ }
93
+
94
+ @JRubyMethod(name = {"compare_and_set", "compare_and_swap"})
95
+ public IRubyObject compare_and_set(ThreadContext context, IRubyObject expectedValue, IRubyObject newValue) {
96
+ Ruby runtime = context.runtime;
97
+
98
+ if (expectedValue instanceof RubyNumeric) {
99
+ // numerics are not always idempotent in Ruby, so we need to do slower logic
100
+ return compareAndSetNumeric(context, expectedValue, newValue);
101
+ }
102
+
103
+ return runtime.newBoolean(UNSAFE.compareAndSwapObject(this, referenceOffset, expectedValue, newValue));
104
+ }
105
+
106
+ @JRubyMethod(name = {"get_and_set", "swap"})
107
+ public IRubyObject get_and_set(ThreadContext context, IRubyObject newValue) {
108
+ // less-efficient version for Java 6 and 7
109
+ while (true) {
110
+ IRubyObject oldValue = get();
111
+ if (UNSAFE.compareAndSwapObject(this, referenceOffset, oldValue, newValue)) {
112
+ return oldValue;
113
+ }
114
+ }
115
+ }
116
+
117
+ private IRubyObject compareAndSetNumeric(ThreadContext context, IRubyObject expectedValue, IRubyObject newValue) {
118
+ Ruby runtime = context.runtime;
119
+
120
+ // loop until:
121
+ // * reference CAS would succeed for same-valued objects
122
+ // * current and expected have different values as determined by #equals
123
+ while (true) {
124
+ IRubyObject current = reference;
125
+
126
+ if (!(current instanceof RubyNumeric)) {
127
+ // old value is not numeric, CAS fails
128
+ return runtime.getFalse();
129
+ }
130
+
131
+ RubyNumeric currentNumber = (RubyNumeric)current;
132
+ if (!currentNumber.equals(expectedValue)) {
133
+ // current number does not equal expected, fail CAS
134
+ return runtime.getFalse();
135
+ }
136
+
137
+ // check that current has not changed, or else allow loop to repeat
138
+ boolean success = UNSAFE.compareAndSwapObject(this, referenceOffset, current, newValue);
139
+ if (success) {
140
+ // value is same and did not change in interim...success
141
+ return runtime.getTrue();
142
+ }
143
+ }
144
+ }
145
+ }
146
+
147
+ private static final class UnsafeHolder {
148
+ private UnsafeHolder(){}
149
+
150
+ public static final sun.misc.Unsafe U = loadUnsafe();
151
+
152
+ private static sun.misc.Unsafe loadUnsafe() {
153
+ try {
154
+ Class unsafeClass = Class.forName("sun.misc.Unsafe");
155
+ Field f = unsafeClass.getDeclaredField("theUnsafe");
156
+ f.setAccessible(true);
157
+ return (sun.misc.Unsafe) f.get(null);
158
+ } catch (Exception e) {
159
+ return null;
160
+ }
161
+ }
162
+ }
163
+
164
+ public static class JRubyReference8 extends JRubyReference {
165
+ public JRubyReference8(Ruby runtime, RubyClass klass) {
166
+ super(runtime, klass);
167
+ }
168
+
169
+ @Override
170
+ public IRubyObject get_and_set(ThreadContext context, IRubyObject newValue) {
171
+ // efficient version for Java 8
172
+ return (IRubyObject)UNSAFE.getAndSetObject(this, referenceOffset, newValue);
173
+ }
174
+ }
175
+ }
@@ -0,0 +1,248 @@
1
+ package com.concurrent_ruby.ext;
2
+
3
+ import org.jruby.*;
4
+ import org.jruby.anno.JRubyClass;
5
+ import org.jruby.anno.JRubyMethod;
6
+ import com.concurrent_ruby.ext.jsr166e.ConcurrentHashMap;
7
+ import com.concurrent_ruby.ext.jsr166e.ConcurrentHashMapV8;
8
+ import com.concurrent_ruby.ext.jsr166e.nounsafe.*;
9
+ import org.jruby.runtime.Block;
10
+ import org.jruby.runtime.ObjectAllocator;
11
+ import org.jruby.runtime.ThreadContext;
12
+ import org.jruby.runtime.builtin.IRubyObject;
13
+ import org.jruby.runtime.load.Library;
14
+
15
+ import java.io.IOException;
16
+ import java.util.Map;
17
+
18
+ import static org.jruby.runtime.Visibility.PRIVATE;
19
+
20
+ /**
21
+ * Native Java implementation to avoid the JI overhead.
22
+ *
23
+ * @author thedarkone
24
+ */
25
+ public class JRubyMapBackendLibrary implements Library {
26
+ public void load(Ruby runtime, boolean wrap) throws IOException {
27
+
28
+ RubyModule concurrentMod = runtime.defineModule("Concurrent");
29
+ RubyModule thread_safeMod = concurrentMod.defineModuleUnder("Collection");
30
+ RubyClass jrubyRefClass = thread_safeMod.defineClassUnder("JRubyMapBackend", runtime.getObject(), BACKEND_ALLOCATOR);
31
+ jrubyRefClass.setAllocator(BACKEND_ALLOCATOR);
32
+ jrubyRefClass.defineAnnotatedMethods(JRubyMapBackend.class);
33
+ }
34
+
35
+ private static final ObjectAllocator BACKEND_ALLOCATOR = new ObjectAllocator() {
36
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
37
+ return new JRubyMapBackend(runtime, klazz);
38
+ }
39
+ };
40
+
41
+ @JRubyClass(name="JRubyMapBackend", parent="Object")
42
+ public static class JRubyMapBackend extends RubyObject {
43
+ // Defaults used by the CHM
44
+ static final int DEFAULT_INITIAL_CAPACITY = 16;
45
+ static final float DEFAULT_LOAD_FACTOR = 0.75f;
46
+
47
+ public static final boolean CAN_USE_UNSAFE_CHM = canUseUnsafeCHM();
48
+
49
+ private ConcurrentHashMap<IRubyObject, IRubyObject> map;
50
+
51
+ private static ConcurrentHashMap<IRubyObject, IRubyObject> newCHM(int initialCapacity, float loadFactor) {
52
+ if (CAN_USE_UNSAFE_CHM) {
53
+ return new ConcurrentHashMapV8<IRubyObject, IRubyObject>(initialCapacity, loadFactor);
54
+ } else {
55
+ return new com.concurrent_ruby.ext.jsr166e.nounsafe.ConcurrentHashMapV8<IRubyObject, IRubyObject>(initialCapacity, loadFactor);
56
+ }
57
+ }
58
+
59
+ private static ConcurrentHashMap<IRubyObject, IRubyObject> newCHM() {
60
+ return newCHM(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
61
+ }
62
+
63
+ private static boolean canUseUnsafeCHM() {
64
+ try {
65
+ new com.concurrent_ruby.ext.jsr166e.ConcurrentHashMapV8(); // force class load and initialization
66
+ return true;
67
+ } catch (Throwable t) { // ensuring we really do catch everything
68
+ // Doug's Unsafe setup errors always have this "Could not ini.." message
69
+ if (isCausedBySecurityException(t)) {
70
+ return false;
71
+ }
72
+ throw (t instanceof RuntimeException ? (RuntimeException) t : new RuntimeException(t));
73
+ }
74
+ }
75
+
76
+ private static boolean isCausedBySecurityException(Throwable t) {
77
+ while (t != null) {
78
+ if ((t.getMessage() != null && t.getMessage().contains("Could not initialize intrinsics")) || t instanceof SecurityException) {
79
+ return true;
80
+ }
81
+ t = t.getCause();
82
+ }
83
+ return false;
84
+ }
85
+
86
+ public JRubyMapBackend(Ruby runtime, RubyClass klass) {
87
+ super(runtime, klass);
88
+ }
89
+
90
+ @JRubyMethod
91
+ public IRubyObject initialize(ThreadContext context) {
92
+ map = newCHM();
93
+ return context.getRuntime().getNil();
94
+ }
95
+
96
+ @JRubyMethod
97
+ public IRubyObject initialize(ThreadContext context, IRubyObject options) {
98
+ map = toCHM(context, options);
99
+ return context.getRuntime().getNil();
100
+ }
101
+
102
+ private ConcurrentHashMap<IRubyObject, IRubyObject> toCHM(ThreadContext context, IRubyObject options) {
103
+ Ruby runtime = context.getRuntime();
104
+ if (!options.isNil() && options.respondsTo("[]")) {
105
+ IRubyObject rInitialCapacity = options.callMethod(context, "[]", runtime.newSymbol("initial_capacity"));
106
+ IRubyObject rLoadFactor = options.callMethod(context, "[]", runtime.newSymbol("load_factor"));
107
+ int initialCapacity = !rInitialCapacity.isNil() ? RubyNumeric.num2int(rInitialCapacity.convertToInteger()) : DEFAULT_INITIAL_CAPACITY;
108
+ float loadFactor = !rLoadFactor.isNil() ? (float)RubyNumeric.num2dbl(rLoadFactor.convertToFloat()) : DEFAULT_LOAD_FACTOR;
109
+ return newCHM(initialCapacity, loadFactor);
110
+ } else {
111
+ return newCHM();
112
+ }
113
+ }
114
+
115
+ @JRubyMethod(name = "[]", required = 1)
116
+ public IRubyObject op_aref(ThreadContext context, IRubyObject key) {
117
+ IRubyObject value;
118
+ return ((value = map.get(key)) == null) ? context.getRuntime().getNil() : value;
119
+ }
120
+
121
+ @JRubyMethod(name = {"[]="}, required = 2)
122
+ public IRubyObject op_aset(IRubyObject key, IRubyObject value) {
123
+ map.put(key, value);
124
+ return value;
125
+ }
126
+
127
+ @JRubyMethod
128
+ public IRubyObject put_if_absent(IRubyObject key, IRubyObject value) {
129
+ IRubyObject result = map.putIfAbsent(key, value);
130
+ return result == null ? getRuntime().getNil() : result;
131
+ }
132
+
133
+ @JRubyMethod
134
+ public IRubyObject compute_if_absent(final ThreadContext context, final IRubyObject key, final Block block) {
135
+ return map.computeIfAbsent(key, new ConcurrentHashMap.Fun<IRubyObject, IRubyObject>() {
136
+ @Override
137
+ public IRubyObject apply(IRubyObject key) {
138
+ return block.yieldSpecific(context);
139
+ }
140
+ });
141
+ }
142
+
143
+ @JRubyMethod
144
+ public IRubyObject compute_if_present(final ThreadContext context, final IRubyObject key, final Block block) {
145
+ IRubyObject result = map.computeIfPresent(key, new ConcurrentHashMap.BiFun<IRubyObject, IRubyObject, IRubyObject>() {
146
+ @Override
147
+ public IRubyObject apply(IRubyObject key, IRubyObject oldValue) {
148
+ IRubyObject result = block.yieldSpecific(context, oldValue == null ? context.getRuntime().getNil() : oldValue);
149
+ return result.isNil() ? null : result;
150
+ }
151
+ });
152
+ return result == null ? context.getRuntime().getNil() : result;
153
+ }
154
+
155
+ @JRubyMethod
156
+ public IRubyObject compute(final ThreadContext context, final IRubyObject key, final Block block) {
157
+ IRubyObject result = map.compute(key, new ConcurrentHashMap.BiFun<IRubyObject, IRubyObject, IRubyObject>() {
158
+ @Override
159
+ public IRubyObject apply(IRubyObject key, IRubyObject oldValue) {
160
+ IRubyObject result = block.yieldSpecific(context, oldValue == null ? context.getRuntime().getNil() : oldValue);
161
+ return result.isNil() ? null : result;
162
+ }
163
+ });
164
+ return result == null ? context.getRuntime().getNil() : result;
165
+ }
166
+
167
+ @JRubyMethod
168
+ public IRubyObject merge_pair(final ThreadContext context, final IRubyObject key, final IRubyObject value, final Block block) {
169
+ IRubyObject result = map.merge(key, value, new ConcurrentHashMap.BiFun<IRubyObject, IRubyObject, IRubyObject>() {
170
+ @Override
171
+ public IRubyObject apply(IRubyObject oldValue, IRubyObject newValue) {
172
+ IRubyObject result = block.yieldSpecific(context, oldValue == null ? context.getRuntime().getNil() : oldValue);
173
+ return result.isNil() ? null : result;
174
+ }
175
+ });
176
+ return result == null ? context.getRuntime().getNil() : result;
177
+ }
178
+
179
+ @JRubyMethod
180
+ public RubyBoolean replace_pair(IRubyObject key, IRubyObject oldValue, IRubyObject newValue) {
181
+ return getRuntime().newBoolean(map.replace(key, oldValue, newValue));
182
+ }
183
+
184
+ @JRubyMethod(name = "key?", required = 1)
185
+ public RubyBoolean has_key_p(IRubyObject key) {
186
+ return map.containsKey(key) ? getRuntime().getTrue() : getRuntime().getFalse();
187
+ }
188
+
189
+ @JRubyMethod
190
+ public IRubyObject key(IRubyObject value) {
191
+ final IRubyObject key = map.findKey(value);
192
+ return key == null ? getRuntime().getNil() : key;
193
+ }
194
+
195
+ @JRubyMethod
196
+ public IRubyObject replace_if_exists(IRubyObject key, IRubyObject value) {
197
+ IRubyObject result = map.replace(key, value);
198
+ return result == null ? getRuntime().getNil() : result;
199
+ }
200
+
201
+ @JRubyMethod
202
+ public IRubyObject get_and_set(IRubyObject key, IRubyObject value) {
203
+ IRubyObject result = map.put(key, value);
204
+ return result == null ? getRuntime().getNil() : result;
205
+ }
206
+
207
+ @JRubyMethod
208
+ public IRubyObject delete(IRubyObject key) {
209
+ IRubyObject result = map.remove(key);
210
+ return result == null ? getRuntime().getNil() : result;
211
+ }
212
+
213
+ @JRubyMethod
214
+ public RubyBoolean delete_pair(IRubyObject key, IRubyObject value) {
215
+ return getRuntime().newBoolean(map.remove(key, value));
216
+ }
217
+
218
+ @JRubyMethod
219
+ public IRubyObject clear() {
220
+ map.clear();
221
+ return this;
222
+ }
223
+
224
+ @JRubyMethod
225
+ public IRubyObject each_pair(ThreadContext context, Block block) {
226
+ for (Map.Entry<IRubyObject,IRubyObject> entry : map.entrySet()) {
227
+ block.yieldSpecific(context, entry.getKey(), entry.getValue());
228
+ }
229
+ return this;
230
+ }
231
+
232
+ @JRubyMethod
233
+ public RubyFixnum size(ThreadContext context) {
234
+ return context.getRuntime().newFixnum(map.size());
235
+ }
236
+
237
+ @JRubyMethod
238
+ public IRubyObject get_or_default(IRubyObject key, IRubyObject defaultValue) {
239
+ return map.getValueOrDefault(key, defaultValue);
240
+ }
241
+
242
+ @JRubyMethod(visibility = PRIVATE)
243
+ public JRubyMapBackend initialize_copy(ThreadContext context, IRubyObject other) {
244
+ map = newCHM();
245
+ return this;
246
+ }
247
+ }
248
+ }
@@ -0,0 +1,93 @@
1
+ package com.concurrent_ruby.ext;
2
+
3
+ import org.jruby.Ruby;
4
+ import org.jruby.RubyBoolean;
5
+ import org.jruby.RubyClass;
6
+ import org.jruby.RubyModule;
7
+ import org.jruby.RubyNil;
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
+ import java.io.IOException;
17
+ import java.util.concurrent.atomic.AtomicBoolean;
18
+
19
+ public class JavaAtomicBooleanLibrary implements Library {
20
+
21
+ public void load(Ruby runtime, boolean wrap) throws IOException {
22
+ RubyModule concurrentMod = runtime.defineModule("Concurrent");
23
+ RubyClass atomicCls = concurrentMod.defineClassUnder("JavaAtomicBoolean", runtime.getObject(), JRUBYREFERENCE_ALLOCATOR);
24
+ atomicCls.defineAnnotatedMethods(JavaAtomicBoolean.class);
25
+ }
26
+
27
+ private static final ObjectAllocator JRUBYREFERENCE_ALLOCATOR = new ObjectAllocator() {
28
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
29
+ return new JavaAtomicBoolean(runtime, klazz);
30
+ }
31
+ };
32
+
33
+ @JRubyClass(name = "JavaAtomicBoolean", parent = "Object")
34
+ public static class JavaAtomicBoolean extends RubyObject {
35
+
36
+ private AtomicBoolean atomicBoolean;
37
+
38
+ public JavaAtomicBoolean(Ruby runtime, RubyClass metaClass) {
39
+ super(runtime, metaClass);
40
+ }
41
+
42
+ @JRubyMethod
43
+ public IRubyObject initialize(ThreadContext context, IRubyObject value) {
44
+ atomicBoolean = new AtomicBoolean(convertRubyBooleanToJavaBoolean(value));
45
+ return context.nil;
46
+ }
47
+
48
+ @JRubyMethod
49
+ public IRubyObject initialize(ThreadContext context) {
50
+ atomicBoolean = new AtomicBoolean();
51
+ return context.nil;
52
+ }
53
+
54
+ @JRubyMethod(name = "value")
55
+ public IRubyObject value() {
56
+ return getRuntime().newBoolean(atomicBoolean.get());
57
+ }
58
+
59
+ @JRubyMethod(name = "true?")
60
+ public IRubyObject isAtomicTrue() {
61
+ return getRuntime().newBoolean(atomicBoolean.get());
62
+ }
63
+
64
+ @JRubyMethod(name = "false?")
65
+ public IRubyObject isAtomicFalse() {
66
+ return getRuntime().newBoolean((atomicBoolean.get() == false));
67
+ }
68
+
69
+ @JRubyMethod(name = "value=")
70
+ public IRubyObject setAtomic(ThreadContext context, IRubyObject newValue) {
71
+ atomicBoolean.set(convertRubyBooleanToJavaBoolean(newValue));
72
+ return context.nil;
73
+ }
74
+
75
+ @JRubyMethod(name = "make_true")
76
+ public IRubyObject makeTrue() {
77
+ return getRuntime().newBoolean(atomicBoolean.compareAndSet(false, true));
78
+ }
79
+
80
+ @JRubyMethod(name = "make_false")
81
+ public IRubyObject makeFalse() {
82
+ return getRuntime().newBoolean(atomicBoolean.compareAndSet(true, false));
83
+ }
84
+
85
+ private boolean convertRubyBooleanToJavaBoolean(IRubyObject newValue) {
86
+ if (newValue instanceof RubyBoolean.False || newValue instanceof RubyNil) {
87
+ return false;
88
+ } else {
89
+ return true;
90
+ }
91
+ }
92
+ }
93
+ }