concurrent-ruby 1.0.0.pre1-java → 1.0.0.pre2-java

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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -1
  3. data/README.md +16 -18
  4. data/lib/concurrent.rb +3 -3
  5. data/lib/concurrent/agent.rb +583 -0
  6. data/lib/concurrent/array.rb +1 -0
  7. data/lib/concurrent/async.rb +236 -111
  8. data/lib/concurrent/atom.rb +101 -46
  9. data/lib/concurrent/atomic/atomic_boolean.rb +2 -0
  10. data/lib/concurrent/atomic/atomic_fixnum.rb +2 -0
  11. data/lib/concurrent/atomic/cyclic_barrier.rb +1 -1
  12. data/lib/concurrent/atomic/event.rb +1 -1
  13. data/lib/concurrent/atomic/mutex_atomic_boolean.rb +1 -1
  14. data/lib/concurrent/atomic/mutex_atomic_fixnum.rb +1 -1
  15. data/lib/concurrent/atomic/mutex_count_down_latch.rb +1 -1
  16. data/lib/concurrent/atomic/mutex_semaphore.rb +2 -2
  17. data/lib/concurrent/atomic/read_write_lock.rb +5 -4
  18. data/lib/concurrent/atomic/reentrant_read_write_lock.rb +3 -1
  19. data/lib/concurrent/atomic/thread_local_var.rb +2 -0
  20. data/lib/concurrent/atomic_reference/mutex_atomic.rb +1 -1
  21. data/lib/concurrent/atomics.rb +6 -4
  22. data/lib/concurrent/collection/copy_on_notify_observer_set.rb +7 -15
  23. data/lib/concurrent/collection/copy_on_write_observer_set.rb +7 -15
  24. data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +5 -0
  25. data/lib/concurrent/concern/observable.rb +38 -13
  26. data/lib/concurrent/configuration.rb +5 -4
  27. data/lib/concurrent/delay.rb +9 -8
  28. data/lib/concurrent/exchanger.rb +2 -0
  29. data/lib/concurrent/executor/abstract_executor_service.rb +2 -2
  30. data/lib/concurrent/executor/java_single_thread_executor.rb +0 -1
  31. data/lib/concurrent/executor/ruby_executor_service.rb +10 -4
  32. data/lib/concurrent/executor/ruby_single_thread_executor.rb +10 -68
  33. data/lib/concurrent/executor/safe_task_executor.rb +7 -8
  34. data/lib/concurrent/executor/serialized_execution.rb +4 -4
  35. data/lib/concurrent/executor/single_thread_executor.rb +20 -10
  36. data/lib/concurrent/executor/timer_set.rb +4 -2
  37. data/lib/concurrent/executors.rb +0 -1
  38. data/lib/concurrent/future.rb +3 -2
  39. data/lib/concurrent/hash.rb +1 -1
  40. data/lib/concurrent/immutable_struct.rb +5 -1
  41. data/lib/concurrent/ivar.rb +1 -1
  42. data/lib/concurrent/mutable_struct.rb +7 -6
  43. data/lib/concurrent/{executor/executor.rb → options.rb} +4 -3
  44. data/lib/concurrent/promise.rb +3 -2
  45. data/lib/concurrent/scheduled_task.rb +3 -2
  46. data/lib/concurrent/settable_struct.rb +5 -4
  47. data/lib/concurrent/synchronization.rb +11 -3
  48. data/lib/concurrent/synchronization/abstract_lockable_object.rb +117 -0
  49. data/lib/concurrent/synchronization/abstract_object.rb +16 -129
  50. data/lib/concurrent/synchronization/abstract_struct.rb +2 -3
  51. data/lib/concurrent/synchronization/condition.rb +6 -4
  52. data/lib/concurrent/synchronization/jruby_lockable_object.rb +13 -0
  53. data/lib/concurrent/synchronization/{java_object.rb → jruby_object.rb} +5 -3
  54. data/lib/concurrent/synchronization/lock.rb +3 -2
  55. data/lib/concurrent/synchronization/lockable_object.rb +59 -0
  56. data/lib/concurrent/synchronization/mri_lockable_object.rb +71 -0
  57. data/lib/concurrent/synchronization/mri_object.rb +35 -0
  58. data/lib/concurrent/synchronization/object.rb +111 -39
  59. data/lib/concurrent/synchronization/rbx_lockable_object.rb +64 -0
  60. data/lib/concurrent/synchronization/rbx_object.rb +17 -68
  61. data/lib/concurrent/thread_safe/util.rb +0 -9
  62. data/lib/concurrent/thread_safe/util/adder.rb +3 -0
  63. data/lib/concurrent/thread_safe/util/array_hash_rbx.rb +3 -1
  64. data/lib/concurrent/thread_safe/util/cheap_lockable.rb +3 -0
  65. data/lib/concurrent/thread_safe/util/power_of_two_tuple.rb +1 -0
  66. data/lib/concurrent/thread_safe/util/striped64.rb +6 -1
  67. data/lib/concurrent/thread_safe/util/volatile.rb +2 -0
  68. data/lib/concurrent/thread_safe/util/xor_shift_random.rb +2 -0
  69. data/lib/concurrent/tvar.rb +36 -0
  70. data/lib/concurrent/utility/at_exit.rb +1 -1
  71. data/lib/concurrent/utility/monotonic_time.rb +3 -4
  72. data/lib/concurrent/utility/native_extension_loader.rb +1 -1
  73. data/lib/concurrent/version.rb +2 -2
  74. data/lib/concurrent_ruby_ext.jar +0 -0
  75. metadata +11 -6
  76. data/lib/concurrent/synchronization/monitor_object.rb +0 -27
  77. data/lib/concurrent/synchronization/mutex_object.rb +0 -43
@@ -6,23 +6,17 @@ module Concurrent
6
6
  # A thread safe observer set implemented using copy-on-write approach:
7
7
  # every time an observer is added or removed the whole internal data structure is
8
8
  # duplicated and replaced with a new one.
9
- #
9
+ #
10
10
  # @api private
11
- class CopyOnWriteObserverSet < Synchronization::Object
11
+ class CopyOnWriteObserverSet < Synchronization::LockableObject
12
12
 
13
13
  def initialize
14
14
  super()
15
15
  synchronize { ns_initialize }
16
16
  end
17
17
 
18
- # Adds an observer to this set
19
- # If a block is passed, the observer will be created by this method and no
20
- # other params should be passed
21
- # @param [Object] observer the observer to add
22
- # @param [Symbol] func the function to call on the observer during notification.
23
- # Default is :update
24
- # @return [Object] the added observer
25
- def add_observer(observer=nil, func=:update, &block)
18
+ # @!macro observable_add_observer
19
+ def add_observer(observer = nil, func = :update, &block)
26
20
  if observer.nil? && block.nil?
27
21
  raise ArgumentError, 'should pass observer as a first argument or block'
28
22
  elsif observer && block
@@ -42,8 +36,7 @@ module Concurrent
42
36
  end
43
37
  end
44
38
 
45
- # @param [Object] observer the observer to remove
46
- # @return [Object] the deleted observer
39
+ # @!macro observable_delete_observer
47
40
  def delete_observer(observer)
48
41
  synchronize do
49
42
  new_observers = @observers.dup
@@ -53,14 +46,13 @@ module Concurrent
53
46
  end
54
47
  end
55
48
 
56
- # Deletes all observers
57
- # @return [CopyOnWriteObserverSet] self
49
+ # @!macro observable_delete_observers
58
50
  def delete_observers
59
51
  self.observers = {}
60
52
  self
61
53
  end
62
54
 
63
- # @return [Integer] the observers count
55
+ # @!macro observable_count_observers
64
56
  def count_observers
65
57
  observers.count
66
58
  end
@@ -1,4 +1,9 @@
1
1
  require 'concurrent/thread_safe/util'
2
+ require 'concurrent/thread_safe/util/adder'
3
+ require 'concurrent/thread_safe/util/cheap_lockable'
4
+ require 'concurrent/thread_safe/util/power_of_two_tuple'
5
+ require 'concurrent/thread_safe/util/volatile'
6
+ require 'concurrent/thread_safe/util/xor_shift_random'
2
7
 
3
8
  module Concurrent
4
9
 
@@ -21,8 +21,8 @@ module Concurrent
21
21
  #
22
22
  # In a multi threaded environment things are more complex. The `subject` must
23
23
  # synchronize the access to its data structure and to do so currently we're
24
- # using two specialized ObserverSet: CopyOnWriteObserverSet and
25
- # CopyOnNotifyObserverSet.
24
+ # using two specialized ObserverSet: {Concurrent::Concern::CopyOnWriteObserverSet}
25
+ # and {Concurrent::Concern::CopyOnNotifyObserverSet}.
26
26
  #
27
27
  # When implementing and `observer` there's a very important rule to remember:
28
28
  # **there are no guarantees about the thread that will execute the callback**
@@ -49,30 +49,55 @@ module Concurrent
49
49
  # or an AtomicFixum)
50
50
  module Observable
51
51
 
52
- # @return [Object] the added observer
53
- def add_observer(*args, &block)
54
- observers.add_observer(*args, &block)
52
+ # @!macro [attach] observable_add_observer
53
+ #
54
+ # Adds an observer to this set. If a block is passed, the observer will be
55
+ # created by this method and no other params should be passed.
56
+ #
57
+ # @param [Object] observer the observer to add
58
+ # @param [Symbol] func the function to call on the observer during notification.
59
+ # Default is :update
60
+ # @return [Object] the added observer
61
+ def add_observer(observer = nil, func = :update, &block)
62
+ observers.add_observer(observer, func, &block)
55
63
  end
56
64
 
57
- # as #add_observer but it can be used for chaining
65
+ # As `#add_observer` but can be used for chaining.
66
+ #
67
+ # @param [Object] observer the observer to add
68
+ # @param [Symbol] func the function to call on the observer during notification.
58
69
  # @return [Observable] self
59
- def with_observer(*args, &block)
60
- add_observer(*args, &block)
70
+ def with_observer(observer = nil, func = :update, &block)
71
+ add_observer(observer, func, &block)
61
72
  self
62
73
  end
63
74
 
64
- # @return [Object] the deleted observer
65
- def delete_observer(*args)
66
- observers.delete_observer(*args)
75
+ # @!macro [attach] observable_delete_observer
76
+ #
77
+ # Remove `observer` as an observer on this object so that it will no
78
+ # longer receive notifications.
79
+ #
80
+ # @param [Object] observer the observer to remove
81
+ # @return [Object] the deleted observer
82
+ def delete_observer(observer)
83
+ observers.delete_observer(observer)
67
84
  end
68
85
 
69
- # @return [Observable] self
86
+ # @!macro [attach] observable_delete_observers
87
+ #
88
+ # Remove all observers associated with this object.
89
+ #
90
+ # @return [Observable] self
70
91
  def delete_observers
71
92
  observers.delete_observers
72
93
  self
73
94
  end
74
95
 
75
- # @return [Integer] the observers count
96
+ # @!macro [attach] observable_count_observers
97
+ #
98
+ # Return the number of observers associated with this object.
99
+ #
100
+ # @return [Integer] the observers count
76
101
  def count_observers
77
102
  observers.count_observers
78
103
  end
@@ -3,16 +3,17 @@ require 'concurrent/delay'
3
3
  require 'concurrent/errors'
4
4
  require 'concurrent/atomic/atomic_reference'
5
5
  require 'concurrent/concern/logging'
6
- require 'concurrent/executor/timer_set'
7
6
  require 'concurrent/executor/immediate_executor'
8
- require 'concurrent/executor/fixed_thread_pool'
9
- require 'concurrent/executor/thread_pool_executor'
10
7
  require 'concurrent/utility/at_exit'
11
8
  require 'concurrent/utility/processor_counter'
12
9
 
13
10
  module Concurrent
14
11
  extend Concern::Logging
15
12
 
13
+ autoload :Options, 'concurrent/options'
14
+ autoload :TimerSet, 'concurrent/executor/timer_set'
15
+ autoload :ThreadPoolExecutor, 'concurrent/executor/thread_pool_executor'
16
+
16
17
  # @return [Logger] Logger with provided level and output.
17
18
  def self.create_stdlib_logger(level = Logger::FATAL, output = $stderr)
18
19
  logger = Logger.new(output)
@@ -125,7 +126,7 @@ module Concurrent
125
126
  # - :immediate - {Concurrent.global_immediate_executor}
126
127
  # @return [Executor]
127
128
  def self.executor(executor_identifier)
128
- Executor.executor(executor_identifier)
129
+ Options.executor(executor_identifier)
129
130
  end
130
131
 
131
132
  def self.new_fast_executor(opts = {})
@@ -1,12 +1,12 @@
1
1
  require 'thread'
2
- require 'concurrent/configuration'
3
2
  require 'concurrent/concern/obligation'
4
- require 'concurrent/executor/executor'
5
3
  require 'concurrent/executor/immediate_executor'
6
4
  require 'concurrent/synchronization'
7
5
 
8
6
  module Concurrent
9
7
 
8
+ autoload :Options, 'concurrent/options'
9
+
10
10
  # Lazy evaluation of a block yielding an immutable result. Useful for
11
11
  # expensive operations that may never be needed. It may be non-blocking,
12
12
  # supports the `Concern::Obligation` interface, and accepts the injection of
@@ -40,7 +40,7 @@ module Concurrent
40
40
  # execute on the given executor, allowing the call to timeout.
41
41
  #
42
42
  # @see Concurrent::Concern::Dereferenceable
43
- class Delay < Synchronization::Object
43
+ class Delay < Synchronization::LockableObject
44
44
  include Concern::Obligation
45
45
 
46
46
  # NOTE: Because the global thread pools are lazy-loaded with these objects
@@ -74,7 +74,7 @@ module Concurrent
74
74
  #
75
75
  # @!macro delay_note_regarding_blocking
76
76
  def value(timeout = nil)
77
- if @task_executor
77
+ if @executor # TODO (pitr 12-Sep-2015): broken unsafe read?
78
78
  super
79
79
  else
80
80
  # this function has been optimized for performance and
@@ -108,7 +108,7 @@ module Concurrent
108
108
  #
109
109
  # @!macro delay_note_regarding_blocking
110
110
  def value!(timeout = nil)
111
- if @task_executor
111
+ if @executor
112
112
  super
113
113
  else
114
114
  result = value
@@ -127,7 +127,7 @@ module Concurrent
127
127
  #
128
128
  # @!macro delay_note_regarding_blocking
129
129
  def wait(timeout = nil)
130
- if @task_executor
130
+ if @executor
131
131
  execute_task_once
132
132
  super(timeout)
133
133
  else
@@ -157,7 +157,7 @@ module Concurrent
157
157
  def ns_initialize(opts, &block)
158
158
  init_obligation(self)
159
159
  set_deref_options(opts)
160
- @task_executor = Executor.executor_from_options(opts)
160
+ @executor = opts[:executor]
161
161
 
162
162
  @task = block
163
163
  @state = :pending
@@ -177,7 +177,8 @@ module Concurrent
177
177
  end
178
178
 
179
179
  if execute
180
- @task_executor.post do
180
+ executor = Options.executor_from_options(executor: @executor)
181
+ executor.post do
181
182
  begin
182
183
  result = task.call
183
184
  success = true
@@ -13,6 +13,8 @@ module Concurrent
13
13
  # pairs. Each thread presents some object on entry to the exchange method,
14
14
  # matches with a partner thread, and receives its partner's object on return.
15
15
  #
16
+ # @!macro thread_safe_variable_comparison
17
+ #
16
18
  # This implementation is very simple, using only a single slot for each
17
19
  # exchanger (unlike more advanced implementations which use an "arena").
18
20
  # This approach will work perfectly fine when there are only a few threads
@@ -1,13 +1,13 @@
1
1
  require 'concurrent/errors'
2
2
  require 'concurrent/executor/executor_service'
3
- require 'concurrent/synchronization/object'
3
+ require 'concurrent/synchronization'
4
4
  require 'concurrent/utility/at_exit'
5
5
 
6
6
  module Concurrent
7
7
 
8
8
  # @!macro abstract_executor_service_public_api
9
9
  # @!visibility private
10
- class AbstractExecutorService < Synchronization::Object
10
+ class AbstractExecutorService < Synchronization::LockableObject
11
11
  include ExecutorService
12
12
 
13
13
  # The set of possible fallback policies that may be set at thread pool creation.
@@ -6,7 +6,6 @@ if Concurrent.on_jruby?
6
6
  module Concurrent
7
7
 
8
8
  # @!macro single_thread_executor
9
- # @!macro thread_pool_options
10
9
  # @!macro abstract_executor_service_public_api
11
10
  # @!visibility private
12
11
  class JavaSingleThreadExecutor < JavaExecutorService
@@ -6,12 +6,12 @@ module Concurrent
6
6
  # @!macro abstract_executor_service_public_api
7
7
  # @!visibility private
8
8
  class RubyExecutorService < AbstractExecutorService
9
+ safe_initialization!
9
10
 
10
11
  def initialize(*args, &block)
11
12
  super
12
- @stop_event = Event.new
13
- @stopped_event = Event.new
14
- ensure_ivar_visibility!
13
+ @StopEvent = Event.new
14
+ @StoppedEvent = Event.new
15
15
  end
16
16
 
17
17
  def post(*args, &task)
@@ -51,7 +51,13 @@ module Concurrent
51
51
 
52
52
  private
53
53
 
54
- attr_reader :stop_event, :stopped_event
54
+ def stop_event
55
+ @StopEvent
56
+ end
57
+
58
+ def stopped_event
59
+ @StoppedEvent
60
+ end
55
61
 
56
62
  def ns_shutdown_execution
57
63
  stopped_event.set
@@ -1,80 +1,22 @@
1
- require 'thread'
2
- require 'concurrent/executor/ruby_executor_service'
3
- require 'concurrent/executor/serial_executor_service'
1
+ require 'concurrent/executor/ruby_thread_pool_executor'
4
2
 
5
3
  module Concurrent
6
4
 
7
5
  # @!macro single_thread_executor
8
- # @!macro thread_pool_options
9
6
  # @!macro abstract_executor_service_public_api
10
7
  # @!visibility private
11
- class RubySingleThreadExecutor < RubyExecutorService
12
- include SerialExecutorService
8
+ class RubySingleThreadExecutor < RubyThreadPoolExecutor
13
9
 
14
10
  # @!macro single_thread_executor_method_initialize
15
11
  def initialize(opts = {})
16
- super
17
- end
18
-
19
- private
20
-
21
- def ns_initialize(opts)
22
- @queue = Queue.new
23
- @thread = nil
24
- @fallback_policy = opts.fetch(:fallback_policy, :discard)
25
- raise ArgumentError.new("#{@fallback_policy} is not a valid fallback policy") unless FALLBACK_POLICIES.include?(@fallback_policy)
26
- self.auto_terminate = opts.fetch(:auto_terminate, true)
27
- end
28
-
29
- # @!visibility private
30
- def ns_execute(*args, &task)
31
- supervise
32
- @queue << [args, task]
33
- end
34
-
35
- # @!visibility private
36
- def ns_shutdown_execution
37
- @queue << :stop
38
- stopped_event.set unless alive?
39
- end
40
-
41
- # @!visibility private
42
- def ns_kill_execution
43
- @queue.clear
44
- @thread.kill if alive?
45
- end
46
-
47
- # @!visibility private
48
- def alive?
49
- @thread && @thread.alive?
50
- end
51
-
52
- # @!visibility private
53
- def supervise
54
- @thread = new_worker_thread unless alive?
55
- end
56
-
57
- # @!visibility private
58
- def new_worker_thread
59
- Thread.new do
60
- Thread.current.abort_on_exception = false
61
- work
62
- end
63
- end
64
-
65
- # @!visibility private
66
- def work
67
- loop do
68
- task = @queue.pop
69
- break if task == :stop
70
- begin
71
- task.last.call(*task.first)
72
- rescue => ex
73
- # let it fail
74
- log DEBUG, ex
75
- end
76
- end
77
- stopped_event.set
12
+ super(
13
+ min_threads: 1,
14
+ max_threads: 1,
15
+ max_queue: 0,
16
+ idletime: DEFAULT_THREAD_IDLETIMEOUT,
17
+ fallback_policy: opts.fetch(:fallback_policy, :discard),
18
+ auto_terminate: opts.fetch(:auto_terminate, true)
19
+ )
78
20
  end
79
21
  end
80
22
  end
@@ -1,4 +1,4 @@
1
- require 'concurrent/synchronization/object'
1
+ require 'concurrent/synchronization'
2
2
 
3
3
  module Concurrent
4
4
 
@@ -6,26 +6,25 @@ module Concurrent
6
6
  # success - indicating if the callable has been executed without errors
7
7
  # value - filled by the callable result if it has been executed without errors, nil otherwise
8
8
  # reason - the error risen by the callable if it has been executed with errors, nil otherwise
9
- class SafeTaskExecutor < Synchronization::Object
9
+ class SafeTaskExecutor < Synchronization::LockableObject
10
10
 
11
11
  def initialize(task, opts = {})
12
- super()
13
- @task = task
12
+ @task = task
14
13
  @exception_class = opts.fetch(:rescue_exception, false) ? Exception : StandardError
15
- ensure_ivar_visibility!
14
+ super() # ensures visibility
16
15
  end
17
16
 
18
17
  # @return [Array]
19
18
  def execute(*args)
20
19
  synchronize do
21
20
  success = false
22
- value = reason = nil
21
+ value = reason = nil
23
22
 
24
23
  begin
25
- value = @task.call(*args)
24
+ value = @task.call(*args)
26
25
  success = true
27
26
  rescue @exception_class => ex
28
- reason = ex
27
+ reason = ex
29
28
  success = false
30
29
  end
31
30
 
@@ -1,11 +1,11 @@
1
1
  require 'concurrent/errors'
2
2
  require 'concurrent/concern/logging'
3
- require 'concurrent/synchronization/object'
3
+ require 'concurrent/synchronization'
4
4
 
5
5
  module Concurrent
6
6
 
7
7
  # Ensures passed jobs in a serialized order never running at the same time.
8
- class SerializedExecution < Synchronization::Object
8
+ class SerializedExecution < Synchronization::LockableObject
9
9
  include Concern::Logging
10
10
 
11
11
  def initialize()
@@ -39,8 +39,8 @@ module Concurrent
39
39
  # As {#post} but allows to submit multiple tasks at once, it's guaranteed that they will not
40
40
  # be interleaved by other tasks.
41
41
  #
42
- # @param [Array<Array(Executor, Array<Object>, Proc)>] posts array of triplets where
43
- # first is a {Executor}, second is array of args for task, third is a task (Proc)
42
+ # @param [Array<Array(ExecutorService, Array<Object>, Proc)>] posts array of triplets where
43
+ # first is a {ExecutorService}, second is array of args for task, third is a task (Proc)
44
44
  def posts(posts)
45
45
  # if can_overflow?
46
46
  # raise ArgumentError, 'SerializedExecution does not support thread-pools which can overflow'