concurrent-ruby 0.8.0.pre2-java → 0.9.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +114 -3
  3. data/README.md +111 -55
  4. data/lib/concurrent.rb +90 -14
  5. data/lib/concurrent/async.rb +143 -51
  6. data/lib/concurrent/atom.rb +131 -0
  7. data/lib/concurrent/atomic/atomic_boolean.rb +57 -107
  8. data/lib/concurrent/atomic/atomic_fixnum.rb +73 -101
  9. data/lib/concurrent/atomic/atomic_reference.rb +49 -0
  10. data/lib/concurrent/atomic/condition.rb +23 -12
  11. data/lib/concurrent/atomic/count_down_latch.rb +23 -21
  12. data/lib/concurrent/atomic/cyclic_barrier.rb +47 -47
  13. data/lib/concurrent/atomic/event.rb +33 -42
  14. data/lib/concurrent/atomic/read_write_lock.rb +252 -0
  15. data/lib/concurrent/atomic/semaphore.rb +64 -89
  16. data/lib/concurrent/atomic/thread_local_var.rb +130 -58
  17. data/lib/concurrent/atomic/thread_local_var/weak_key_map.rb +236 -0
  18. data/lib/concurrent/atomic_reference/direct_update.rb +34 -3
  19. data/lib/concurrent/atomic_reference/jruby.rb +6 -3
  20. data/lib/concurrent/atomic_reference/mutex_atomic.rb +17 -39
  21. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +3 -0
  22. data/lib/concurrent/atomic_reference/rbx.rb +4 -1
  23. data/lib/concurrent/atomic_reference/ruby.rb +6 -3
  24. data/lib/concurrent/atomics.rb +74 -4
  25. data/lib/concurrent/collection/copy_on_notify_observer_set.rb +115 -0
  26. data/lib/concurrent/collection/copy_on_write_observer_set.rb +119 -0
  27. data/lib/concurrent/collection/priority_queue.rb +300 -245
  28. data/lib/concurrent/concern/deprecation.rb +34 -0
  29. data/lib/concurrent/concern/dereferenceable.rb +88 -0
  30. data/lib/concurrent/concern/logging.rb +27 -0
  31. data/lib/concurrent/concern/obligation.rb +228 -0
  32. data/lib/concurrent/concern/observable.rb +85 -0
  33. data/lib/concurrent/configuration.rb +234 -109
  34. data/lib/concurrent/dataflow.rb +2 -3
  35. data/lib/concurrent/delay.rb +141 -50
  36. data/lib/concurrent/edge.rb +30 -0
  37. data/lib/concurrent/errors.rb +19 -7
  38. data/lib/concurrent/exchanger.rb +25 -1
  39. data/lib/concurrent/executor/cached_thread_pool.rb +51 -33
  40. data/lib/concurrent/executor/executor.rb +46 -299
  41. data/lib/concurrent/executor/executor_service.rb +521 -0
  42. data/lib/concurrent/executor/fixed_thread_pool.rb +196 -23
  43. data/lib/concurrent/executor/immediate_executor.rb +9 -9
  44. data/lib/concurrent/executor/indirect_immediate_executor.rb +4 -3
  45. data/lib/concurrent/executor/java_single_thread_executor.rb +17 -16
  46. data/lib/concurrent/executor/java_thread_pool_executor.rb +55 -102
  47. data/lib/concurrent/executor/ruby_single_thread_executor.rb +14 -16
  48. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +250 -166
  49. data/lib/concurrent/executor/safe_task_executor.rb +5 -4
  50. data/lib/concurrent/executor/serialized_execution.rb +22 -18
  51. data/lib/concurrent/executor/{per_thread_executor.rb → simple_executor_service.rb} +29 -20
  52. data/lib/concurrent/executor/single_thread_executor.rb +32 -21
  53. data/lib/concurrent/executor/thread_pool_executor.rb +73 -60
  54. data/lib/concurrent/executor/timer_set.rb +96 -84
  55. data/lib/concurrent/executors.rb +1 -1
  56. data/lib/concurrent/future.rb +71 -38
  57. data/lib/concurrent/immutable_struct.rb +89 -0
  58. data/lib/concurrent/ivar.rb +152 -60
  59. data/lib/concurrent/lazy_register.rb +40 -20
  60. data/lib/concurrent/maybe.rb +226 -0
  61. data/lib/concurrent/mutable_struct.rb +227 -0
  62. data/lib/concurrent/mvar.rb +44 -43
  63. data/lib/concurrent/promise.rb +229 -136
  64. data/lib/concurrent/scheduled_task.rb +341 -43
  65. data/lib/concurrent/settable_struct.rb +127 -0
  66. data/lib/concurrent/synchronization.rb +17 -0
  67. data/lib/concurrent/synchronization/abstract_object.rb +163 -0
  68. data/lib/concurrent/synchronization/abstract_struct.rb +158 -0
  69. data/lib/concurrent/synchronization/condition.rb +53 -0
  70. data/lib/concurrent/synchronization/java_object.rb +34 -0
  71. data/lib/concurrent/synchronization/lock.rb +32 -0
  72. data/lib/concurrent/synchronization/monitor_object.rb +26 -0
  73. data/lib/concurrent/synchronization/mutex_object.rb +43 -0
  74. data/lib/concurrent/synchronization/object.rb +78 -0
  75. data/lib/concurrent/synchronization/rbx_object.rb +75 -0
  76. data/lib/concurrent/timer_task.rb +92 -103
  77. data/lib/concurrent/tvar.rb +42 -38
  78. data/lib/concurrent/utilities.rb +3 -1
  79. data/lib/concurrent/utility/at_exit.rb +97 -0
  80. data/lib/concurrent/utility/engine.rb +44 -0
  81. data/lib/concurrent/utility/monotonic_time.rb +59 -0
  82. data/lib/concurrent/utility/native_extension_loader.rb +56 -0
  83. data/lib/concurrent/utility/processor_counter.rb +156 -0
  84. data/lib/concurrent/utility/timeout.rb +18 -14
  85. data/lib/concurrent/utility/timer.rb +11 -6
  86. data/lib/concurrent/version.rb +2 -1
  87. data/lib/concurrent_ruby.rb +1 -0
  88. data/lib/concurrent_ruby_ext.jar +0 -0
  89. metadata +46 -66
  90. data/lib/concurrent/actor.rb +0 -103
  91. data/lib/concurrent/actor/behaviour.rb +0 -70
  92. data/lib/concurrent/actor/behaviour/abstract.rb +0 -48
  93. data/lib/concurrent/actor/behaviour/awaits.rb +0 -21
  94. data/lib/concurrent/actor/behaviour/buffer.rb +0 -54
  95. data/lib/concurrent/actor/behaviour/errors_on_unknown_message.rb +0 -12
  96. data/lib/concurrent/actor/behaviour/executes_context.rb +0 -18
  97. data/lib/concurrent/actor/behaviour/linking.rb +0 -45
  98. data/lib/concurrent/actor/behaviour/pausing.rb +0 -77
  99. data/lib/concurrent/actor/behaviour/removes_child.rb +0 -16
  100. data/lib/concurrent/actor/behaviour/sets_results.rb +0 -36
  101. data/lib/concurrent/actor/behaviour/supervised.rb +0 -59
  102. data/lib/concurrent/actor/behaviour/supervising.rb +0 -34
  103. data/lib/concurrent/actor/behaviour/terminates_children.rb +0 -13
  104. data/lib/concurrent/actor/behaviour/termination.rb +0 -54
  105. data/lib/concurrent/actor/context.rb +0 -154
  106. data/lib/concurrent/actor/core.rb +0 -217
  107. data/lib/concurrent/actor/default_dead_letter_handler.rb +0 -9
  108. data/lib/concurrent/actor/envelope.rb +0 -41
  109. data/lib/concurrent/actor/errors.rb +0 -27
  110. data/lib/concurrent/actor/internal_delegations.rb +0 -49
  111. data/lib/concurrent/actor/public_delegations.rb +0 -40
  112. data/lib/concurrent/actor/reference.rb +0 -81
  113. data/lib/concurrent/actor/root.rb +0 -37
  114. data/lib/concurrent/actor/type_check.rb +0 -48
  115. data/lib/concurrent/actor/utils.rb +0 -10
  116. data/lib/concurrent/actor/utils/ad_hoc.rb +0 -21
  117. data/lib/concurrent/actor/utils/balancer.rb +0 -42
  118. data/lib/concurrent/actor/utils/broadcast.rb +0 -52
  119. data/lib/concurrent/actor/utils/pool.rb +0 -59
  120. data/lib/concurrent/actress.rb +0 -3
  121. data/lib/concurrent/agent.rb +0 -209
  122. data/lib/concurrent/atomic.rb +0 -92
  123. data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +0 -118
  124. data/lib/concurrent/atomic/copy_on_write_observer_set.rb +0 -117
  125. data/lib/concurrent/atomic/synchronization.rb +0 -51
  126. data/lib/concurrent/channel/buffered_channel.rb +0 -85
  127. data/lib/concurrent/channel/channel.rb +0 -41
  128. data/lib/concurrent/channel/unbuffered_channel.rb +0 -35
  129. data/lib/concurrent/channel/waitable_list.rb +0 -40
  130. data/lib/concurrent/channels.rb +0 -5
  131. data/lib/concurrent/collection/blocking_ring_buffer.rb +0 -71
  132. data/lib/concurrent/collection/ring_buffer.rb +0 -59
  133. data/lib/concurrent/collections.rb +0 -3
  134. data/lib/concurrent/dereferenceable.rb +0 -108
  135. data/lib/concurrent/executor/java_cached_thread_pool.rb +0 -32
  136. data/lib/concurrent/executor/java_fixed_thread_pool.rb +0 -31
  137. data/lib/concurrent/executor/ruby_cached_thread_pool.rb +0 -29
  138. data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +0 -32
  139. data/lib/concurrent/executor/ruby_thread_pool_worker.rb +0 -73
  140. data/lib/concurrent/logging.rb +0 -20
  141. data/lib/concurrent/obligation.rb +0 -171
  142. data/lib/concurrent/observable.rb +0 -73
  143. data/lib/concurrent/options_parser.rb +0 -48
  144. data/lib/concurrent/utility/processor_count.rb +0 -152
  145. data/lib/extension_helper.rb +0 -37
@@ -0,0 +1,30 @@
1
+ module Concurrent
2
+
3
+ # A submodule for unstable, highly experimental features that are likely to
4
+ # change often and which may never become part of the core gem. Also for
5
+ # new, experimental version of abstractions already in the core gem.
6
+ #
7
+ # Most new features should start in this module, clearly indicating the
8
+ # experimental and unstable nature of the feature. Once a feature becomes
9
+ # more stable and is a candidate for inclusion in the core gem it should
10
+ # be moved up to the `Concurrent` module, where it would reside once merged
11
+ # into the core gem.
12
+ #
13
+ # The only exception to this is for features which *replace* features from
14
+ # the core gem in ways that are breaking and not backward compatible. These
15
+ # features should remain in this module until merged into the core gem. This
16
+ # will prevent namespace collisions.
17
+ #
18
+ # This file should *never* be used as a global `require` for all files within
19
+ # the edge gem. Because these features are experimental users should always
20
+ # explicitly require only what they need.
21
+ #
22
+ # @!macro [attach] edge_warning
23
+ # @api Edge
24
+ # @note **Edge Feature:** Edge features are under active development and may change frequently. They are expected not to
25
+ # keep backward compatibility (there may also lack tests and documentation). Semantic versions will
26
+ # be obeyed though. Features developed in `concurrent-ruby-edge` are expected to move
27
+ # to `concurrent-ruby` when final.
28
+ module Edge
29
+ end
30
+ end
@@ -1,30 +1,42 @@
1
1
  module Concurrent
2
2
 
3
+ Error = Class.new(StandardError)
4
+
3
5
  # Raised when errors occur during configuration.
4
- ConfigurationError = Class.new(StandardError)
6
+ ConfigurationError = Class.new(Error)
7
+
8
+ # Raised when an asynchronous operation is cancelled before execution.
9
+ CancelledOperationError = Class.new(Error)
5
10
 
6
11
  # Raised when a lifecycle method (such as `stop`) is called in an improper
7
12
  # sequence or when the object is in an inappropriate state.
8
- LifecycleError = Class.new(StandardError)
13
+ LifecycleError = Class.new(Error)
14
+
15
+ # Raised when an attempt is made to violate an immutability guarantee.
16
+ ImmutabilityError = Class.new(Error)
9
17
 
10
18
  # Raised when an object's methods are called when it has not been
11
19
  # properly initialized.
12
- InitializationError = Class.new(StandardError)
20
+ InitializationError = Class.new(Error)
13
21
 
14
22
  # Raised when an object with a start/stop lifecycle has been started an
15
23
  # excessive number of times. Often used in conjunction with a restart
16
24
  # policy or strategy.
17
- MaxRestartFrequencyError = Class.new(StandardError)
25
+ MaxRestartFrequencyError = Class.new(Error)
18
26
 
19
27
  # Raised when an attempt is made to modify an immutable object
20
28
  # (such as an `IVar`) after its final state has been set.
21
- MultipleAssignmentError = Class.new(StandardError)
29
+ MultipleAssignmentError = Class.new(Error)
22
30
 
23
31
  # Raised by an `Executor` when it is unable to process a given task,
24
32
  # possibly because of a reject policy or other internal error.
25
- RejectedExecutionError = Class.new(StandardError)
33
+ RejectedExecutionError = Class.new(Error)
34
+
35
+ # Raised when any finite resource, such as a lock counter, exceeds its
36
+ # maximum limit/threshold.
37
+ ResourceLimitError = Class.new(Error)
26
38
 
27
39
  # Raised when an operation times out.
28
- TimeoutError = Class.new(StandardError)
40
+ TimeoutError = Class.new(Error)
29
41
 
30
42
  end
@@ -1,15 +1,39 @@
1
1
  module Concurrent
2
+
3
+ # A synchronization point at which threads can pair and swap elements within
4
+ # pairs. Each thread presents some object on entry to the exchange method,
5
+ # matches with a partner thread, and receives its partner's object on return.
6
+ #
7
+ # Uses `MVar` to manage synchronization of the individual elements.
8
+ # Since `MVar` is also a `Dereferenceable`, the exchanged values support all
9
+ # dereferenceable options. The constructor options hash will be passed to
10
+ # the `MVar` constructors.
11
+ #
12
+ # @see Concurrent::MVar
13
+ # @see Concurrent::Concern::Dereferenceable
14
+ # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Exchanger.html java.util.concurrent.Exchanger
15
+ #
16
+ # @!macro edge_warning
2
17
  class Exchanger
3
18
 
4
19
  EMPTY = Object.new
5
20
 
21
+ # Create a new `Exchanger` object.
22
+ #
23
+ # @param [Hash] opts the options controlling how the managed references
24
+ # will be processed
6
25
  def initialize(opts = {})
7
26
  @first = MVar.new(EMPTY, opts)
8
27
  @second = MVar.new(MVar::EMPTY, opts)
9
28
  end
10
29
 
30
+ # Waits for another thread to arrive at this exchange point (unless the
31
+ # current thread is interrupted), and then transfers the given object to
32
+ # it, receiving its object in return.
33
+ #
11
34
  # @param [Object] value the value to exchange with an other thread
12
- # @param [Numeric] timeout the maximum time in second to wait for one other thread. nil (default value) means no timeout
35
+ # @param [Numeric] timeout the maximum time in second to wait for one other
36
+ # thread. nil (default value) means no timeout
13
37
  # @return [Object] the value exchanged by the other thread; nil if timed out
14
38
  def exchange(value, timeout = nil)
15
39
  first = @first.take(timeout)
@@ -1,44 +1,62 @@
1
- require 'concurrent/executor/ruby_cached_thread_pool'
1
+ require 'concurrent/utility/engine'
2
+ require 'concurrent/executor/thread_pool_executor'
2
3
 
3
4
  module Concurrent
4
5
 
5
- if RUBY_PLATFORM == 'java'
6
- require 'concurrent/executor/java_cached_thread_pool'
7
- # @!macro [attach] cached_thread_pool
8
- # A thread pool that dynamically grows and shrinks to fit the current workload.
9
- # New threads are created as needed, existing threads are reused, and threads
10
- # that remain idle for too long are killed and removed from the pool. These
11
- # pools are particularly suited to applications that perform a high volume of
12
- # short-lived tasks.
13
- #
14
- # On creation a `CachedThreadPool` has zero running threads. New threads are
15
- # created on the pool as new operations are `#post`. The size of the pool
16
- # will grow until `#max_length` threads are in the pool or until the number
17
- # of threads exceeds the number of running and pending operations. When a new
18
- # operation is post to the pool the first available idle thread will be tasked
19
- # with the new operation.
20
- #
21
- # Should a thread crash for any reason the thread will immediately be removed
22
- # from the pool. Similarly, threads which remain idle for an extended period
23
- # of time will be killed and reclaimed. Thus these thread pools are very
24
- # efficient at reclaiming unused resources.
6
+ # A thread pool that dynamically grows and shrinks to fit the current workload.
7
+ # New threads are created as needed, existing threads are reused, and threads
8
+ # that remain idle for too long are killed and removed from the pool. These
9
+ # pools are particularly suited to applications that perform a high volume of
10
+ # short-lived tasks.
11
+ #
12
+ # On creation a `CachedThreadPool` has zero running threads. New threads are
13
+ # created on the pool as new operations are `#post`. The size of the pool
14
+ # will grow until `#max_length` threads are in the pool or until the number
15
+ # of threads exceeds the number of running and pending operations. When a new
16
+ # operation is post to the pool the first available idle thread will be tasked
17
+ # with the new operation.
18
+ #
19
+ # Should a thread crash for any reason the thread will immediately be removed
20
+ # from the pool. Similarly, threads which remain idle for an extended period
21
+ # of time will be killed and reclaimed. Thus these thread pools are very
22
+ # efficient at reclaiming unused resources.
23
+ #
24
+ # The API and behavior of this class are based on Java's `CachedThreadPool`
25
+ #
26
+ # @!macro thread_pool_options
27
+ class CachedThreadPool < ThreadPoolExecutor
28
+
29
+ # @!macro [attach] cached_thread_pool_method_initialize
25
30
  #
26
- # The API and behavior of this class are based on Java's `CachedThreadPool`
31
+ # Create a new thread pool.
27
32
  #
28
- # @note When running on the JVM (JRuby) this class will inherit from `JavaCachedThreadPool`.
29
- # On all other platforms it will inherit from `RubyCachedThreadPool`.
33
+ # @param [Hash] opts the options defining pool behavior.
34
+ # @option opts [Symbol] :fallback_policy (`:abort`) the fallback policy
30
35
  #
31
- # @see Concurrent::RubyCachedThreadPool
32
- # @see Concurrent::JavaCachedThreadPool
36
+ # @raise [ArgumentError] if `fallback_policy` is not a known policy
33
37
  #
34
- # @see http://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html
35
- # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html
36
- # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html
37
- class CachedThreadPool < JavaCachedThreadPool
38
+ # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors.html#newCachedThreadPool--
39
+ def initialize(opts = {})
40
+ defaults = { idletime: DEFAULT_THREAD_IDLETIMEOUT }
41
+ overrides = { min_threads: 0,
42
+ max_threads: DEFAULT_MAX_POOL_SIZE,
43
+ max_queue: DEFAULT_MAX_QUEUE_SIZE }
44
+ super(defaults.merge(opts).merge(overrides))
38
45
  end
39
- else
40
- # @!macro cached_thread_pool
41
- class CachedThreadPool < RubyCachedThreadPool
46
+
47
+ private
48
+
49
+ # @!macro cached_thread_pool_method_initialize
50
+ # @!visibility private
51
+ def ns_initialize(opts)
52
+ super(opts)
53
+ if Concurrent.on_jruby?
54
+ @max_queue = 0
55
+ @executor = java.util.concurrent.Executors.newCachedThreadPool
56
+ @executor.setRejectedExecutionHandler(FALLBACK_POLICY_CLASSES[@fallback_policy].new)
57
+ @executor.setKeepAliveTime(opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT), java.util.concurrent.TimeUnit::SECONDS)
58
+ self.auto_terminate = opts.fetch(:auto_terminate, true)
59
+ end
42
60
  end
43
61
  end
44
62
  end
@@ -1,319 +1,66 @@
1
- require 'concurrent/errors'
2
- require 'concurrent/logging'
3
- require 'concurrent/atomic/event'
1
+ require 'concurrent/executor/executor_service'
2
+ require 'concurrent/concern/deprecation'
4
3
 
5
4
  module Concurrent
6
5
 
6
+ # @!visibility private
7
7
  module Executor
8
- # The policy defining how rejected tasks (tasks received once the
9
- # queue size reaches the configured `max_queue`, or after the
10
- # executor has shut down) are handled. Must be one of the values
11
- # specified in `FALLBACK_POLICIES`.
12
- attr_reader :fallback_policy
8
+ extend Concern::Deprecation
13
9
 
14
- # @!macro [attach] executor_module_method_can_overflow_question
10
+ # Get the requested `Executor` based on the values set in the options hash.
15
11
  #
16
- # Does the task queue have a maximum size?
12
+ # @param [Hash] opts the options defining the requested executor
13
+ # @option opts [Executor] :executor when set use the given `Executor` instance.
14
+ # Three special values are also supported: `:fast` returns the global fast executor,
15
+ # `:io` returns the global io executor, and `:immediate` returns a new
16
+ # `ImmediateExecutor` object.
17
17
  #
18
- # @return [Boolean] True if the task queue has a maximum size else false.
19
- #
20
- # @note Always returns `false`
21
- def can_overflow?
22
- false
23
- end
24
-
25
- # Handler which executes the `fallback_policy` once the queue size
26
- # reaches `max_queue`.
27
- #
28
- # @param [Array] args the arguments to the task which is being handled.
18
+ # @return [Executor, nil] the requested thread pool, or nil when no option specified
29
19
  #
30
20
  # @!visibility private
31
- def handle_fallback(*args)
32
- case @fallback_policy
33
- when :abort
34
- raise RejectedExecutionError
35
- when :discard
36
- false
37
- when :caller_runs
38
- begin
39
- yield(*args)
40
- rescue => ex
41
- # let it fail
42
- log DEBUG, ex
43
- end
44
- true
45
- else
46
- fail "Unknown fallback policy #{@fallback_policy}"
47
- end
48
- end
49
-
50
- # @!macro [attach] executor_module_method_serialized_question
51
- #
52
- # Does this executor guarantee serialization of its operations?
53
- #
54
- # @return [Boolean] True if the executor guarantees that all operations
55
- # will be post in the order they are received and no two operations may
56
- # occur simultaneously. Else false.
57
- #
58
- # @note Always returns `false`
59
- def serialized?
60
- false
61
- end
62
- end
63
-
64
- # Indicates that the including `Executor` or `ExecutorService` guarantees
65
- # that all operations will occur in the order they are post and that no
66
- # two operations may occur simultaneously. This module provides no
67
- # functionality and provides no guarantees. That is the responsibility
68
- # of the including class. This module exists solely to allow the including
69
- # object to be interrogated for its serialization status.
70
- #
71
- # @example
72
- # class Foo
73
- # include Concurrent::SerialExecutor
74
- # end
75
- #
76
- # foo = Foo.new
77
- #
78
- # foo.is_a? Concurrent::Executor #=> true
79
- # foo.is_a? Concurrent::SerialExecutor #=> true
80
- # foo.serialized? #=> true
81
- module SerialExecutor
82
- include Executor
83
-
84
- # @!macro executor_module_method_serialized_question
85
- #
86
- # @note Always returns `true`
87
- def serialized?
88
- true
89
- end
90
- end
91
-
92
- module RubyExecutor
93
- include Executor
94
- include Logging
95
-
96
- # The set of possible fallback policies that may be set at thread pool creation.
97
- FALLBACK_POLICIES = [:abort, :discard, :caller_runs]
98
-
99
- # @!macro [attach] executor_method_post
100
- #
101
- # Submit a task to the executor for asynchronous processing.
102
- #
103
- # @param [Array] args zero or more arguments to be passed to the task
104
- #
105
- # @yield the asynchronous task to perform
106
- #
107
- # @return [Boolean] `true` if the task is queued, `false` if the executor
108
- # is not running
109
- #
110
- # @raise [ArgumentError] if no task is given
111
- def post(*args, &task)
112
- raise ArgumentError.new('no block given') unless block_given?
113
- mutex.synchronize do
114
- # If the executor is shut down, reject this task
115
- return handle_fallback(*args, &task) unless running?
116
- execute(*args, &task)
117
- true
118
- end
119
- end
120
-
121
- # @!macro [attach] executor_method_left_shift
122
- #
123
- # Submit a task to the executor for asynchronous processing.
124
- #
125
- # @param [Proc] task the asynchronous task to perform
126
- #
127
- # @return [self] returns itself
128
- def <<(task)
129
- post(&task)
130
- self
131
- end
132
-
133
- # @!macro [attach] executor_method_running_question
134
- #
135
- # Is the executor running?
136
- #
137
- # @return [Boolean] `true` when running, `false` when shutting down or shutdown
138
- def running?
139
- ! stop_event.set?
140
- end
141
-
142
- # @!macro [attach] executor_method_shuttingdown_question
143
- #
144
- # Is the executor shuttingdown?
145
- #
146
- # @return [Boolean] `true` when not running and not shutdown, else `false`
147
- def shuttingdown?
148
- ! (running? || shutdown?)
149
- end
150
-
151
- # @!macro [attach] executor_method_shutdown_question
152
- #
153
- # Is the executor shutdown?
154
- #
155
- # @return [Boolean] `true` when shutdown, `false` when shutting down or running
156
- def shutdown?
157
- stopped_event.set?
158
- end
159
-
160
- # @!macro [attach] executor_method_shutdown
161
- #
162
- # Begin an orderly shutdown. Tasks already in the queue will be executed,
163
- # but no new tasks will be accepted. Has no additional effect if the
164
- # thread pool is not running.
165
- def shutdown
166
- mutex.synchronize do
167
- break unless running?
168
- stop_event.set
169
- shutdown_execution
170
- end
171
- true
172
- end
173
-
174
- # @!macro [attach] executor_method_kill
175
- #
176
- # Begin an immediate shutdown. In-progress tasks will be allowed to
177
- # complete but enqueued tasks will be dismissed and no new tasks
178
- # will be accepted. Has no additional effect if the thread pool is
179
- # not running.
180
- def kill
181
- mutex.synchronize do
182
- break if shutdown?
183
- stop_event.set
184
- kill_execution
185
- stopped_event.set
186
- end
187
- true
188
- end
189
-
190
- # @!macro [attach] executor_method_wait_for_termination
191
- #
192
- # Block until executor shutdown is complete or until `timeout` seconds have
193
- # passed.
194
- #
195
- # @note Does not initiate shutdown or termination. Either `shutdown` or `kill`
196
- # must be called before this method (or on another thread).
197
- #
198
- # @param [Integer] timeout the maximum number of seconds to wait for shutdown to complete
199
- #
200
- # @return [Boolean] `true` if shutdown complete or false on `timeout`
201
- def wait_for_termination(timeout = nil)
202
- stopped_event.wait(timeout)
203
- end
204
-
205
- protected
206
-
207
- attr_reader :mutex, :stop_event, :stopped_event
208
-
209
- # @!macro [attach] executor_method_init_executor
210
- #
211
- # Initialize the executor by creating and initializing all the
212
- # internal synchronization objects.
213
- def init_executor
214
- @mutex = Mutex.new
215
- @stop_event = Event.new
216
- @stopped_event = Event.new
217
- end
218
-
219
- # @!macro [attach] executor_method_execute
220
- def execute(*args, &task)
221
- raise NotImplementedError
222
- end
223
-
224
- # @!macro [attach] executor_method_shutdown_execution
225
- #
226
- # Callback method called when an orderly shutdown has completed.
227
- # The default behavior is to signal all waiting threads.
228
- def shutdown_execution
229
- stopped_event.set
230
- end
231
-
232
- # @!macro [attach] executor_method_kill_execution
233
- #
234
- # Callback method called when the executor has been killed.
235
- # The default behavior is to do nothing.
236
- def kill_execution
237
- # do nothing
238
- end
239
- end
240
-
241
- if RUBY_PLATFORM == 'java'
242
-
243
- module JavaExecutor
244
- include Executor
245
- java_import 'java.lang.Runnable'
246
-
247
- # The set of possible fallback policies that may be set at thread pool creation.
248
- FALLBACK_POLICIES = {
249
- abort: java.util.concurrent.ThreadPoolExecutor::AbortPolicy,
250
- discard: java.util.concurrent.ThreadPoolExecutor::DiscardPolicy,
251
- caller_runs: java.util.concurrent.ThreadPoolExecutor::CallerRunsPolicy
252
- }.freeze
253
-
254
- # @!macro executor_method_post
255
- def post(*args, &task)
256
- raise ArgumentError.new('no block given') unless block_given?
257
- return handle_fallback(*args, &task) unless running?
258
- executor_submit = @executor.java_method(:submit, [Runnable.java_class])
259
- executor_submit.call { yield(*args) }
260
- true
261
- rescue Java::JavaUtilConcurrent::RejectedExecutionException
262
- raise RejectedExecutionError
263
- end
264
-
265
- # @!macro executor_method_left_shift
266
- def <<(task)
267
- post(&task)
268
- self
269
- end
270
-
271
- # @!macro executor_method_running_question
272
- def running?
273
- ! (shuttingdown? || shutdown?)
274
- end
275
-
276
- # @!macro executor_method_shuttingdown_question
277
- def shuttingdown?
278
- if @executor.respond_to? :isTerminating
279
- @executor.isTerminating
21
+ def self.executor_from_options(opts = {}) # :nodoc:
22
+ case
23
+ when opts.key?(:executor)
24
+ if opts[:executor].nil?
25
+ nil
280
26
  else
281
- false
27
+ executor(opts[:executor])
282
28
  end
283
- end
284
-
285
- # @!macro executor_method_shutdown_question
286
- def shutdown?
287
- @executor.isShutdown || @executor.isTerminated
288
- end
289
-
290
- # @!macro executor_method_wait_for_termination
291
- def wait_for_termination(timeout = nil)
292
- if timeout.nil?
293
- ok = @executor.awaitTermination(60, java.util.concurrent.TimeUnit::SECONDS) until ok
294
- true
295
- else
296
- @executor.awaitTermination(1000 * timeout, java.util.concurrent.TimeUnit::MILLISECONDS)
29
+ when opts.key?(:operation) || opts.key?(:task)
30
+ if opts[:operation] == true || opts[:task] == false
31
+ deprecated 'use `executor: :fast` instead'
32
+ return Concurrent.global_fast_executor
297
33
  end
298
- end
299
34
 
300
- # @!macro executor_method_shutdown
301
- def shutdown
302
- @executor.shutdown
303
- nil
304
- end
35
+ if opts[:operation] == false || opts[:task] == true
36
+ deprecated 'use `executor: :io` instead'
37
+ return Concurrent.global_io_executor
38
+ end
305
39
 
306
- # @!macro executor_method_kill
307
- def kill
308
- @executor.shutdownNow
40
+ raise ArgumentError.new("executor '#{opts[:executor]}' not recognized")
41
+ else
309
42
  nil
310
43
  end
44
+ end
311
45
 
312
- protected
313
-
314
- def set_shutdown_hook
315
- # without this the process may fail to exit
316
- at_exit { self.kill }
46
+ def self.executor(executor_identifier)
47
+ case executor_identifier
48
+ when :fast
49
+ Concurrent.global_fast_executor
50
+ when :io
51
+ Concurrent.global_io_executor
52
+ when :immediate
53
+ Concurrent.global_immediate_executor
54
+ when :operation
55
+ deprecated 'use `executor: :fast` instead'
56
+ Concurrent.global_fast_executor
57
+ when :task
58
+ deprecated 'use `executor: :io` instead'
59
+ Concurrent.global_io_executor
60
+ when Concurrent::ExecutorService
61
+ executor_identifier
62
+ else
63
+ raise ArgumentError, "executor not recognized by '#{executor_identifier}'"
317
64
  end
318
65
  end
319
66
  end