concurrent-ruby 0.9.2-java → 1.0.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +49 -1
  3. data/README.md +86 -120
  4. data/lib/concurrent.rb +14 -5
  5. data/lib/concurrent/agent.rb +587 -0
  6. data/lib/concurrent/array.rb +39 -0
  7. data/lib/concurrent/async.rb +296 -149
  8. data/lib/concurrent/atom.rb +135 -45
  9. data/lib/concurrent/atomic/abstract_thread_local_var.rb +38 -0
  10. data/lib/concurrent/atomic/atomic_boolean.rb +83 -118
  11. data/lib/concurrent/atomic/atomic_fixnum.rb +101 -163
  12. data/lib/concurrent/atomic/atomic_reference.rb +1 -8
  13. data/lib/concurrent/atomic/count_down_latch.rb +62 -103
  14. data/lib/concurrent/atomic/cyclic_barrier.rb +3 -1
  15. data/lib/concurrent/atomic/event.rb +1 -1
  16. data/lib/concurrent/atomic/java_count_down_latch.rb +39 -0
  17. data/lib/concurrent/atomic/java_thread_local_var.rb +50 -0
  18. data/lib/concurrent/atomic/mutex_atomic_boolean.rb +60 -0
  19. data/lib/concurrent/atomic/mutex_atomic_fixnum.rb +91 -0
  20. data/lib/concurrent/atomic/mutex_count_down_latch.rb +43 -0
  21. data/lib/concurrent/atomic/mutex_semaphore.rb +115 -0
  22. data/lib/concurrent/atomic/read_write_lock.rb +5 -4
  23. data/lib/concurrent/atomic/reentrant_read_write_lock.rb +3 -1
  24. data/lib/concurrent/atomic/ruby_thread_local_var.rb +172 -0
  25. data/lib/concurrent/atomic/semaphore.rb +84 -178
  26. data/lib/concurrent/atomic/thread_local_var.rb +65 -294
  27. data/lib/concurrent/atomic_reference/jruby+truffle.rb +1 -0
  28. data/lib/concurrent/atomic_reference/jruby.rb +1 -1
  29. data/lib/concurrent/atomic_reference/mutex_atomic.rb +14 -8
  30. data/lib/concurrent/atomic_reference/ruby.rb +1 -1
  31. data/lib/concurrent/atomics.rb +7 -37
  32. data/lib/concurrent/collection/copy_on_notify_observer_set.rb +7 -15
  33. data/lib/concurrent/collection/copy_on_write_observer_set.rb +7 -15
  34. data/lib/concurrent/collection/java_non_concurrent_priority_queue.rb +84 -0
  35. data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +927 -0
  36. data/lib/concurrent/collection/map/mri_map_backend.rb +66 -0
  37. data/lib/concurrent/collection/map/non_concurrent_map_backend.rb +144 -0
  38. data/lib/concurrent/collection/map/synchronized_map_backend.rb +86 -0
  39. data/lib/concurrent/collection/non_concurrent_priority_queue.rb +143 -0
  40. data/lib/concurrent/collection/ruby_non_concurrent_priority_queue.rb +150 -0
  41. data/lib/concurrent/concern/dereferenceable.rb +9 -24
  42. data/lib/concurrent/concern/logging.rb +1 -1
  43. data/lib/concurrent/concern/obligation.rb +11 -20
  44. data/lib/concurrent/concern/observable.rb +38 -13
  45. data/lib/concurrent/configuration.rb +23 -152
  46. data/lib/concurrent/constants.rb +8 -0
  47. data/lib/concurrent/delay.rb +14 -12
  48. data/lib/concurrent/exchanger.rb +339 -41
  49. data/lib/concurrent/executor/abstract_executor_service.rb +134 -0
  50. data/lib/concurrent/executor/executor_service.rb +23 -359
  51. data/lib/concurrent/executor/immediate_executor.rb +3 -2
  52. data/lib/concurrent/executor/java_executor_service.rb +100 -0
  53. data/lib/concurrent/executor/java_single_thread_executor.rb +3 -3
  54. data/lib/concurrent/executor/java_thread_pool_executor.rb +3 -4
  55. data/lib/concurrent/executor/ruby_executor_service.rb +78 -0
  56. data/lib/concurrent/executor/ruby_single_thread_executor.rb +10 -66
  57. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +25 -22
  58. data/lib/concurrent/executor/safe_task_executor.rb +6 -7
  59. data/lib/concurrent/executor/serial_executor_service.rb +34 -0
  60. data/lib/concurrent/executor/serialized_execution.rb +10 -33
  61. data/lib/concurrent/executor/serialized_execution_delegator.rb +28 -0
  62. data/lib/concurrent/executor/simple_executor_service.rb +1 -10
  63. data/lib/concurrent/executor/single_thread_executor.rb +20 -10
  64. data/lib/concurrent/executor/timer_set.rb +8 -10
  65. data/lib/concurrent/executors.rb +12 -2
  66. data/lib/concurrent/future.rb +6 -4
  67. data/lib/concurrent/hash.rb +35 -0
  68. data/lib/concurrent/immutable_struct.rb +5 -1
  69. data/lib/concurrent/ivar.rb +12 -16
  70. data/lib/concurrent/lazy_register.rb +11 -8
  71. data/lib/concurrent/map.rb +180 -0
  72. data/lib/concurrent/maybe.rb +6 -3
  73. data/lib/concurrent/mutable_struct.rb +7 -6
  74. data/lib/concurrent/mvar.rb +26 -2
  75. data/lib/concurrent/{executor/executor.rb → options.rb} +5 -29
  76. data/lib/concurrent/promise.rb +7 -5
  77. data/lib/concurrent/scheduled_task.rb +13 -71
  78. data/lib/concurrent/settable_struct.rb +5 -4
  79. data/lib/concurrent/synchronization.rb +15 -3
  80. data/lib/concurrent/synchronization/abstract_lockable_object.rb +98 -0
  81. data/lib/concurrent/synchronization/abstract_object.rb +7 -146
  82. data/lib/concurrent/synchronization/abstract_struct.rb +2 -3
  83. data/lib/concurrent/synchronization/condition.rb +6 -4
  84. data/lib/concurrent/synchronization/jruby_lockable_object.rb +13 -0
  85. data/lib/concurrent/synchronization/jruby_object.rb +44 -0
  86. data/lib/concurrent/synchronization/lock.rb +3 -2
  87. data/lib/concurrent/synchronization/lockable_object.rb +72 -0
  88. data/lib/concurrent/synchronization/mri_lockable_object.rb +71 -0
  89. data/lib/concurrent/synchronization/mri_object.rb +43 -0
  90. data/lib/concurrent/synchronization/object.rb +140 -73
  91. data/lib/concurrent/synchronization/rbx_lockable_object.rb +65 -0
  92. data/lib/concurrent/synchronization/rbx_object.rb +30 -73
  93. data/lib/concurrent/synchronization/volatile.rb +34 -0
  94. data/lib/concurrent/thread_safe/synchronized_delegator.rb +50 -0
  95. data/lib/concurrent/thread_safe/util.rb +14 -0
  96. data/lib/concurrent/thread_safe/util/adder.rb +74 -0
  97. data/lib/concurrent/thread_safe/util/array_hash_rbx.rb +30 -0
  98. data/lib/concurrent/thread_safe/util/cheap_lockable.rb +118 -0
  99. data/lib/concurrent/thread_safe/util/power_of_two_tuple.rb +38 -0
  100. data/lib/concurrent/thread_safe/util/striped64.rb +241 -0
  101. data/lib/concurrent/thread_safe/util/volatile.rb +75 -0
  102. data/lib/concurrent/thread_safe/util/xor_shift_random.rb +50 -0
  103. data/lib/concurrent/timer_task.rb +3 -4
  104. data/lib/concurrent/tuple.rb +86 -0
  105. data/lib/concurrent/tvar.rb +5 -1
  106. data/lib/concurrent/utility/at_exit.rb +1 -1
  107. data/lib/concurrent/utility/engine.rb +4 -0
  108. data/lib/concurrent/utility/monotonic_time.rb +3 -4
  109. data/lib/concurrent/utility/native_extension_loader.rb +50 -30
  110. data/lib/concurrent/version.rb +2 -2
  111. data/lib/concurrent_ruby_ext.jar +0 -0
  112. metadata +47 -12
  113. data/lib/concurrent/atomic/condition.rb +0 -78
  114. data/lib/concurrent/collection/priority_queue.rb +0 -360
  115. data/lib/concurrent/synchronization/java_object.rb +0 -34
  116. data/lib/concurrent/synchronization/monitor_object.rb +0 -27
  117. data/lib/concurrent/synchronization/mutex_object.rb +0 -43
  118. data/lib/concurrent/utilities.rb +0 -5
  119. data/lib/concurrent/utility/timeout.rb +0 -39
  120. data/lib/concurrent/utility/timer.rb +0 -26
  121. data/lib/concurrent_ruby.rb +0 -2
@@ -0,0 +1,65 @@
1
+ module Concurrent
2
+ module Synchronization
3
+
4
+ # @!visibility private
5
+ # @!macro internal_implementation_note
6
+ class RbxLockableObject < AbstractLockableObject
7
+ safe_initialization!
8
+
9
+ def initialize(*defaults)
10
+ super(*defaults)
11
+ @__Waiters__ = []
12
+ @__owner__ = nil
13
+ end
14
+
15
+ protected
16
+
17
+ def synchronize(&block)
18
+ if @__owner__ == Thread.current
19
+ yield
20
+ else
21
+ result = nil
22
+ Rubinius.synchronize(self) do
23
+ begin
24
+ @__owner__ = Thread.current
25
+ result = yield
26
+ ensure
27
+ @__owner__ = nil
28
+ end
29
+ end
30
+ result
31
+ end
32
+ end
33
+
34
+ def ns_wait(timeout = nil)
35
+ wchan = Rubinius::Channel.new
36
+
37
+ begin
38
+ @__Waiters__.push wchan
39
+ Rubinius.unlock(self)
40
+ signaled = wchan.receive_timeout timeout
41
+ ensure
42
+ Rubinius.lock(self)
43
+
44
+ if !signaled && !@__Waiters__.delete(wchan)
45
+ # we timed out, but got signaled afterwards,
46
+ # so pass that signal on to the next waiter
47
+ @__Waiters__.shift << true unless @__Waiters__.empty?
48
+ end
49
+ end
50
+
51
+ self
52
+ end
53
+
54
+ def ns_signal
55
+ @__Waiters__.shift << true unless @__Waiters__.empty?
56
+ self
57
+ end
58
+
59
+ def ns_broadcast
60
+ @__Waiters__.shift << true until @__Waiters__.empty?
61
+ self
62
+ end
63
+ end
64
+ end
65
+ end
@@ -1,88 +1,45 @@
1
1
  module Concurrent
2
2
  module Synchronization
3
3
 
4
- if Concurrent.on_rbx?
5
-
6
- # @!visibility private
7
- # @!macro internal_implementation_note
8
- class RbxObject < AbstractObject
9
- def initialize
10
- @__Waiters__ = []
11
- @__owner__ = nil
12
- ensure_ivar_visibility!
13
- end
14
-
15
- protected
16
-
17
- def synchronize(&block)
18
- if @__owner__ == Thread.current
19
- yield
20
- else
21
- Rubinius.lock(self)
22
- begin
23
- @__owner__ = Thread.current
24
- result = yield
25
- ensure
26
- @__owner__ = nil
27
- Rubinius.unlock(self)
28
- result
29
- end
30
- end
31
- end
32
-
33
- def ns_wait(timeout = nil)
34
- wchan = Rubinius::Channel.new
35
-
36
- begin
37
- @__Waiters__.push wchan
38
- Rubinius.unlock(self)
39
- signaled = wchan.receive_timeout timeout
40
- ensure
41
- Rubinius.lock(self)
42
-
43
- if !signaled && !@__Waiters__.delete(wchan)
44
- # we timed out, but got signaled afterwards,
45
- # so pass that signal on to the next waiter
46
- @__Waiters__.shift << true unless @__Waiters__.empty?
47
- end
48
- end
49
-
50
- self
51
- end
52
-
53
- def ns_signal
54
- @__Waiters__.shift << true unless @__Waiters__.empty?
55
- self
56
- end
57
-
58
- def ns_broadcast
59
- @__Waiters__.shift << true until @__Waiters__.empty?
60
- self
61
- end
62
-
63
- def ensure_ivar_visibility!
64
- # Rubinius instance variables are not volatile so we need to insert barrier
65
- Rubinius.memory_barrier
66
- end
4
+ module RbxAttrVolatile
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ end
67
8
 
68
- def self.attr_volatile *names
9
+ module ClassMethods
10
+ def attr_volatile(*names)
69
11
  names.each do |name|
70
12
  ivar = :"@volatile_#{name}"
71
13
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
72
- def #{name}
73
- Rubinius.memory_barrier
74
- #{ivar}
75
- end
76
-
77
- def #{name}=(value)
78
- #{ivar} = value
79
- Rubinius.memory_barrier
80
- end
14
+ def #{name}
15
+ Rubinius.memory_barrier
16
+ #{ivar}
17
+ end
18
+
19
+ def #{name}=(value)
20
+ #{ivar} = value
21
+ Rubinius.memory_barrier
22
+ end
81
23
  RUBY
82
24
  end
83
25
  names.map { |n| [n, :"#{n}="] }.flatten
84
26
  end
85
27
  end
28
+
29
+ def full_memory_barrier
30
+ # Rubinius instance variables are not volatile so we need to insert barrier
31
+ Rubinius.memory_barrier
32
+ end
33
+ end
34
+
35
+ # @!visibility private
36
+ # @!macro internal_implementation_note
37
+ class RbxObject < AbstractObject
38
+ include RbxAttrVolatile
39
+
40
+ def initialize
41
+ # nothing to do
42
+ end
86
43
  end
87
44
  end
88
45
  end
@@ -0,0 +1,34 @@
1
+ module Concurrent
2
+ module Synchronization
3
+
4
+ # Volatile adds the attr_volatile class method when included.
5
+ #
6
+ # @example
7
+ # class Foo
8
+ # include Concurrent::Synchronization::Volatile
9
+ #
10
+ # attr_volatile :bar
11
+ #
12
+ # def initialize
13
+ # self.bar = 1
14
+ # end
15
+ # end
16
+ #
17
+ # foo = Foo.new
18
+ # foo.bar
19
+ # => 1
20
+ # foo.bar = 2
21
+ # => 2
22
+
23
+ Volatile = case
24
+ when Concurrent.on_cruby?
25
+ MriAttrVolatile
26
+ when Concurrent.on_jruby?
27
+ JRubyAttrVolatile
28
+ when Concurrent.on_rbx? || Concurrent.on_truffle?
29
+ RbxAttrVolatile
30
+ else
31
+ MriAttrVolatile
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,50 @@
1
+ require 'delegate'
2
+ require 'monitor'
3
+
4
+ module Concurrent
5
+ unless defined?(SynchronizedDelegator)
6
+
7
+ # This class provides a trivial way to synchronize all calls to a given object
8
+ # by wrapping it with a `Delegator` that performs `Monitor#enter/exit` calls
9
+ # around the delegated `#send`. Example:
10
+ #
11
+ # array = [] # not thread-safe on many impls
12
+ # array = SynchronizedDelegator.new([]) # thread-safe
13
+ #
14
+ # A simple `Monitor` provides a very coarse-grained way to synchronize a given
15
+ # object, in that it will cause synchronization for methods that have no need
16
+ # for it, but this is a trivial way to get thread-safety where none may exist
17
+ # currently on some implementations.
18
+ #
19
+ # This class is currently being considered for inclusion into stdlib, via
20
+ # https://bugs.ruby-lang.org/issues/8556
21
+ #
22
+ # @!visibility private
23
+ class SynchronizedDelegator < SimpleDelegator
24
+ def setup
25
+ @old_abort = Thread.abort_on_exception
26
+ Thread.abort_on_exception = true
27
+ end
28
+
29
+ def teardown
30
+ Thread.abort_on_exception = @old_abort
31
+ end
32
+
33
+ def initialize(obj)
34
+ __setobj__(obj)
35
+ @monitor = Monitor.new
36
+ end
37
+
38
+ def method_missing(method, *args, &block)
39
+ monitor = @monitor
40
+ begin
41
+ monitor.enter
42
+ super
43
+ ensure
44
+ monitor.exit
45
+ end
46
+ end
47
+
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,14 @@
1
+ module Concurrent
2
+
3
+ # @!visibility private
4
+ module ThreadSafe
5
+
6
+ # @!visibility private
7
+ module Util
8
+
9
+ FIXNUM_BIT_SIZE = (0.size * 8) - 2
10
+ MAX_INT = (2 ** FIXNUM_BIT_SIZE) - 1
11
+ CPU_COUNT = 16 # is there a way to determine this?
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,74 @@
1
+ require 'concurrent/thread_safe/util'
2
+ require 'concurrent/thread_safe/util/striped64'
3
+
4
+ module Concurrent
5
+
6
+ # @!visibility private
7
+ module ThreadSafe
8
+
9
+ # @!visibility private
10
+ module Util
11
+
12
+ # A Ruby port of the Doug Lea's jsr166e.LondAdder class version 1.8
13
+ # available in public domain.
14
+ #
15
+ # Original source code available here:
16
+ # http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/LongAdder.java?revision=1.8
17
+ #
18
+ # One or more variables that together maintain an initially zero
19
+ # sum. When updates (method +add+) are contended across threads,
20
+ # the set of variables may grow dynamically to reduce contention.
21
+ # Method +sum+ returns the current total combined across the
22
+ # variables maintaining the sum.
23
+ #
24
+ # This class is usually preferable to single +Atomic+ reference when
25
+ # multiple threads update a common sum that is used for purposes such
26
+ # as collecting statistics, not for fine-grained synchronization
27
+ # control. Under low update contention, the two classes have similar
28
+ # characteristics. But under high contention, expected throughput of
29
+ # this class is significantly higher, at the expense of higher space
30
+ # consumption.
31
+ #
32
+ # @!visibility private
33
+ class Adder < Striped64
34
+ # Adds the given value.
35
+ def add(x)
36
+ if (current_cells = cells) || !cas_base_computed {|current_base| current_base + x}
37
+ was_uncontended = true
38
+ hash = hash_code
39
+ unless current_cells && (cell = current_cells.volatile_get_by_hash(hash)) && (was_uncontended = cell.cas_computed {|current_value| current_value + x})
40
+ retry_update(x, hash, was_uncontended) {|current_value| current_value + x}
41
+ end
42
+ end
43
+ end
44
+
45
+ def increment
46
+ add(1)
47
+ end
48
+
49
+ def decrement
50
+ add(-1)
51
+ end
52
+
53
+ # Returns the current sum. The returned value is _NOT_ an
54
+ # atomic snapshot: Invocation in the absence of concurrent
55
+ # updates returns an accurate result, but concurrent updates that
56
+ # occur while the sum is being calculated might not be
57
+ # incorporated.
58
+ def sum
59
+ x = base
60
+ if current_cells = cells
61
+ current_cells.each do |cell|
62
+ x += cell.value if cell
63
+ end
64
+ end
65
+ x
66
+ end
67
+
68
+ def reset
69
+ internal_reset(0)
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,30 @@
1
+ require 'concurrent/thread_safe/util'
2
+
3
+ module Concurrent
4
+ module ThreadSafe
5
+ module Util
6
+ def self.make_synchronized_on_rbx(klass)
7
+ klass.class_eval do
8
+ private
9
+ def _mon_initialize
10
+ @_monitor = Monitor.new unless @_monitor # avoid double initialisation
11
+ end
12
+
13
+ def self.allocate
14
+ obj = super
15
+ obj.send(:_mon_initialize)
16
+ obj
17
+ end
18
+ end
19
+
20
+ klass.superclass.instance_methods(false).each do |method|
21
+ klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
22
+ def #{method}(*args)
23
+ @_monitor.synchronize { super }
24
+ end
25
+ RUBY
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,118 @@
1
+ require 'concurrent/thread_safe/util'
2
+ require 'concurrent/thread_safe/util/volatile'
3
+
4
+ module Concurrent
5
+
6
+ # @!visibility private
7
+ module ThreadSafe
8
+
9
+ # @!visibility private
10
+ module Util
11
+
12
+ # Provides a cheapest possible (mainly in terms of memory usage) +Mutex+
13
+ # with the +ConditionVariable+ bundled in.
14
+ #
15
+ # Usage:
16
+ # class A
17
+ # include CheapLockable
18
+ #
19
+ # def do_exlusively
20
+ # cheap_synchronize { yield }
21
+ # end
22
+ #
23
+ # def wait_for_something
24
+ # cheap_synchronize do
25
+ # cheap_wait until resource_available?
26
+ # do_something
27
+ # cheap_broadcast # wake up others
28
+ # end
29
+ # end
30
+ # end
31
+ #
32
+ # @!visibility private
33
+ module CheapLockable
34
+ private
35
+ engine = defined?(RUBY_ENGINE) && RUBY_ENGINE
36
+ if engine == 'rbx'
37
+ # Making use of the Rubinius' ability to lock via object headers to avoid the overhead of the extra Mutex objects.
38
+ def cheap_synchronize
39
+ Rubinius.lock(self)
40
+ begin
41
+ yield
42
+ ensure
43
+ Rubinius.unlock(self)
44
+ end
45
+ end
46
+
47
+ def cheap_wait
48
+ wchan = Rubinius::Channel.new
49
+
50
+ begin
51
+ waiters = @waiters ||= []
52
+ waiters.push wchan
53
+ Rubinius.unlock(self)
54
+ signaled = wchan.receive_timeout nil
55
+ ensure
56
+ Rubinius.lock(self)
57
+
58
+ unless signaled or waiters.delete(wchan)
59
+ # we timed out, but got signaled afterwards (e.g. while waiting to
60
+ # acquire @lock), so pass that signal on to the next waiter
61
+ waiters.shift << true unless waiters.empty?
62
+ end
63
+ end
64
+
65
+ self
66
+ end
67
+
68
+ def cheap_broadcast
69
+ waiters = @waiters ||= []
70
+ waiters.shift << true until waiters.empty?
71
+ self
72
+ end
73
+ elsif engine == 'jruby'
74
+ # Use Java's native synchronized (this) { wait(); notifyAll(); } to avoid the overhead of the extra Mutex objects
75
+ require 'jruby'
76
+
77
+ def cheap_synchronize
78
+ JRuby.reference0(self).synchronized { yield }
79
+ end
80
+
81
+ def cheap_wait
82
+ JRuby.reference0(self).wait
83
+ end
84
+
85
+ def cheap_broadcast
86
+ JRuby.reference0(self).notify_all
87
+ end
88
+ else
89
+ require 'thread'
90
+
91
+ extend Volatile
92
+ attr_volatile :mutex
93
+
94
+ # Non-reentrant Mutex#syncrhonize
95
+ def cheap_synchronize
96
+ true until (my_mutex = mutex) || cas_mutex(nil, my_mutex = Mutex.new)
97
+ my_mutex.synchronize { yield }
98
+ end
99
+
100
+ # Releases this object's +cheap_synchronize+ lock and goes to sleep waiting for other threads to +cheap_broadcast+, reacquires the lock on wakeup.
101
+ # Must only be called in +cheap_broadcast+'s block.
102
+ def cheap_wait
103
+ conditional_variable = @conditional_variable ||= ConditionVariable.new
104
+ conditional_variable.wait(mutex)
105
+ end
106
+
107
+ # Wakes up all threads waiting for this object's +cheap_synchronize+ lock.
108
+ # Must only be called in +cheap_broadcast+'s block.
109
+ def cheap_broadcast
110
+ if conditional_variable = @conditional_variable
111
+ conditional_variable.broadcast
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end