concurrent-ruby 0.6.0.pre.1 → 0.6.0.pre.2

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 (142) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +16 -0
  3. data/lib/concurrent.rb +9 -29
  4. data/lib/concurrent/{actor.rb → actor/actor.rb} +3 -3
  5. data/lib/concurrent/actor/actor_context.rb +77 -0
  6. data/lib/concurrent/actor/actor_ref.rb +67 -0
  7. data/lib/concurrent/{postable.rb → actor/postable.rb} +1 -1
  8. data/lib/concurrent/actor/simple_actor_ref.rb +94 -0
  9. data/lib/concurrent/actors.rb +5 -0
  10. data/lib/concurrent/agent.rb +81 -47
  11. data/lib/concurrent/async.rb +35 -35
  12. data/lib/concurrent/atomic/atomic_boolean.rb +157 -0
  13. data/lib/concurrent/atomic/atomic_fixnum.rb +170 -0
  14. data/lib/concurrent/{condition.rb → atomic/condition.rb} +0 -0
  15. data/lib/concurrent/{copy_on_notify_observer_set.rb → atomic/copy_on_notify_observer_set.rb} +48 -13
  16. data/lib/concurrent/{copy_on_write_observer_set.rb → atomic/copy_on_write_observer_set.rb} +41 -20
  17. data/lib/concurrent/atomic/count_down_latch.rb +116 -0
  18. data/lib/concurrent/atomic/cyclic_barrier.rb +106 -0
  19. data/lib/concurrent/atomic/event.rb +103 -0
  20. data/lib/concurrent/{thread_local_var.rb → atomic/thread_local_var.rb} +0 -0
  21. data/lib/concurrent/atomics.rb +9 -0
  22. data/lib/concurrent/channel/buffered_channel.rb +6 -4
  23. data/lib/concurrent/channel/channel.rb +30 -2
  24. data/lib/concurrent/channel/unbuffered_channel.rb +2 -2
  25. data/lib/concurrent/channel/waitable_list.rb +3 -1
  26. data/lib/concurrent/channels.rb +5 -0
  27. data/lib/concurrent/{channel → collection}/blocking_ring_buffer.rb +16 -5
  28. data/lib/concurrent/collection/priority_queue.rb +305 -0
  29. data/lib/concurrent/{channel → collection}/ring_buffer.rb +6 -1
  30. data/lib/concurrent/collections.rb +3 -0
  31. data/lib/concurrent/configuration.rb +68 -19
  32. data/lib/concurrent/dataflow.rb +9 -9
  33. data/lib/concurrent/delay.rb +21 -13
  34. data/lib/concurrent/dereferenceable.rb +40 -33
  35. data/lib/concurrent/exchanger.rb +3 -0
  36. data/lib/concurrent/{cached_thread_pool.rb → executor/cached_thread_pool.rb} +8 -9
  37. data/lib/concurrent/executor/executor.rb +222 -0
  38. data/lib/concurrent/{fixed_thread_pool.rb → executor/fixed_thread_pool.rb} +6 -7
  39. data/lib/concurrent/{immediate_executor.rb → executor/immediate_executor.rb} +5 -5
  40. data/lib/concurrent/executor/java_cached_thread_pool.rb +31 -0
  41. data/lib/concurrent/{java_fixed_thread_pool.rb → executor/java_fixed_thread_pool.rb} +7 -11
  42. data/lib/concurrent/executor/java_single_thread_executor.rb +21 -0
  43. data/lib/concurrent/{java_thread_pool_executor.rb → executor/java_thread_pool_executor.rb} +66 -77
  44. data/lib/concurrent/executor/one_by_one.rb +65 -0
  45. data/lib/concurrent/{per_thread_executor.rb → executor/per_thread_executor.rb} +4 -4
  46. data/lib/concurrent/executor/ruby_cached_thread_pool.rb +29 -0
  47. data/lib/concurrent/{ruby_fixed_thread_pool.rb → executor/ruby_fixed_thread_pool.rb} +5 -4
  48. data/lib/concurrent/executor/ruby_single_thread_executor.rb +72 -0
  49. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +282 -0
  50. data/lib/concurrent/{ruby_thread_pool_worker.rb → executor/ruby_thread_pool_worker.rb} +6 -6
  51. data/lib/concurrent/{safe_task_executor.rb → executor/safe_task_executor.rb} +20 -13
  52. data/lib/concurrent/executor/single_thread_executor.rb +35 -0
  53. data/lib/concurrent/executor/thread_pool_executor.rb +68 -0
  54. data/lib/concurrent/executor/timer_set.rb +138 -0
  55. data/lib/concurrent/executors.rb +9 -0
  56. data/lib/concurrent/future.rb +39 -40
  57. data/lib/concurrent/ivar.rb +22 -15
  58. data/lib/concurrent/mvar.rb +2 -1
  59. data/lib/concurrent/obligation.rb +9 -3
  60. data/lib/concurrent/observable.rb +33 -0
  61. data/lib/concurrent/options_parser.rb +46 -0
  62. data/lib/concurrent/promise.rb +23 -24
  63. data/lib/concurrent/scheduled_task.rb +21 -45
  64. data/lib/concurrent/timer_task.rb +204 -126
  65. data/lib/concurrent/tvar.rb +1 -1
  66. data/lib/concurrent/utilities.rb +3 -36
  67. data/lib/concurrent/{processor_count.rb → utility/processor_count.rb} +1 -1
  68. data/lib/concurrent/utility/timeout.rb +36 -0
  69. data/lib/concurrent/utility/timer.rb +21 -0
  70. data/lib/concurrent/version.rb +1 -1
  71. data/lib/concurrent_ruby_ext.bundle +0 -0
  72. data/spec/concurrent/{actor_context_spec.rb → actor/actor_context_spec.rb} +0 -8
  73. data/spec/concurrent/{actor_ref_shared.rb → actor/actor_ref_shared.rb} +9 -59
  74. data/spec/concurrent/{actor_spec.rb → actor/actor_spec.rb} +43 -41
  75. data/spec/concurrent/{postable_shared.rb → actor/postable_shared.rb} +0 -0
  76. data/spec/concurrent/actor/simple_actor_ref_spec.rb +135 -0
  77. data/spec/concurrent/agent_spec.rb +160 -71
  78. data/spec/concurrent/atomic/atomic_boolean_spec.rb +172 -0
  79. data/spec/concurrent/atomic/atomic_fixnum_spec.rb +186 -0
  80. data/spec/concurrent/{condition_spec.rb → atomic/condition_spec.rb} +2 -2
  81. data/spec/concurrent/{copy_on_notify_observer_set_spec.rb → atomic/copy_on_notify_observer_set_spec.rb} +0 -0
  82. data/spec/concurrent/{copy_on_write_observer_set_spec.rb → atomic/copy_on_write_observer_set_spec.rb} +0 -0
  83. data/spec/concurrent/atomic/count_down_latch_spec.rb +151 -0
  84. data/spec/concurrent/atomic/cyclic_barrier_spec.rb +248 -0
  85. data/spec/concurrent/{event_spec.rb → atomic/event_spec.rb} +18 -3
  86. data/spec/concurrent/{observer_set_shared.rb → atomic/observer_set_shared.rb} +15 -6
  87. data/spec/concurrent/{thread_local_var_spec.rb → atomic/thread_local_var_spec.rb} +0 -0
  88. data/spec/concurrent/channel/buffered_channel_spec.rb +1 -1
  89. data/spec/concurrent/channel/channel_spec.rb +6 -4
  90. data/spec/concurrent/channel/probe_spec.rb +37 -9
  91. data/spec/concurrent/channel/unbuffered_channel_spec.rb +2 -2
  92. data/spec/concurrent/{channel → collection}/blocking_ring_buffer_spec.rb +0 -0
  93. data/spec/concurrent/collection/priority_queue_spec.rb +317 -0
  94. data/spec/concurrent/{channel → collection}/ring_buffer_spec.rb +0 -0
  95. data/spec/concurrent/configuration_spec.rb +4 -70
  96. data/spec/concurrent/dereferenceable_shared.rb +5 -4
  97. data/spec/concurrent/exchanger_spec.rb +10 -5
  98. data/spec/concurrent/{cached_thread_pool_shared.rb → executor/cached_thread_pool_shared.rb} +15 -37
  99. data/spec/concurrent/{fixed_thread_pool_shared.rb → executor/fixed_thread_pool_shared.rb} +0 -0
  100. data/spec/concurrent/{global_thread_pool_shared.rb → executor/global_thread_pool_shared.rb} +10 -8
  101. data/spec/concurrent/{immediate_executor_spec.rb → executor/immediate_executor_spec.rb} +0 -0
  102. data/spec/concurrent/{java_cached_thread_pool_spec.rb → executor/java_cached_thread_pool_spec.rb} +1 -21
  103. data/spec/concurrent/{java_fixed_thread_pool_spec.rb → executor/java_fixed_thread_pool_spec.rb} +0 -0
  104. data/spec/concurrent/executor/java_single_thread_executor_spec.rb +21 -0
  105. data/spec/concurrent/{java_thread_pool_executor_spec.rb → executor/java_thread_pool_executor_spec.rb} +0 -0
  106. data/spec/concurrent/{per_thread_executor_spec.rb → executor/per_thread_executor_spec.rb} +0 -4
  107. data/spec/concurrent/{ruby_cached_thread_pool_spec.rb → executor/ruby_cached_thread_pool_spec.rb} +1 -1
  108. data/spec/concurrent/{ruby_fixed_thread_pool_spec.rb → executor/ruby_fixed_thread_pool_spec.rb} +0 -0
  109. data/spec/concurrent/executor/ruby_single_thread_executor_spec.rb +18 -0
  110. data/spec/concurrent/{ruby_thread_pool_executor_spec.rb → executor/ruby_thread_pool_executor_spec.rb} +12 -24
  111. data/spec/concurrent/executor/safe_task_executor_spec.rb +103 -0
  112. data/spec/concurrent/{thread_pool_class_cast_spec.rb → executor/thread_pool_class_cast_spec.rb} +12 -0
  113. data/spec/concurrent/{thread_pool_executor_shared.rb → executor/thread_pool_executor_shared.rb} +0 -0
  114. data/spec/concurrent/{thread_pool_shared.rb → executor/thread_pool_shared.rb} +84 -119
  115. data/spec/concurrent/executor/timer_set_spec.rb +183 -0
  116. data/spec/concurrent/future_spec.rb +12 -0
  117. data/spec/concurrent/ivar_spec.rb +11 -1
  118. data/spec/concurrent/observable_shared.rb +173 -0
  119. data/spec/concurrent/observable_spec.rb +51 -0
  120. data/spec/concurrent/options_parser_spec.rb +71 -0
  121. data/spec/concurrent/runnable_shared.rb +6 -0
  122. data/spec/concurrent/scheduled_task_spec.rb +60 -40
  123. data/spec/concurrent/timer_task_spec.rb +130 -144
  124. data/spec/concurrent/{processor_count_spec.rb → utility/processor_count_spec.rb} +0 -0
  125. data/spec/concurrent/{utilities_spec.rb → utility/timeout_spec.rb} +0 -0
  126. data/spec/concurrent/utility/timer_spec.rb +52 -0
  127. metadata +147 -108
  128. data/lib/concurrent/actor_context.rb +0 -31
  129. data/lib/concurrent/actor_ref.rb +0 -39
  130. data/lib/concurrent/atomic.rb +0 -121
  131. data/lib/concurrent/channel/probe.rb +0 -19
  132. data/lib/concurrent/count_down_latch.rb +0 -60
  133. data/lib/concurrent/event.rb +0 -80
  134. data/lib/concurrent/java_cached_thread_pool.rb +0 -45
  135. data/lib/concurrent/ruby_cached_thread_pool.rb +0 -37
  136. data/lib/concurrent/ruby_thread_pool_executor.rb +0 -268
  137. data/lib/concurrent/simple_actor_ref.rb +0 -124
  138. data/lib/concurrent/thread_pool_executor.rb +0 -30
  139. data/spec/concurrent/atomic_spec.rb +0 -201
  140. data/spec/concurrent/count_down_latch_spec.rb +0 -125
  141. data/spec/concurrent/safe_task_executor_spec.rb +0 -58
  142. data/spec/concurrent/simple_actor_ref_spec.rb +0 -219
@@ -0,0 +1,222 @@
1
+ require 'concurrent/atomic/event'
2
+
3
+ module Concurrent
4
+
5
+ # An exception class raised when the maximum queue size is reached and the
6
+ # `overflow_policy` is set to `:abort`.
7
+ RejectedExecutionError = Class.new(StandardError)
8
+
9
+ module Executor
10
+
11
+ # Submit a task to the executor for asynchronous processing.
12
+ #
13
+ # @param [Array] args zero or more arguments to be passed to the task
14
+ #
15
+ # @yield the asynchronous task to perform
16
+ #
17
+ # @return [Boolean] `true` if the task is queued, `false` if the executor
18
+ # is not running
19
+ #
20
+ # @raise [ArgumentError] if no task is given
21
+ def post(*args, &task)
22
+ raise ArgumentError.new('no block given') unless block_given?
23
+ mutex.synchronize do
24
+ return false unless running?
25
+ execute(*args, &task)
26
+ true
27
+ end
28
+ end
29
+
30
+ # Submit a task to the executor for asynchronous processing.
31
+ #
32
+ # @param [Proc] task the asynchronous task to perform
33
+ #
34
+ # @return [self] returns itself
35
+ def <<(task)
36
+ post(&task)
37
+ self
38
+ end
39
+
40
+ # Is the executor running?
41
+ #
42
+ # @return [Boolean] `true` when running, `false` when shutting down or shutdown
43
+ def running?
44
+ ! stop_event.set?
45
+ end
46
+
47
+ # Is the executor shuttingdown?
48
+ #
49
+ # @return [Boolean] `true` when not running and not shutdown, else `false`
50
+ def shuttingdown?
51
+ ! (running? || shutdown?)
52
+ end
53
+
54
+ # Is the executor shutdown?
55
+ #
56
+ # @return [Boolean] `true` when shutdown, `false` when shutting down or running
57
+ def shutdown?
58
+ stopped_event.set?
59
+ end
60
+
61
+ # Begin an orderly shutdown. Tasks already in the queue will be executed,
62
+ # but no new tasks will be accepted. Has no additional effect if the
63
+ # thread pool is not running.
64
+ def shutdown
65
+ mutex.synchronize do
66
+ break unless running?
67
+ stop_event.set
68
+ shutdown_execution
69
+ end
70
+ true
71
+ end
72
+
73
+ # Begin an immediate shutdown. In-progress tasks will be allowed to
74
+ # complete but enqueued tasks will be dismissed and no new tasks
75
+ # will be accepted. Has no additional effect if the thread pool is
76
+ # not running.
77
+ def kill
78
+ mutex.synchronize do
79
+ break if shutdown?
80
+ stop_event.set
81
+ kill_execution
82
+ stopped_event.set
83
+ end
84
+ true
85
+ end
86
+
87
+ # Block until executor shutdown is complete or until `timeout` seconds have
88
+ # passed.
89
+ #
90
+ # @note Does not initiate shutdown or termination. Either `shutdown` or `kill`
91
+ # must be called before this method (or on another thread).
92
+ #
93
+ # @param [Integer] timeout the maximum number of seconds to wait for shutdown to complete
94
+ #
95
+ # @return [Boolean] `true` if shutdown complete or false on `timeout`
96
+ def wait_for_termination(timeout = nil)
97
+ stopped_event.wait(timeout)
98
+ end
99
+
100
+ protected
101
+
102
+ attr_reader :mutex, :stop_event, :stopped_event
103
+
104
+ def init_executor
105
+ @mutex = Mutex.new
106
+ @stop_event = Event.new
107
+ @stopped_event = Event.new
108
+ end
109
+
110
+ def execute(*args, &task)
111
+ raise NotImplementedError
112
+ end
113
+
114
+ def shutdown_execution
115
+ stopped_event.set
116
+ end
117
+
118
+ def kill_execution
119
+ # do nothing
120
+ end
121
+ end
122
+
123
+ if RUBY_PLATFORM == 'java'
124
+
125
+ module JavaExecutor
126
+
127
+ # Submit a task to the executor for asynchronous processing.
128
+ #
129
+ # @param [Array] args zero or more arguments to be passed to the task
130
+ #
131
+ # @yield the asynchronous task to perform
132
+ #
133
+ # @return [Boolean] `true` if the task is queued, `false` if the executor
134
+ # is not running
135
+ #
136
+ # @raise [ArgumentError] if no task is given
137
+ def post(*args)
138
+ raise ArgumentError.new('no block given') unless block_given?
139
+ if running?
140
+ @executor.submit{ yield(*args) }
141
+ true
142
+ else
143
+ false
144
+ end
145
+ rescue Java::JavaUtilConcurrent::RejectedExecutionException => ex
146
+ raise RejectedExecutionError
147
+ end
148
+
149
+ # Submit a task to the executor for asynchronous processing.
150
+ #
151
+ # @param [Proc] task the asynchronous task to perform
152
+ #
153
+ # @return [self] returns itself
154
+ def <<(task)
155
+ post(&task)
156
+ self
157
+ end
158
+
159
+ # Is the executor running?
160
+ #
161
+ # @return [Boolean] `true` when running, `false` when shutting down or shutdown
162
+ def running?
163
+ ! (shuttingdown? || shutdown?)
164
+ end
165
+
166
+ # Is the executor shuttingdown?
167
+ #
168
+ # @return [Boolean] `true` when not running and not shutdown, else `false`
169
+ def shuttingdown?
170
+ if @executor.respond_to? :isTerminating
171
+ @executor.isTerminating
172
+ else
173
+ false
174
+ end
175
+ end
176
+
177
+ # Is the executor shutdown?
178
+ #
179
+ # @return [Boolean] `true` when shutdown, `false` when shutting down or running
180
+ def shutdown?
181
+ @executor.isShutdown || @executor.isTerminated
182
+ end
183
+
184
+ # Block until executor shutdown is complete or until `timeout` seconds have
185
+ # passed.
186
+ #
187
+ # @note Does not initiate shutdown or termination. Either `shutdown` or `kill`
188
+ # must be called before this method (or on another thread).
189
+ #
190
+ # @param [Integer] timeout the maximum number of seconds to wait for shutdown to complete
191
+ #
192
+ # @return [Boolean] `true` if shutdown complete or false on `timeout`
193
+ def wait_for_termination(timeout)
194
+ @executor.awaitTermination(1000 * timeout, java.util.concurrent.TimeUnit::MILLISECONDS)
195
+ end
196
+
197
+ # Begin an orderly shutdown. Tasks already in the queue will be executed,
198
+ # but no new tasks will be accepted. Has no additional effect if the
199
+ # executor is not running.
200
+ def shutdown
201
+ @executor.shutdown
202
+ nil
203
+ end
204
+
205
+ # Begin an immediate shutdown. In-progress tasks will be allowed to
206
+ # complete but enqueued tasks will be dismissed and no new tasks
207
+ # will be accepted. Has no additional effect if the executor is
208
+ # not running.
209
+ def kill
210
+ @executor.shutdownNow
211
+ nil
212
+ end
213
+
214
+ protected
215
+
216
+ def set_shutdown_hook
217
+ # without this the process may fail to exit
218
+ at_exit { self.kill }
219
+ end
220
+ end
221
+ end
222
+ end
@@ -1,21 +1,21 @@
1
- require 'concurrent/ruby_fixed_thread_pool'
1
+ require 'concurrent/executor/ruby_fixed_thread_pool'
2
2
 
3
3
  module Concurrent
4
4
 
5
5
  if RUBY_PLATFORM == 'java'
6
- require 'concurrent/java_fixed_thread_pool'
6
+ require 'concurrent/executor/java_fixed_thread_pool'
7
7
  # @!macro [attach] fixed_thread_pool
8
8
  #
9
9
  # A thread pool with a set number of threads. The number of threads in the pool
10
10
  # is set on construction and remains constant. When all threads are busy new
11
- # tasks +#post+ to the thread pool are enqueued until a thread becomes available.
11
+ # tasks `#post` to the thread pool are enqueued until a thread becomes available.
12
12
  # Should a thread crash for any reason the thread will immediately be removed
13
13
  # from the pool and replaced.
14
14
  #
15
- # The API and behavior of this class are based on Java's +FixedThreadPool+
15
+ # The API and behavior of this class are based on Java's `FixedThreadPool`
16
16
  #
17
- # @note When running on the JVM (JRuby) this class will inherit from +JavaFixedThreadPool+.
18
- # On all other platforms it will inherit from +RubyFixedThreadPool+.
17
+ # @note When running on the JVM (JRuby) this class will inherit from `JavaFixedThreadPool`.
18
+ # On all other platforms it will inherit from `RubyFixedThreadPool`.
19
19
  #
20
20
  # @see Concurrent::RubyFixedThreadPool
21
21
  # @see Concurrent::JavaFixedThreadPool
@@ -23,7 +23,6 @@ module Concurrent
23
23
  # @see http://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html
24
24
  # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html
25
25
  # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html
26
- # @see http://stackoverflow.com/questions/17957382/fixedthreadpool-vs-fixedthreadpool-the-lesser-of-two-evils
27
26
  class FixedThreadPool < JavaFixedThreadPool
28
27
  end
29
28
  else
@@ -1,14 +1,14 @@
1
1
  module Concurrent
2
2
  class ImmediateExecutor
3
3
 
4
- def post(*args, &block)
4
+ def post(*args, &task)
5
5
  raise ArgumentError.new('no block given') unless block_given?
6
- block.call(*args)
7
- return true
6
+ task.call(*args)
7
+ true
8
8
  end
9
9
 
10
- def <<(block)
11
- post(&block)
10
+ def <<(task)
11
+ post(&task)
12
12
  self
13
13
  end
14
14
  end
@@ -0,0 +1,31 @@
1
+ if RUBY_PLATFORM == 'java'
2
+
3
+ require 'concurrent/executor/java_thread_pool_executor'
4
+
5
+ module Concurrent
6
+
7
+ # @!macro cached_thread_pool
8
+ class JavaCachedThreadPool < JavaThreadPoolExecutor
9
+
10
+ # Create a new thread pool.
11
+ #
12
+ # @param [Hash] opts the options defining pool behavior.
13
+ # @option opts [Symbol] :overflow_policy (`:abort`) the overflow policy
14
+ #
15
+ # @raise [ArgumentError] if `overflow_policy` is not a known policy
16
+ #
17
+ # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors.html#newCachedThreadPool--
18
+ def initialize(opts = {})
19
+ @overflow_policy = opts.fetch(:overflow_policy, :abort)
20
+ @max_queue = 0
21
+
22
+ raise ArgumentError.new("#{@overflow_policy} is not a valid overflow policy") unless OVERFLOW_POLICIES.keys.include?(@overflow_policy)
23
+
24
+ @executor = java.util.concurrent.Executors.newCachedThreadPool
25
+ @executor.setRejectedExecutionHandler(OVERFLOW_POLICIES[@overflow_policy].new)
26
+
27
+ set_shutdown_hook
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,6 +1,6 @@
1
1
  if RUBY_PLATFORM == 'java'
2
2
 
3
- require 'concurrent/java_thread_pool_executor'
3
+ require 'concurrent/executor/java_thread_pool_executor'
4
4
 
5
5
  module Concurrent
6
6
 
@@ -10,10 +10,10 @@ if RUBY_PLATFORM == 'java'
10
10
  # Create a new thread pool.
11
11
  #
12
12
  # @param [Hash] opts the options defining pool behavior.
13
- # @option opts [Symbol] :overflow_policy (+:abort+) the overflow policy
13
+ # @option opts [Symbol] :overflow_policy (`:abort`) the overflow policy
14
14
  #
15
- # @raise [ArgumentError] if +num_threads+ is less than or equal to zero
16
- # @raise [ArgumentError] if +overflow_policy+ is not a known policy
15
+ # @raise [ArgumentError] if `num_threads` is less than or equal to zero
16
+ # @raise [ArgumentError] if `overflow_policy` is not a known policy
17
17
  #
18
18
  # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors.html#newFixedThreadPool-int-
19
19
  def initialize(num_threads, opts = {})
@@ -23,14 +23,10 @@ if RUBY_PLATFORM == 'java'
23
23
  raise ArgumentError.new('number of threads must be greater than zero') if num_threads < 1
24
24
  raise ArgumentError.new("#{@overflow_policy} is not a valid overflow policy") unless OVERFLOW_POLICIES.keys.include?(@overflow_policy)
25
25
 
26
- @executor = java.util.concurrent.ThreadPoolExecutor.new(
27
- num_threads, num_threads,
28
- @max_queue, java.util.concurrent.TimeUnit::SECONDS,
29
- java.util.concurrent.LinkedBlockingQueue.new,
30
- OVERFLOW_POLICIES[@overflow_policy].new)
26
+ @executor = java.util.concurrent.Executors.newFixedThreadPool(num_threads)
27
+ @executor.setRejectedExecutionHandler(OVERFLOW_POLICIES[@overflow_policy].new)
31
28
 
32
- # without this the process may fail to exit
33
- at_exit { self.kill }
29
+ set_shutdown_hook
34
30
  end
35
31
  end
36
32
  end
@@ -0,0 +1,21 @@
1
+ if RUBY_PLATFORM == 'java'
2
+ require_relative 'executor'
3
+
4
+ module Concurrent
5
+
6
+ # @!macro single_thread_executor
7
+ class JavaSingleThreadExecutor
8
+ include JavaExecutor
9
+
10
+ # Create a new thread pool.
11
+ #
12
+ # @see http://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html
13
+ # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html
14
+ # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html
15
+ def initialize(opts = {})
16
+ @executor = java.util.concurrent.Executors.newSingleThreadExecutor
17
+ set_shutdown_hook
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,26 +1,26 @@
1
1
  if RUBY_PLATFORM == 'java'
2
+ require_relative 'executor'
2
3
 
3
4
  module Concurrent
4
5
 
5
- RejectedExecutionError = Class.new(StandardError) unless defined? RejectedExecutionError
6
-
7
6
  # @!macro thread_pool_executor
8
7
  class JavaThreadPoolExecutor
8
+ include JavaExecutor
9
9
 
10
- # The maximum number of threads that will be created in the pool
11
- # (unless overridden during construction).
10
+ # Default maximum number of threads that will be created in the pool.
12
11
  DEFAULT_MAX_POOL_SIZE = java.lang.Integer::MAX_VALUE # 2147483647
13
12
 
14
- # The minimum number of threads that will be created in the pool
15
- # (unless overridden during construction).
13
+ # Default minimum number of threads that will be retained in the pool.
16
14
  DEFAULT_MIN_POOL_SIZE = 0
17
15
 
16
+ # Default maximum number of tasks that may be added to the task queue.
18
17
  DEFAULT_MAX_QUEUE_SIZE = 0
19
18
 
20
- # The maximum number of seconds a thread in the pool may remain idle before
21
- # being reclaimed (unless overridden during construction).
19
+ # Default maximum number of seconds a thread in the pool may remain idle
20
+ # before being reclaimed.
22
21
  DEFAULT_THREAD_IDLETIMEOUT = 60
23
22
 
23
+ # The set of possible overflow policies that may be set at thread pool creation.
24
24
  OVERFLOW_POLICIES = {
25
25
  abort: java.util.concurrent.ThreadPoolExecutor::AbortPolicy,
26
26
  discard: java.util.concurrent.ThreadPoolExecutor::DiscardPolicy,
@@ -30,12 +30,37 @@ if RUBY_PLATFORM == 'java'
30
30
  # The maximum number of threads that may be created in the pool.
31
31
  attr_reader :max_length
32
32
 
33
+ # The maximum number of tasks that may be waiting in the work queue at any one time.
34
+ # When the queue size reaches `max_queue` subsequent tasks will be rejected in
35
+ # accordance with the configured `overflow_policy`.
33
36
  attr_reader :max_queue
34
37
 
38
+ # The policy defining how rejected tasks (tasks received once the queue size reaches
39
+ # the configured `max_queue`) are handled. Must be one of the values specified in
40
+ # `OVERFLOW_POLICIES`.
35
41
  attr_reader :overflow_policy
36
42
 
37
43
  # Create a new thread pool.
38
44
  #
45
+ # @param [Hash] opts the options which configure the thread pool
46
+ #
47
+ # @option opts [Integer] :max_threads (DEFAULT_MAX_POOL_SIZE) the maximum
48
+ # number of threads to be created
49
+ # @option opts [Integer] :min_threads (DEFAULT_MIN_POOL_SIZE) the minimum
50
+ # number of threads to be retained
51
+ # @option opts [Integer] :idletime (DEFAULT_THREAD_IDLETIMEOUT) the maximum
52
+ # number of seconds a thread may be idle before being reclaimed
53
+ # @option opts [Integer] :max_queue (DEFAULT_MAX_QUEUE_SIZE) the maximum
54
+ # number of tasks allowed in the work queue at any one time; a value of
55
+ # zero means the queue may grow without bounnd
56
+ # @option opts [Symbol] :overflow_policy (:abort) the policy for handling new
57
+ # tasks that are received when the queue size has reached `max_queue`
58
+ #
59
+ # @raise [ArgumentError] if `:max_threads` is less than one
60
+ # @raise [ArgumentError] if `:min_threads` is less than zero
61
+ # @raise [ArgumentError] if `:overflow_policy` is not one of the values specified
62
+ # in `OVERFLOW_POLICIES`
63
+ #
39
64
  # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html
40
65
  def initialize(opts = {})
41
66
  min_length = opts.fetch(:min_threads, DEFAULT_MIN_POOL_SIZE).to_i
@@ -61,43 +86,70 @@ if RUBY_PLATFORM == 'java'
61
86
  idletime, java.util.concurrent.TimeUnit::SECONDS,
62
87
  queue, OVERFLOW_POLICIES[@overflow_policy].new)
63
88
 
64
- # without this the process may fail to exit
65
- at_exit { self.kill }
89
+ set_shutdown_hook
66
90
  end
67
91
 
92
+ # The minimum number of threads that may be retained in the pool.
93
+ #
94
+ # @return [Integer] the min_length
68
95
  def min_length
69
96
  @executor.getCorePoolSize
70
97
  end
71
98
 
99
+ # The maximum number of threads that may be created in the pool.
100
+ #
101
+ # @return [Integer] the max_length
72
102
  def max_length
73
103
  @executor.getMaximumPoolSize
74
104
  end
75
105
 
106
+ # The number of threads currently in the pool.
107
+ #
108
+ # @return [Integer] the length
76
109
  def length
77
110
  @executor.getPoolSize
78
111
  end
79
112
  alias_method :current_length, :length
80
113
 
114
+ # The largest number of threads that have been created in the pool since construction.
115
+ #
116
+ # @return [Integer] the largest_length
81
117
  def largest_length
82
118
  @executor.getLargestPoolSize
83
119
  end
84
120
 
121
+ # The number of tasks that have been scheduled for execution on the pool since construction.
122
+ #
123
+ # @return [Integer] the scheduled_task_count
85
124
  def scheduled_task_count
86
125
  @executor.getTaskCount
87
126
  end
88
127
 
128
+ # The number of tasks that have been completed by the pool since construction.
129
+ #
130
+ # @return [Integer] the completed_task_count
89
131
  def completed_task_count
90
132
  @executor.getCompletedTaskCount
91
133
  end
92
134
 
135
+ # The number of seconds that a thread may be idle before being reclaimed.
136
+ #
137
+ # @return [Integer] the idletime
93
138
  def idletime
94
139
  @executor.getKeepAliveTime(java.util.concurrent.TimeUnit::SECONDS)
95
140
  end
96
141
 
142
+ # The number of tasks in the queue awaiting execution.
143
+ #
144
+ # @return [Integer] the queue_length
97
145
  def queue_length
98
146
  @executor.getQueue.size
99
147
  end
100
148
 
149
+ # Number of tasks that may be enqueued before reaching `max_queue` and rejecting
150
+ # new tasks. A value of -1 indicates that the queue may grow without bound.
151
+ #
152
+ # @return [Integer] the remaining_capacity
101
153
  def remaining_capacity
102
154
  @max_queue == 0 ? -1 : @executor.getQueue.remainingCapacity
103
155
  end
@@ -113,81 +165,18 @@ if RUBY_PLATFORM == 'java'
113
165
 
114
166
  # Is the thread pool running?
115
167
  #
116
- # @return [Boolean] +true+ when running, +false+ when shutting down or shutdown
168
+ # @return [Boolean] `true` when running, `false` when shutting down or shutdown
117
169
  def running?
118
- ! (@executor.isShutdown || @executor.isTerminated || @executor.isTerminating)
119
- end
120
-
121
- # Is the thread pool shutdown?
122
- #
123
- # @return [Boolean] +true+ when shutdown, +false+ when shutting down or running
124
- def shutdown?
125
- @executor.isShutdown
126
- end
127
-
128
- # Block until thread pool shutdown is complete or until +timeout+ seconds have
129
- # passed.
130
- #
131
- # @note Does not initiate shutdown or termination. Either +shutdown+ or +kill+
132
- # must be called before this method (or on another thread).
133
- #
134
- # @param [Integer] timeout the maximum number of seconds to wait for shutdown to complete
135
- #
136
- # @return [Boolean] +true+ if shutdown complete or false on +timeout+
137
- def wait_for_termination(timeout)
138
- @executor.awaitTermination(timeout.to_i, java.util.concurrent.TimeUnit::SECONDS)
139
- end
140
-
141
- # Submit a task to the thread pool for asynchronous processing.
142
- #
143
- # @param [Array] args zero or more arguments to be passed to the task
144
- #
145
- # @yield the asynchronous task to perform
146
- #
147
- # @return [Boolean] +true+ if the task is queued, +false+ if the thread pool
148
- # is not running
149
- #
150
- # @raise [ArgumentError] if no task is given
151
- def post(*args)
152
- raise ArgumentError.new('no block given') unless block_given?
153
- if running?
154
- @executor.submit{ yield(*args) }
155
- true
156
- else
157
- false
158
- end
159
- rescue Java::JavaUtilConcurrent::RejectedExecutionException => ex
160
- raise RejectedExecutionError
161
- end
162
-
163
- # Submit a task to the thread pool for asynchronous processing.
164
- #
165
- # @param [Proc] task the asynchronous task to perform
166
- #
167
- # @return [self] returns itself
168
- def <<(task)
169
- @executor.submit(&task)
170
- rescue Java::JavaUtilConcurrent::RejectedExecutionException => ex
171
- raise RejectedExecutionError
170
+ super && ! @executor.isTerminating
172
171
  end
173
172
 
174
173
  # Begin an orderly shutdown. Tasks already in the queue will be executed,
175
174
  # but no new tasks will be accepted. Has no additional effect if the
176
175
  # thread pool is not running.
177
176
  def shutdown
178
- @executor.shutdown
179
- @executor.getQueue.clear
180
- return nil
181
- end
182
-
183
- # Begin an immediate shutdown. In-progress tasks will be allowed to
184
- # complete but enqueued tasks will be dismissed and no new tasks
185
- # will be accepted. Has no additional effect if the thread pool is
186
- # not running.
187
- def kill
188
- @executor.shutdownNow
177
+ super
189
178
  @executor.getQueue.clear
190
- return nil
179
+ nil
191
180
  end
192
181
  end
193
182
  end