concurrent-ruby 1.0.5 → 1.1.1

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 (109) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +65 -0
  3. data/Gemfile +39 -0
  4. data/{LICENSE.txt → LICENSE.md} +2 -0
  5. data/README.md +207 -105
  6. data/Rakefile +314 -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 +306 -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/agent.rb +7 -7
  23. data/lib/concurrent/array.rb +59 -32
  24. data/lib/concurrent/async.rb +4 -4
  25. data/lib/concurrent/atom.rb +9 -9
  26. data/lib/concurrent/atomic/atomic_boolean.rb +24 -20
  27. data/lib/concurrent/atomic/atomic_fixnum.rb +27 -23
  28. data/lib/concurrent/atomic/atomic_markable_reference.rb +164 -0
  29. data/lib/concurrent/atomic/atomic_reference.rb +185 -32
  30. data/lib/concurrent/atomic/count_down_latch.rb +6 -6
  31. data/lib/concurrent/atomic/cyclic_barrier.rb +1 -1
  32. data/lib/concurrent/atomic/event.rb +1 -1
  33. data/lib/concurrent/atomic/java_count_down_latch.rb +9 -6
  34. data/lib/concurrent/atomic/mutex_atomic_boolean.rb +2 -0
  35. data/lib/concurrent/atomic/mutex_count_down_latch.rb +1 -0
  36. data/lib/concurrent/atomic/read_write_lock.rb +2 -1
  37. data/lib/concurrent/atomic/reentrant_read_write_lock.rb +3 -1
  38. data/lib/concurrent/atomic/semaphore.rb +8 -8
  39. data/lib/concurrent/atomic/thread_local_var.rb +7 -7
  40. data/lib/concurrent/atomic_reference/mutex_atomic.rb +3 -8
  41. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +1 -1
  42. data/lib/concurrent/atomics.rb +0 -43
  43. data/lib/concurrent/collection/lock_free_stack.rb +158 -0
  44. data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +3 -3
  45. data/lib/concurrent/collection/map/non_concurrent_map_backend.rb +1 -2
  46. data/lib/concurrent/collection/non_concurrent_priority_queue.rb +29 -29
  47. data/lib/concurrent/concern/dereferenceable.rb +1 -1
  48. data/lib/concurrent/concern/logging.rb +6 -1
  49. data/lib/concurrent/concern/observable.rb +7 -7
  50. data/lib/concurrent/concurrent_ruby.jar +0 -0
  51. data/lib/concurrent/configuration.rb +1 -6
  52. data/lib/concurrent/constants.rb +1 -1
  53. data/lib/concurrent/dataflow.rb +2 -1
  54. data/lib/concurrent/delay.rb +9 -7
  55. data/lib/concurrent/exchanger.rb +21 -25
  56. data/lib/concurrent/executor/abstract_executor_service.rb +2 -2
  57. data/lib/concurrent/executor/cached_thread_pool.rb +1 -1
  58. data/lib/concurrent/executor/executor_service.rb +15 -15
  59. data/lib/concurrent/executor/fixed_thread_pool.rb +18 -18
  60. data/lib/concurrent/executor/java_thread_pool_executor.rb +10 -7
  61. data/lib/concurrent/executor/single_thread_executor.rb +2 -2
  62. data/lib/concurrent/executor/thread_pool_executor.rb +6 -6
  63. data/lib/concurrent/executor/timer_set.rb +1 -1
  64. data/lib/concurrent/future.rb +4 -1
  65. data/lib/concurrent/hash.rb +53 -30
  66. data/lib/concurrent/ivar.rb +5 -6
  67. data/lib/concurrent/map.rb +178 -81
  68. data/lib/concurrent/maybe.rb +1 -1
  69. data/lib/concurrent/mutable_struct.rb +15 -14
  70. data/lib/concurrent/mvar.rb +2 -2
  71. data/lib/concurrent/promise.rb +53 -21
  72. data/lib/concurrent/promises.rb +1936 -0
  73. data/lib/concurrent/re_include.rb +58 -0
  74. data/lib/concurrent/set.rb +66 -0
  75. data/lib/concurrent/settable_struct.rb +1 -0
  76. data/lib/concurrent/synchronization/abstract_lockable_object.rb +5 -5
  77. data/lib/concurrent/synchronization/abstract_struct.rb +6 -4
  78. data/lib/concurrent/synchronization/lockable_object.rb +6 -6
  79. data/lib/concurrent/synchronization/{mri_lockable_object.rb → mutex_lockable_object.rb} +19 -14
  80. data/lib/concurrent/synchronization/object.rb +8 -4
  81. data/lib/concurrent/synchronization/truffleruby_object.rb +46 -0
  82. data/lib/concurrent/synchronization/volatile.rb +11 -9
  83. data/lib/concurrent/synchronization.rb +4 -5
  84. data/lib/concurrent/thread_safe/util/data_structures.rb +63 -0
  85. data/lib/concurrent/thread_safe/util/striped64.rb +9 -4
  86. data/lib/concurrent/timer_task.rb +5 -2
  87. data/lib/concurrent/tuple.rb +1 -1
  88. data/lib/concurrent/tvar.rb +2 -2
  89. data/lib/concurrent/utility/193.rb +17 -0
  90. data/lib/concurrent/utility/at_exit.rb +1 -1
  91. data/lib/concurrent/utility/engine.rb +4 -4
  92. data/lib/concurrent/utility/monotonic_time.rb +3 -3
  93. data/lib/concurrent/utility/native_extension_loader.rb +31 -33
  94. data/lib/concurrent/utility/processor_counter.rb +0 -2
  95. data/lib/concurrent/version.rb +2 -2
  96. data/lib/concurrent-ruby.rb +1 -0
  97. data/lib/concurrent.rb +26 -20
  98. metadata +33 -18
  99. data/lib/concurrent/atomic_reference/concurrent_update_error.rb +0 -8
  100. data/lib/concurrent/atomic_reference/direct_update.rb +0 -81
  101. data/lib/concurrent/atomic_reference/jruby+truffle.rb +0 -2
  102. data/lib/concurrent/atomic_reference/jruby.rb +0 -16
  103. data/lib/concurrent/atomic_reference/rbx.rb +0 -22
  104. data/lib/concurrent/atomic_reference/ruby.rb +0 -32
  105. data/lib/concurrent/edge.rb +0 -26
  106. data/lib/concurrent/lazy_register.rb +0 -81
  107. data/lib/concurrent/synchronization/truffle_lockable_object.rb +0 -9
  108. data/lib/concurrent/synchronization/truffle_object.rb +0 -31
  109. data/lib/concurrent/thread_safe/util/array_hash_rbx.rb +0 -30
@@ -0,0 +1,306 @@
1
+ package com.concurrent_ruby.ext;
2
+
3
+ import org.jruby.Ruby;
4
+ import org.jruby.RubyBasicObject;
5
+ import org.jruby.RubyClass;
6
+ import org.jruby.RubyModule;
7
+ import org.jruby.RubyObject;
8
+ import org.jruby.RubyThread;
9
+ import org.jruby.anno.JRubyClass;
10
+ import org.jruby.anno.JRubyMethod;
11
+ import org.jruby.runtime.Block;
12
+ import org.jruby.runtime.ObjectAllocator;
13
+ import org.jruby.runtime.ThreadContext;
14
+ import org.jruby.runtime.Visibility;
15
+ import org.jruby.runtime.builtin.IRubyObject;
16
+ import org.jruby.runtime.load.Library;
17
+ import sun.misc.Unsafe;
18
+
19
+ import java.io.IOException;
20
+ import java.lang.reflect.Field;
21
+ import java.lang.reflect.Method;
22
+
23
+ public class SynchronizationLibrary implements Library {
24
+
25
+ private static final Unsafe UNSAFE = loadUnsafe();
26
+
27
+ private static Unsafe loadUnsafe() {
28
+ try {
29
+ Class ncdfe = Class.forName("sun.misc.Unsafe");
30
+ Field f = ncdfe.getDeclaredField("theUnsafe");
31
+ f.setAccessible(true);
32
+ return (Unsafe) f.get((java.lang.Object) null);
33
+ } catch (Exception var2) {
34
+ return null;
35
+ } catch (NoClassDefFoundError var3) {
36
+ return null;
37
+ }
38
+ }
39
+
40
+ private static boolean supportsFences() {
41
+ if (UNSAFE == null) {
42
+ return false;
43
+ } else {
44
+ try {
45
+ Method m = UNSAFE.getClass().getDeclaredMethod("fullFence", new Class[0]);
46
+ if (m != null) {
47
+ return true;
48
+ }
49
+ } catch (Exception var1) {
50
+ // nothing
51
+ }
52
+
53
+ return false;
54
+ }
55
+ }
56
+
57
+ private static final ObjectAllocator JRUBY_OBJECT_ALLOCATOR = new ObjectAllocator() {
58
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
59
+ return new JRubyObject(runtime, klazz);
60
+ }
61
+ };
62
+
63
+ private static final ObjectAllocator OBJECT_ALLOCATOR = new ObjectAllocator() {
64
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
65
+ return new Object(runtime, klazz);
66
+ }
67
+ };
68
+
69
+ private static final ObjectAllocator ABSTRACT_LOCKABLE_OBJECT_ALLOCATOR = new ObjectAllocator() {
70
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
71
+ return new AbstractLockableObject(runtime, klazz);
72
+ }
73
+ };
74
+
75
+ private static final ObjectAllocator JRUBY_LOCKABLE_OBJECT_ALLOCATOR = new ObjectAllocator() {
76
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
77
+ return new JRubyLockableObject(runtime, klazz);
78
+ }
79
+ };
80
+
81
+ public void load(Ruby runtime, boolean wrap) throws IOException {
82
+ RubyModule synchronizationModule = runtime.
83
+ defineModule("Concurrent").
84
+ defineModuleUnder("Synchronization");
85
+
86
+ RubyModule jrubyAttrVolatileModule = synchronizationModule.defineModuleUnder("JRubyAttrVolatile");
87
+ jrubyAttrVolatileModule.defineAnnotatedMethods(JRubyAttrVolatile.class);
88
+
89
+ defineClass(runtime, synchronizationModule, "AbstractObject", "JRubyObject",
90
+ JRubyObject.class, JRUBY_OBJECT_ALLOCATOR);
91
+
92
+ defineClass(runtime, synchronizationModule, "JRubyObject", "Object",
93
+ Object.class, OBJECT_ALLOCATOR);
94
+
95
+ defineClass(runtime, synchronizationModule, "Object", "AbstractLockableObject",
96
+ AbstractLockableObject.class, ABSTRACT_LOCKABLE_OBJECT_ALLOCATOR);
97
+
98
+ defineClass(runtime, synchronizationModule, "AbstractLockableObject", "JRubyLockableObject",
99
+ JRubyLockableObject.class, JRUBY_LOCKABLE_OBJECT_ALLOCATOR);
100
+
101
+ defineClass(runtime, synchronizationModule, "Object", "JRuby",
102
+ JRuby.class, new ObjectAllocator() {
103
+ @Override
104
+ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
105
+ return new JRuby(runtime, klazz);
106
+ }
107
+ });
108
+ }
109
+
110
+ private RubyClass defineClass(
111
+ Ruby runtime,
112
+ RubyModule namespace,
113
+ String parentName,
114
+ String name,
115
+ Class javaImplementation,
116
+ ObjectAllocator allocator) {
117
+ final RubyClass parentClass = namespace.getClass(parentName);
118
+
119
+ if (parentClass == null) {
120
+ System.out.println("not found " + parentName);
121
+ throw runtime.newRuntimeError(namespace.toString() + "::" + parentName + " is missing");
122
+ }
123
+
124
+ final RubyClass newClass = namespace.defineClassUnder(name, parentClass, allocator);
125
+ newClass.defineAnnotatedMethods(javaImplementation);
126
+ return newClass;
127
+ }
128
+
129
+ // Facts:
130
+ // - all ivar reads are without any synchronisation of fences see
131
+ // https://github.com/jruby/jruby/blob/master/core/src/main/java/org/jruby/runtime/ivars/VariableAccessor.java#L110-110
132
+ // - writes depend on UnsafeHolder.U, null -> SynchronizedVariableAccessor, !null -> StampedVariableAccessor
133
+ // SynchronizedVariableAccessor wraps with synchronized block, StampedVariableAccessor uses fullFence or
134
+ // volatilePut
135
+ // TODO (pitr 16-Sep-2015): what do we do in Java 9 ?
136
+
137
+ // module JRubyAttrVolatile
138
+ public static class JRubyAttrVolatile {
139
+
140
+ // volatile threadContext is used as a memory barrier per the JVM memory model happens-before semantic
141
+ // on volatile fields. any volatile field could have been used but using the thread context is an
142
+ // attempt to avoid code elimination.
143
+ private static volatile ThreadContext threadContext = null;
144
+
145
+ @JRubyMethod(name = "full_memory_barrier", visibility = Visibility.PUBLIC)
146
+ public static IRubyObject fullMemoryBarrier(ThreadContext context, IRubyObject self) {
147
+ // Prevent reordering of ivar writes with publication of this instance
148
+ if (!supportsFences()) {
149
+ // Assuming that following volatile read and write is not eliminated it simulates fullFence.
150
+ // If it's eliminated it'll cause problems only on non-x86 platforms.
151
+ // http://shipilev.net/blog/2014/jmm-pragmatics/#_happens_before_test_your_understanding
152
+ final ThreadContext oldContext = threadContext;
153
+ threadContext = context;
154
+ } else {
155
+ UNSAFE.fullFence();
156
+ }
157
+ return context.nil;
158
+ }
159
+
160
+ @JRubyMethod(name = "instance_variable_get_volatile", visibility = Visibility.PUBLIC)
161
+ public static IRubyObject instanceVariableGetVolatile(
162
+ ThreadContext context,
163
+ IRubyObject self,
164
+ IRubyObject name) {
165
+ // Ensure we ses latest value with loadFence
166
+ if (!supportsFences()) {
167
+ // piggybacking on volatile read, simulating loadFence
168
+ final ThreadContext oldContext = threadContext;
169
+ return ((RubyBasicObject) self).instance_variable_get(context, name);
170
+ } else {
171
+ UNSAFE.loadFence();
172
+ return ((RubyBasicObject) self).instance_variable_get(context, name);
173
+ }
174
+ }
175
+
176
+ @JRubyMethod(name = "instance_variable_set_volatile", visibility = Visibility.PUBLIC)
177
+ public static IRubyObject InstanceVariableSetVolatile(
178
+ ThreadContext context,
179
+ IRubyObject self,
180
+ IRubyObject name,
181
+ IRubyObject value) {
182
+ // Ensure we make last update visible
183
+ if (!supportsFences()) {
184
+ // piggybacking on volatile write, simulating storeFence
185
+ final IRubyObject result = ((RubyBasicObject) self).instance_variable_set(name, value);
186
+ threadContext = context;
187
+ return result;
188
+ } else {
189
+ // JRuby uses StampedVariableAccessor which calls fullFence
190
+ // so no additional steps needed.
191
+ // See https://github.com/jruby/jruby/blob/master/core/src/main/java/org/jruby/runtime/ivars/StampedVariableAccessor.java#L151-L159
192
+ return ((RubyBasicObject) self).instance_variable_set(name, value);
193
+ }
194
+ }
195
+ }
196
+
197
+ @JRubyClass(name = "JRubyObject", parent = "AbstractObject")
198
+ public static class JRubyObject extends RubyObject {
199
+
200
+ public JRubyObject(Ruby runtime, RubyClass metaClass) {
201
+ super(runtime, metaClass);
202
+ }
203
+ }
204
+
205
+ @JRubyClass(name = "Object", parent = "JRubyObject")
206
+ public static class Object extends JRubyObject {
207
+
208
+ public Object(Ruby runtime, RubyClass metaClass) {
209
+ super(runtime, metaClass);
210
+ }
211
+ }
212
+
213
+ @JRubyClass(name = "AbstractLockableObject", parent = "Object")
214
+ public static class AbstractLockableObject extends Object {
215
+
216
+ public AbstractLockableObject(Ruby runtime, RubyClass metaClass) {
217
+ super(runtime, metaClass);
218
+ }
219
+ }
220
+
221
+ @JRubyClass(name = "JRubyLockableObject", parent = "AbstractLockableObject")
222
+ public static class JRubyLockableObject extends JRubyObject {
223
+
224
+ public JRubyLockableObject(Ruby runtime, RubyClass metaClass) {
225
+ super(runtime, metaClass);
226
+ }
227
+
228
+ @JRubyMethod(name = "synchronize", visibility = Visibility.PROTECTED)
229
+ public IRubyObject rubySynchronize(ThreadContext context, Block block) {
230
+ synchronized (this) {
231
+ return block.yield(context, null);
232
+ }
233
+ }
234
+
235
+ @JRubyMethod(name = "ns_wait", optional = 1, visibility = Visibility.PROTECTED)
236
+ public IRubyObject nsWait(ThreadContext context, IRubyObject[] args) {
237
+ Ruby runtime = context.runtime;
238
+ if (args.length > 1) {
239
+ throw runtime.newArgumentError(args.length, 1);
240
+ }
241
+ Double timeout = null;
242
+ if (args.length > 0 && !args[0].isNil()) {
243
+ timeout = args[0].convertToFloat().getDoubleValue();
244
+ if (timeout < 0) {
245
+ throw runtime.newArgumentError("time interval must be positive");
246
+ }
247
+ }
248
+ if (Thread.interrupted()) {
249
+ throw runtime.newConcurrencyError("thread interrupted");
250
+ }
251
+ boolean success = false;
252
+ try {
253
+ success = context.getThread().wait_timeout(this, timeout);
254
+ } catch (InterruptedException ie) {
255
+ throw runtime.newConcurrencyError(ie.getLocalizedMessage());
256
+ } finally {
257
+ // An interrupt or timeout may have caused us to miss
258
+ // a notify that we consumed, so do another notify in
259
+ // case someone else is available to pick it up.
260
+ if (!success) {
261
+ this.notify();
262
+ }
263
+ }
264
+ return this;
265
+ }
266
+
267
+ @JRubyMethod(name = "ns_signal", visibility = Visibility.PROTECTED)
268
+ public IRubyObject nsSignal(ThreadContext context) {
269
+ notify();
270
+ return this;
271
+ }
272
+
273
+ @JRubyMethod(name = "ns_broadcast", visibility = Visibility.PROTECTED)
274
+ public IRubyObject nsBroadcast(ThreadContext context) {
275
+ notifyAll();
276
+ return this;
277
+ }
278
+ }
279
+
280
+ @JRubyClass(name = "JRuby")
281
+ public static class JRuby extends RubyObject {
282
+ public JRuby(Ruby runtime, RubyClass metaClass) {
283
+ super(runtime, metaClass);
284
+ }
285
+
286
+ @JRubyMethod(name = "sleep_interruptibly", visibility = Visibility.PUBLIC, module = true)
287
+ public static IRubyObject sleepInterruptibly(final ThreadContext context, IRubyObject receiver, final Block block) {
288
+ try {
289
+ context.getThread().executeBlockingTask(new RubyThread.BlockingTask() {
290
+ @Override
291
+ public void run() throws InterruptedException {
292
+ block.call(context);
293
+ }
294
+
295
+ @Override
296
+ public void wakeup() {
297
+ context.getThread().getNativeThread().interrupt();
298
+ }
299
+ });
300
+ } catch (InterruptedException e) {
301
+ throw context.runtime.newThreadError("interrupted in Concurrent::Synchronization::JRuby.sleep_interruptibly");
302
+ }
303
+ return context.nil;
304
+ }
305
+ }
306
+ }
@@ -0,0 +1,31 @@
1
+ package com.concurrent_ruby.ext.jsr166e;
2
+
3
+ import java.util.Map;
4
+ import java.util.Set;
5
+
6
+ public interface ConcurrentHashMap<K, V> {
7
+ /** Interface describing a function of one argument */
8
+ public interface Fun<A,T> { T apply(A a); }
9
+ /** Interface describing a function of two arguments */
10
+ public interface BiFun<A,B,T> { T apply(A a, B b); }
11
+
12
+ public V get(K key);
13
+ public V put(K key, V value);
14
+ public V putIfAbsent(K key, V value);
15
+ public V computeIfAbsent(K key, Fun<? super K, ? extends V> mf);
16
+ public V computeIfPresent(K key, BiFun<? super K, ? super V, ? extends V> mf);
17
+ public V compute(K key, BiFun<? super K, ? super V, ? extends V> mf);
18
+ public V merge(K key, V value, BiFun<? super V, ? super V, ? extends V> mf);
19
+ public boolean replace(K key, V oldVal, V newVal);
20
+ public V replace(K key, V value);
21
+ public boolean containsKey(K key);
22
+ public boolean remove(Object key, Object value);
23
+ public V remove(K key);
24
+ public void clear();
25
+ public Set<Map.Entry<K,V>> entrySet();
26
+ public int size();
27
+ public V getValueOrDefault(Object key, V defaultValue);
28
+
29
+ public boolean containsValue(V value);
30
+ public K findKey(V value);
31
+ }