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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +478 -0
- data/Gemfile +41 -0
- data/LICENSE.md +23 -0
- data/README.md +381 -0
- data/Rakefile +327 -0
- data/ext/concurrent-ruby/ConcurrentRubyService.java +17 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java +175 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java +248 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java +93 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +113 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +159 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +307 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java +31 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java +3863 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java +203 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java +342 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java +3800 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java +204 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java +291 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java +199 -0
- data/lib/concurrent-ruby.rb +1 -0
- data/lib/concurrent.rb +134 -0
- data/lib/concurrent/agent.rb +587 -0
- data/lib/concurrent/array.rb +66 -0
- data/lib/concurrent/async.rb +459 -0
- data/lib/concurrent/atom.rb +222 -0
- data/lib/concurrent/atomic/abstract_thread_local_var.rb +66 -0
- data/lib/concurrent/atomic/atomic_boolean.rb +126 -0
- data/lib/concurrent/atomic/atomic_fixnum.rb +143 -0
- data/lib/concurrent/atomic/atomic_markable_reference.rb +164 -0
- data/lib/concurrent/atomic/atomic_reference.rb +204 -0
- data/lib/concurrent/atomic/count_down_latch.rb +100 -0
- data/lib/concurrent/atomic/cyclic_barrier.rb +128 -0
- data/lib/concurrent/atomic/event.rb +109 -0
- data/lib/concurrent/atomic/java_count_down_latch.rb +42 -0
- data/lib/concurrent/atomic/java_thread_local_var.rb +37 -0
- data/lib/concurrent/atomic/mutex_atomic_boolean.rb +62 -0
- data/lib/concurrent/atomic/mutex_atomic_fixnum.rb +75 -0
- data/lib/concurrent/atomic/mutex_count_down_latch.rb +44 -0
- data/lib/concurrent/atomic/mutex_semaphore.rb +115 -0
- data/lib/concurrent/atomic/read_write_lock.rb +254 -0
- data/lib/concurrent/atomic/reentrant_read_write_lock.rb +379 -0
- data/lib/concurrent/atomic/ruby_thread_local_var.rb +161 -0
- data/lib/concurrent/atomic/semaphore.rb +145 -0
- data/lib/concurrent/atomic/thread_local_var.rb +104 -0
- data/lib/concurrent/atomic_reference/mutex_atomic.rb +56 -0
- data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +28 -0
- data/lib/concurrent/atomics.rb +10 -0
- data/lib/concurrent/collection/copy_on_notify_observer_set.rb +107 -0
- data/lib/concurrent/collection/copy_on_write_observer_set.rb +111 -0
- data/lib/concurrent/collection/java_non_concurrent_priority_queue.rb +84 -0
- data/lib/concurrent/collection/lock_free_stack.rb +158 -0
- data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +927 -0
- data/lib/concurrent/collection/map/mri_map_backend.rb +66 -0
- data/lib/concurrent/collection/map/non_concurrent_map_backend.rb +140 -0
- data/lib/concurrent/collection/map/synchronized_map_backend.rb +82 -0
- data/lib/concurrent/collection/non_concurrent_priority_queue.rb +143 -0
- data/lib/concurrent/collection/ruby_non_concurrent_priority_queue.rb +150 -0
- data/lib/concurrent/concern/deprecation.rb +34 -0
- data/lib/concurrent/concern/dereferenceable.rb +73 -0
- data/lib/concurrent/concern/logging.rb +32 -0
- data/lib/concurrent/concern/obligation.rb +220 -0
- data/lib/concurrent/concern/observable.rb +110 -0
- data/lib/concurrent/concurrent_ruby.jar +0 -0
- data/lib/concurrent/configuration.rb +184 -0
- data/lib/concurrent/constants.rb +8 -0
- data/lib/concurrent/dataflow.rb +81 -0
- data/lib/concurrent/delay.rb +199 -0
- data/lib/concurrent/errors.rb +69 -0
- data/lib/concurrent/exchanger.rb +352 -0
- data/lib/concurrent/executor/abstract_executor_service.rb +134 -0
- data/lib/concurrent/executor/cached_thread_pool.rb +62 -0
- data/lib/concurrent/executor/executor_service.rb +185 -0
- data/lib/concurrent/executor/fixed_thread_pool.rb +206 -0
- data/lib/concurrent/executor/immediate_executor.rb +66 -0
- data/lib/concurrent/executor/indirect_immediate_executor.rb +44 -0
- data/lib/concurrent/executor/java_executor_service.rb +91 -0
- data/lib/concurrent/executor/java_single_thread_executor.rb +29 -0
- data/lib/concurrent/executor/java_thread_pool_executor.rb +123 -0
- data/lib/concurrent/executor/ruby_executor_service.rb +78 -0
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +22 -0
- data/lib/concurrent/executor/ruby_thread_pool_executor.rb +362 -0
- data/lib/concurrent/executor/safe_task_executor.rb +35 -0
- data/lib/concurrent/executor/serial_executor_service.rb +34 -0
- data/lib/concurrent/executor/serialized_execution.rb +107 -0
- data/lib/concurrent/executor/serialized_execution_delegator.rb +28 -0
- data/lib/concurrent/executor/simple_executor_service.rb +100 -0
- data/lib/concurrent/executor/single_thread_executor.rb +56 -0
- data/lib/concurrent/executor/thread_pool_executor.rb +87 -0
- data/lib/concurrent/executor/timer_set.rb +173 -0
- data/lib/concurrent/executors.rb +20 -0
- data/lib/concurrent/future.rb +141 -0
- data/lib/concurrent/hash.rb +59 -0
- data/lib/concurrent/immutable_struct.rb +93 -0
- data/lib/concurrent/ivar.rb +207 -0
- data/lib/concurrent/map.rb +337 -0
- data/lib/concurrent/maybe.rb +229 -0
- data/lib/concurrent/mutable_struct.rb +229 -0
- data/lib/concurrent/mvar.rb +242 -0
- data/lib/concurrent/options.rb +42 -0
- data/lib/concurrent/promise.rb +579 -0
- data/lib/concurrent/promises.rb +2167 -0
- data/lib/concurrent/re_include.rb +58 -0
- data/lib/concurrent/scheduled_task.rb +318 -0
- data/lib/concurrent/set.rb +66 -0
- data/lib/concurrent/settable_struct.rb +129 -0
- data/lib/concurrent/synchronization.rb +30 -0
- data/lib/concurrent/synchronization/abstract_lockable_object.rb +98 -0
- data/lib/concurrent/synchronization/abstract_object.rb +24 -0
- data/lib/concurrent/synchronization/abstract_struct.rb +160 -0
- data/lib/concurrent/synchronization/condition.rb +60 -0
- data/lib/concurrent/synchronization/jruby_lockable_object.rb +13 -0
- data/lib/concurrent/synchronization/jruby_object.rb +45 -0
- data/lib/concurrent/synchronization/lock.rb +36 -0
- data/lib/concurrent/synchronization/lockable_object.rb +74 -0
- data/lib/concurrent/synchronization/mri_object.rb +44 -0
- data/lib/concurrent/synchronization/mutex_lockable_object.rb +76 -0
- data/lib/concurrent/synchronization/object.rb +183 -0
- data/lib/concurrent/synchronization/rbx_lockable_object.rb +65 -0
- data/lib/concurrent/synchronization/rbx_object.rb +49 -0
- data/lib/concurrent/synchronization/truffleruby_object.rb +47 -0
- data/lib/concurrent/synchronization/volatile.rb +36 -0
- data/lib/concurrent/thread_safe/synchronized_delegator.rb +50 -0
- data/lib/concurrent/thread_safe/util.rb +16 -0
- data/lib/concurrent/thread_safe/util/adder.rb +74 -0
- data/lib/concurrent/thread_safe/util/cheap_lockable.rb +118 -0
- data/lib/concurrent/thread_safe/util/data_structures.rb +63 -0
- data/lib/concurrent/thread_safe/util/power_of_two_tuple.rb +38 -0
- data/lib/concurrent/thread_safe/util/striped64.rb +246 -0
- data/lib/concurrent/thread_safe/util/volatile.rb +75 -0
- data/lib/concurrent/thread_safe/util/xor_shift_random.rb +50 -0
- data/lib/concurrent/timer_task.rb +334 -0
- data/lib/concurrent/tuple.rb +86 -0
- data/lib/concurrent/tvar.rb +258 -0
- data/lib/concurrent/utility/at_exit.rb +97 -0
- data/lib/concurrent/utility/engine.rb +56 -0
- data/lib/concurrent/utility/monotonic_time.rb +58 -0
- data/lib/concurrent/utility/native_extension_loader.rb +79 -0
- data/lib/concurrent/utility/native_integer.rb +53 -0
- data/lib/concurrent/utility/processor_counter.rb +158 -0
- data/lib/concurrent/version.rb +3 -0
- 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
|
+
}
|