concurrent-ruby 0.5.0 → 0.6.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +88 -77
  3. data/lib/concurrent.rb +17 -2
  4. data/lib/concurrent/actor.rb +17 -0
  5. data/lib/concurrent/actor_context.rb +31 -0
  6. data/lib/concurrent/actor_ref.rb +39 -0
  7. data/lib/concurrent/agent.rb +12 -3
  8. data/lib/concurrent/async.rb +290 -0
  9. data/lib/concurrent/atomic.rb +5 -9
  10. data/lib/concurrent/cached_thread_pool.rb +39 -137
  11. data/lib/concurrent/channel/blocking_ring_buffer.rb +60 -0
  12. data/lib/concurrent/channel/buffered_channel.rb +83 -0
  13. data/lib/concurrent/channel/channel.rb +11 -0
  14. data/lib/concurrent/channel/probe.rb +19 -0
  15. data/lib/concurrent/channel/ring_buffer.rb +54 -0
  16. data/lib/concurrent/channel/unbuffered_channel.rb +34 -0
  17. data/lib/concurrent/channel/waitable_list.rb +38 -0
  18. data/lib/concurrent/configuration.rb +92 -0
  19. data/lib/concurrent/dataflow.rb +9 -3
  20. data/lib/concurrent/delay.rb +88 -0
  21. data/lib/concurrent/exchanger.rb +31 -0
  22. data/lib/concurrent/fixed_thread_pool.rb +28 -122
  23. data/lib/concurrent/future.rb +10 -5
  24. data/lib/concurrent/immediate_executor.rb +3 -2
  25. data/lib/concurrent/ivar.rb +2 -1
  26. data/lib/concurrent/java_cached_thread_pool.rb +45 -0
  27. data/lib/concurrent/java_fixed_thread_pool.rb +37 -0
  28. data/lib/concurrent/java_thread_pool_executor.rb +194 -0
  29. data/lib/concurrent/per_thread_executor.rb +23 -0
  30. data/lib/concurrent/postable.rb +2 -0
  31. data/lib/concurrent/processor_count.rb +125 -0
  32. data/lib/concurrent/promise.rb +42 -18
  33. data/lib/concurrent/ruby_cached_thread_pool.rb +37 -0
  34. data/lib/concurrent/ruby_fixed_thread_pool.rb +31 -0
  35. data/lib/concurrent/ruby_thread_pool_executor.rb +268 -0
  36. data/lib/concurrent/ruby_thread_pool_worker.rb +69 -0
  37. data/lib/concurrent/simple_actor_ref.rb +124 -0
  38. data/lib/concurrent/thread_local_var.rb +1 -1
  39. data/lib/concurrent/thread_pool_executor.rb +30 -0
  40. data/lib/concurrent/timer_task.rb +13 -10
  41. data/lib/concurrent/tvar.rb +212 -0
  42. data/lib/concurrent/utilities.rb +1 -0
  43. data/lib/concurrent/version.rb +1 -1
  44. data/spec/concurrent/actor_context_spec.rb +37 -0
  45. data/spec/concurrent/actor_ref_shared.rb +313 -0
  46. data/spec/concurrent/actor_spec.rb +9 -1
  47. data/spec/concurrent/agent_spec.rb +97 -96
  48. data/spec/concurrent/async_spec.rb +320 -0
  49. data/spec/concurrent/cached_thread_pool_shared.rb +137 -0
  50. data/spec/concurrent/channel/blocking_ring_buffer_spec.rb +149 -0
  51. data/spec/concurrent/channel/buffered_channel_spec.rb +151 -0
  52. data/spec/concurrent/channel/channel_spec.rb +37 -0
  53. data/spec/concurrent/channel/probe_spec.rb +49 -0
  54. data/spec/concurrent/channel/ring_buffer_spec.rb +126 -0
  55. data/spec/concurrent/channel/unbuffered_channel_spec.rb +132 -0
  56. data/spec/concurrent/configuration_spec.rb +134 -0
  57. data/spec/concurrent/dataflow_spec.rb +109 -27
  58. data/spec/concurrent/delay_spec.rb +77 -0
  59. data/spec/concurrent/exchanger_spec.rb +66 -0
  60. data/spec/concurrent/fixed_thread_pool_shared.rb +136 -0
  61. data/spec/concurrent/future_spec.rb +60 -51
  62. data/spec/concurrent/global_thread_pool_shared.rb +33 -0
  63. data/spec/concurrent/immediate_executor_spec.rb +4 -25
  64. data/spec/concurrent/ivar_spec.rb +36 -23
  65. data/spec/concurrent/java_cached_thread_pool_spec.rb +64 -0
  66. data/spec/concurrent/java_fixed_thread_pool_spec.rb +64 -0
  67. data/spec/concurrent/java_thread_pool_executor_spec.rb +71 -0
  68. data/spec/concurrent/obligation_shared.rb +32 -20
  69. data/spec/concurrent/{global_thread_pool_spec.rb → per_thread_executor_spec.rb} +9 -13
  70. data/spec/concurrent/processor_count_spec.rb +20 -0
  71. data/spec/concurrent/promise_spec.rb +29 -41
  72. data/spec/concurrent/ruby_cached_thread_pool_spec.rb +69 -0
  73. data/spec/concurrent/ruby_fixed_thread_pool_spec.rb +39 -0
  74. data/spec/concurrent/ruby_thread_pool_executor_spec.rb +183 -0
  75. data/spec/concurrent/simple_actor_ref_spec.rb +219 -0
  76. data/spec/concurrent/thread_pool_class_cast_spec.rb +40 -0
  77. data/spec/concurrent/thread_pool_executor_shared.rb +155 -0
  78. data/spec/concurrent/thread_pool_shared.rb +98 -36
  79. data/spec/concurrent/tvar_spec.rb +137 -0
  80. data/spec/spec_helper.rb +4 -0
  81. data/spec/support/functions.rb +4 -0
  82. metadata +85 -20
  83. data/lib/concurrent/cached_thread_pool/worker.rb +0 -91
  84. data/lib/concurrent/channel.rb +0 -63
  85. data/lib/concurrent/fixed_thread_pool/worker.rb +0 -54
  86. data/lib/concurrent/global_thread_pool.rb +0 -42
  87. data/spec/concurrent/cached_thread_pool_spec.rb +0 -101
  88. data/spec/concurrent/channel_spec.rb +0 -86
  89. data/spec/concurrent/fixed_thread_pool_spec.rb +0 -92
  90. data/spec/concurrent/uses_global_thread_pool_shared.rb +0 -64
@@ -0,0 +1,31 @@
1
+ module Concurrent
2
+ class Exchanger
3
+
4
+ EMPTY = Object.new
5
+
6
+ def initialize(opts = {})
7
+ @first = MVar.new(EMPTY, opts)
8
+ @second = MVar.new(MVar::EMPTY, opts)
9
+ end
10
+
11
+ def exchange(value, timeout = nil)
12
+ first = @first.take(timeout)
13
+ if first == MVar::TIMEOUT
14
+ nil
15
+ elsif first == EMPTY
16
+ @first.put value
17
+ second = @second.take timeout
18
+ if second == MVar::TIMEOUT
19
+ nil
20
+ else
21
+ second
22
+ end
23
+ else
24
+ @first.put EMPTY
25
+ @second.put value
26
+ first
27
+ end
28
+ end
29
+
30
+ end
31
+ end
@@ -1,128 +1,34 @@
1
- require 'thread'
2
-
3
- require 'concurrent/event'
4
- require 'concurrent/fixed_thread_pool/worker'
1
+ require 'concurrent/ruby_fixed_thread_pool'
5
2
 
6
3
  module Concurrent
7
4
 
8
- class FixedThreadPool
9
-
10
- MIN_POOL_SIZE = 1
11
- MAX_POOL_SIZE = 256
12
-
13
- attr_accessor :max_threads
14
-
15
- def initialize(size, opts = {})
16
- @max_threads = size || MAX_POOL_SIZE
17
- if @max_threads < MIN_POOL_SIZE || @max_threads > MAX_POOL_SIZE
18
- raise ArgumentError.new("size must be from #{MIN_POOL_SIZE} to #{MAX_POOL_SIZE}")
19
- end
20
-
21
- @state = :running
22
- @pool = []
23
- @terminator = Event.new
24
- @queue = Queue.new
25
- @mutex = Mutex.new
26
- end
27
-
28
- def running?
29
- return @state == :running
30
- end
31
-
32
- def wait_for_termination(timeout = nil)
33
- return @terminator.wait(timeout)
34
- end
35
-
36
- def post(*args, &block)
37
- raise ArgumentError.new('no block given') if block.nil?
38
- @mutex.synchronize do
39
- break false unless @state == :running
40
- @queue << [args, block]
41
- clean_pool
42
- fill_pool
43
- true
44
- end
45
- end
46
-
47
- def <<(block)
48
- self.post(&block)
49
- return self
50
- end
51
-
52
- def shutdown
53
- @mutex.synchronize do
54
- break unless @state == :running
55
- if @pool.empty?
56
- @state = :shutdown
57
- @terminator.set
58
- else
59
- @state = :shuttingdown
60
- @pool.length.times{ @queue << :stop }
61
- end
62
- end
63
- end
64
-
65
- def kill
66
- @mutex.synchronize do
67
- break if @state == :shutdown
68
- @state = :shutdown
69
- @queue.clear
70
- drain_pool
71
- @terminator.set
72
- end
73
- end
74
-
75
- def length
76
- @mutex.synchronize do
77
- @state == :running ? @pool.length : 0
78
- end
79
- end
80
-
81
- def create_worker_thread
82
- wrkr = Worker.new(@queue, self)
83
- Thread.new(wrkr, self) do |worker, parent|
84
- Thread.current.abort_on_exception = false
85
- worker.run
86
- parent.on_worker_exit(worker)
87
- end
88
- return wrkr
89
- end
90
-
91
- def fill_pool
92
- return unless @state == :running
93
- while @pool.length < @max_threads
94
- @pool << create_worker_thread
95
- end
96
- end
97
-
98
- def clean_pool
99
- @pool.reject! {|worker| worker.dead? }
100
- end
101
-
102
- def drain_pool
103
- @pool.each {|worker| worker.kill }
104
- @pool.clear
105
- end
106
-
107
- def on_start_task(worker)
108
- end
109
-
110
- def on_end_task(worker)
111
- @mutex.synchronize do
112
- break unless @state == :running
113
- clean_pool
114
- fill_pool
115
- end
116
- end
117
-
118
- def on_worker_exit(worker)
119
- @mutex.synchronize do
120
- @pool.delete(worker)
121
- if @pool.empty? && @state != :running
122
- @state = :shutdown
123
- @terminator.set
124
- end
125
- end
5
+ if RUBY_PLATFORM == 'java'
6
+ require 'concurrent/java_fixed_thread_pool'
7
+ # @!macro [attach] fixed_thread_pool
8
+ #
9
+ # A thread pool with a set number of threads. The number of threads in the pool
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.
12
+ # Should a thread crash for any reason the thread will immediately be removed
13
+ # from the pool and replaced.
14
+ #
15
+ # The API and behavior of this class are based on Java's +FixedThreadPool+
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+.
19
+ #
20
+ # @see Concurrent::RubyFixedThreadPool
21
+ # @see Concurrent::JavaFixedThreadPool
22
+ #
23
+ # @see http://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html
24
+ # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html
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
+ class FixedThreadPool < JavaFixedThreadPool
28
+ end
29
+ else
30
+ # @!macro fixed_thread_pool
31
+ class FixedThreadPool < RubyFixedThreadPool
126
32
  end
127
33
  end
128
34
  end
@@ -1,7 +1,7 @@
1
1
  require 'thread'
2
2
 
3
+ require 'concurrent/configuration'
3
4
  require 'concurrent/obligation'
4
- require 'concurrent/global_thread_pool'
5
5
  require 'concurrent/safe_task_executor'
6
6
 
7
7
  module Concurrent
@@ -41,13 +41,18 @@ module Concurrent
41
41
  # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html java.util.concurrent.Future
42
42
  class Future < IVar
43
43
  include Obligation
44
- include UsesGlobalThreadPool
44
+ include OptionsParser
45
45
 
46
46
  # Create a new +Future+ in the +:unscheduled+ state.
47
47
  #
48
48
  # @yield the asynchronous operation to perform
49
49
  #
50
50
  # @param [Hash] opts the options to create a message with
51
+ # @option opts [Boolean] :operation (false) when +true+ will execute the future on the global
52
+ # operation pool (for long-running operations), when +false+ will execute the future on the
53
+ # global task pool (for short-running tasks)
54
+ # @option opts [object] :executor when provided will run all operations on
55
+ # this executor rather than the global thread pool (overrides :operation)
51
56
  # @option opts [String] :dup_on_deref (false) call +#dup+ before returning the data
52
57
  # @option opts [String] :freeze_on_deref (false) call +#freeze+ before returning the data
53
58
  # @option opts [String] :copy_on_deref (nil) call the given +Proc+ passing the internal value and
@@ -59,6 +64,7 @@ module Concurrent
59
64
  super(IVar::NO_VALUE, opts)
60
65
  @state = :unscheduled
61
66
  @task = block
67
+ @executor = get_executor_from(opts)
62
68
  end
63
69
 
64
70
  # Execute an +:unscheduled+ +Future+. Immediately sets the state to +:pending+ and
@@ -80,7 +86,7 @@ module Concurrent
80
86
  # @since 0.5.0
81
87
  def execute
82
88
  if compare_and_set_state(:pending, :unscheduled)
83
- Future.thread_pool.post { work }
89
+ @executor.post{ work }
84
90
  self
85
91
  end
86
92
  end
@@ -105,7 +111,7 @@ module Concurrent
105
111
  #
106
112
  # @since 0.5.0
107
113
  def self.execute(opts = {}, &block)
108
- return Future.new(opts, &block).execute
114
+ Future.new(opts, &block).execute
109
115
  end
110
116
 
111
117
  protected :set, :fail, :complete
@@ -117,6 +123,5 @@ module Concurrent
117
123
  success, val, reason = SafeTaskExecutor.new(@task).execute
118
124
  complete(success, val, reason)
119
125
  end
120
-
121
126
  end
122
127
  end
@@ -2,13 +2,14 @@ module Concurrent
2
2
  class ImmediateExecutor
3
3
 
4
4
  def post(*args, &block)
5
+ raise ArgumentError.new('no block given') unless block_given?
5
6
  block.call(*args)
7
+ return true
6
8
  end
7
9
 
8
10
  def <<(block)
9
11
  post(&block)
10
12
  self
11
13
  end
12
-
13
14
  end
14
- end
15
+ end
@@ -60,7 +60,7 @@ module Concurrent
60
60
  complete(true, value, nil)
61
61
  end
62
62
 
63
- def fail(reason = nil)
63
+ def fail(reason = StandardError.new)
64
64
  complete(false, nil, reason)
65
65
  end
66
66
 
@@ -73,6 +73,7 @@ module Concurrent
73
73
 
74
74
  time = Time.now
75
75
  @observers.notify_and_delete_observers{ [time, self.value, reason] }
76
+ self
76
77
  end
77
78
  end
78
79
  end
@@ -0,0 +1,45 @@
1
+ if RUBY_PLATFORM == 'java'
2
+
3
+ require 'concurrent/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 [Integer] :max_threads (+DEFAULT_MAX_POOL_SIZE+) maximum number
14
+ # of threads which may be created in the pool
15
+ # @option opts [Integer] :idletime (+DEFAULT_THREAD_IDLETIMEOUT+) maximum
16
+ # number of seconds a thread may be idle before it is reclaimed
17
+ # @option opts [Symbol] :overflow_policy (+:abort+) the overflow policy
18
+ #
19
+ # @raise [ArgumentError] if +max_threads+ is less than or equal to zero
20
+ # @raise [ArgumentError] if +idletime+ is less than or equal to zero
21
+ # @raise [ArgumentError] if +overflow_policy+ is not a known policy
22
+ #
23
+ # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors.html#newCachedThreadPool--
24
+ def initialize(opts = {})
25
+ max_length = opts.fetch(:max_threads, DEFAULT_MAX_POOL_SIZE).to_i
26
+ idletime = opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT).to_i
27
+ @overflow_policy = opts.fetch(:overflow_policy, :abort)
28
+ @max_queue = 0
29
+
30
+ raise ArgumentError.new('idletime must be greater than zero') if idletime <= 0
31
+ raise ArgumentError.new('max_threads must be greater than zero') if max_length <= 0
32
+ raise ArgumentError.new("#{@overflow_policy} is not a valid overflow policy") unless OVERFLOW_POLICIES.keys.include?(@overflow_policy)
33
+
34
+ @executor = java.util.concurrent.ThreadPoolExecutor.new(
35
+ @max_queue, max_length,
36
+ idletime, java.util.concurrent.TimeUnit::SECONDS,
37
+ java.util.concurrent.SynchronousQueue.new,
38
+ OVERFLOW_POLICIES[@overflow_policy].new)
39
+
40
+ # without this the process may fail to exit
41
+ at_exit { self.kill }
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,37 @@
1
+ if RUBY_PLATFORM == 'java'
2
+
3
+ require 'concurrent/java_thread_pool_executor'
4
+
5
+ module Concurrent
6
+
7
+ # @!macro fixed_thread_pool
8
+ class JavaFixedThreadPool < 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 +num_threads+ is less than or equal to zero
16
+ # @raise [ArgumentError] if +overflow_policy+ is not a known policy
17
+ #
18
+ # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors.html#newFixedThreadPool-int-
19
+ def initialize(num_threads, opts = {})
20
+ @overflow_policy = opts.fetch(:overflow_policy, :abort)
21
+ @max_queue = 0
22
+
23
+ raise ArgumentError.new('number of threads must be greater than zero') if num_threads < 1
24
+ raise ArgumentError.new("#{@overflow_policy} is not a valid overflow policy") unless OVERFLOW_POLICIES.keys.include?(@overflow_policy)
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)
31
+
32
+ # without this the process may fail to exit
33
+ at_exit { self.kill }
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,194 @@
1
+ if RUBY_PLATFORM == 'java'
2
+
3
+ module Concurrent
4
+
5
+ RejectedExecutionError = Class.new(StandardError) unless defined? RejectedExecutionError
6
+
7
+ # @!macro thread_pool_executor
8
+ class JavaThreadPoolExecutor
9
+
10
+ # The maximum number of threads that will be created in the pool
11
+ # (unless overridden during construction).
12
+ DEFAULT_MAX_POOL_SIZE = java.lang.Integer::MAX_VALUE # 2147483647
13
+
14
+ # The minimum number of threads that will be created in the pool
15
+ # (unless overridden during construction).
16
+ DEFAULT_MIN_POOL_SIZE = 0
17
+
18
+ DEFAULT_MAX_QUEUE_SIZE = 0
19
+
20
+ # The maximum number of seconds a thread in the pool may remain idle before
21
+ # being reclaimed (unless overridden during construction).
22
+ DEFAULT_THREAD_IDLETIMEOUT = 60
23
+
24
+ OVERFLOW_POLICIES = {
25
+ abort: java.util.concurrent.ThreadPoolExecutor::AbortPolicy,
26
+ discard: java.util.concurrent.ThreadPoolExecutor::DiscardPolicy,
27
+ caller_runs: java.util.concurrent.ThreadPoolExecutor::CallerRunsPolicy
28
+ }.freeze
29
+
30
+ # The maximum number of threads that may be created in the pool.
31
+ attr_reader :max_length
32
+
33
+ attr_reader :max_queue
34
+
35
+ attr_reader :overflow_policy
36
+
37
+ # Create a new thread pool.
38
+ #
39
+ # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html
40
+ def initialize(opts = {})
41
+ min_length = opts.fetch(:min_threads, DEFAULT_MIN_POOL_SIZE).to_i
42
+ max_length = opts.fetch(:max_threads, DEFAULT_MAX_POOL_SIZE).to_i
43
+ idletime = opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT).to_i
44
+ @max_queue = opts.fetch(:max_queue, DEFAULT_MAX_QUEUE_SIZE).to_i
45
+ @overflow_policy = opts.fetch(:overflow_policy, :abort)
46
+
47
+ raise ArgumentError.new('max_threads must be greater than zero') if max_length <= 0
48
+ raise ArgumentError.new('min_threads cannot be less than zero') if min_length < 0
49
+ raise ArgumentError.new("#{@overflow_policy} is not a valid overflow policy") unless OVERFLOW_POLICIES.keys.include?(@overflow_policy)
50
+
51
+ if min_length == 0 && @max_queue == 0
52
+ queue = java.util.concurrent.SynchronousQueue.new
53
+ elsif @max_queue == 0
54
+ queue = java.util.concurrent.LinkedBlockingQueue.new
55
+ else
56
+ queue = java.util.concurrent.LinkedBlockingQueue.new(@max_queue)
57
+ end
58
+
59
+ @executor = java.util.concurrent.ThreadPoolExecutor.new(
60
+ min_length, max_length,
61
+ idletime, java.util.concurrent.TimeUnit::SECONDS,
62
+ queue, OVERFLOW_POLICIES[@overflow_policy].new)
63
+
64
+ # without this the process may fail to exit
65
+ at_exit { self.kill }
66
+ end
67
+
68
+ def min_length
69
+ @executor.getCorePoolSize
70
+ end
71
+
72
+ def max_length
73
+ @executor.getMaximumPoolSize
74
+ end
75
+
76
+ def length
77
+ @executor.getPoolSize
78
+ end
79
+ alias_method :current_length, :length
80
+
81
+ def largest_length
82
+ @executor.getLargestPoolSize
83
+ end
84
+
85
+ def scheduled_task_count
86
+ @executor.getTaskCount
87
+ end
88
+
89
+ def completed_task_count
90
+ @executor.getCompletedTaskCount
91
+ end
92
+
93
+ def idletime
94
+ @executor.getKeepAliveTime(java.util.concurrent.TimeUnit::SECONDS)
95
+ end
96
+
97
+ def queue_length
98
+ @executor.getQueue.size
99
+ end
100
+
101
+ def remaining_capacity
102
+ @max_queue == 0 ? -1 : @executor.getQueue.remainingCapacity
103
+ end
104
+
105
+ # This method is deprecated and will be removed soon.
106
+ # This method is supost to return the threads status, but Java API doesn't
107
+ # provide a way to get the thread status. So we return an empty Array instead.
108
+ def status
109
+ warn '[DEPRECATED] `status` is deprecated and will be removed soon.'
110
+ warn "Calls to `status` return an empty Array. Java ThreadPoolExecutor does not provide thread's status."
111
+ []
112
+ end
113
+
114
+ # Is the thread pool running?
115
+ #
116
+ # @return [Boolean] +true+ when running, +false+ when shutting down or shutdown
117
+ 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
172
+ end
173
+
174
+ # Begin an orderly shutdown. Tasks already in the queue will be executed,
175
+ # but no new tasks will be accepted. Has no additional effect if the
176
+ # thread pool is not running.
177
+ 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
189
+ @executor.getQueue.clear
190
+ return nil
191
+ end
192
+ end
193
+ end
194
+ end