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
@@ -1,19 +0,0 @@
1
- module Concurrent
2
- class Probe < IVar
3
-
4
- def initialize(value = NO_VALUE, opts = {})
5
- super(value, opts)
6
- end
7
-
8
- def set_unless_assigned(value)
9
- mutex.synchronize do
10
- return false if [:fulfilled, :rejected].include? @state
11
-
12
- set_state(true, value, nil)
13
- event.set
14
- true
15
- end
16
-
17
- end
18
- end
19
- end
@@ -1,60 +0,0 @@
1
- module Concurrent
2
-
3
- # A synchronization object that allows one thread to wait on multiple other threads.
4
- # The thread that will wait creates a +CountDownLatch+ and sets the initial value
5
- # (normally equal to the number of other threads). The initiating thread passes the
6
- # latch to the other threads then waits for the other threads by calling the +#wait+
7
- # method. Each of the other threads calls +#count_down+ when done with its work.
8
- # When the latch counter reaches zero the waiting thread is unblocked and continues
9
- # with its work. A +CountDownLatch+ can be used only once. Its value cannot be reset.
10
- class CountDownLatch
11
-
12
- # Create a new +CountDownLatch+ with the initial +count+.
13
- #
14
- # @param [Fixnum] count the initial count
15
- #
16
- # @raise [ArgumentError] if +count+ is not an integer or is less than zero
17
- def initialize(count)
18
- unless count.is_a?(Fixnum) && count >= 0
19
- raise ArgumentError.new('count must be in integer greater than or equal zero')
20
- end
21
- @mutex = Mutex.new
22
- @condition = Condition.new
23
- @count = count
24
- end
25
-
26
- # Block on the latch until the counter reaches zero or until +timeout+ is reached.
27
- #
28
- # @param [Fixnum] timeout the number of seconds to wait for the counter or +nil+
29
- # to block indefinitely
30
- # @return [Boolean] +true+ if the +count+ reaches zero else false on +timeout+
31
- def wait(timeout = nil)
32
- @mutex.synchronize do
33
-
34
- remaining = Condition::Result.new(timeout)
35
- while @count > 0 && remaining.can_wait?
36
- remaining = @condition.wait(@mutex, remaining.remaining_time)
37
- end
38
-
39
- @count == 0
40
- end
41
- end
42
-
43
- # Signal the latch to decrement the counter. Will signal all blocked threads when
44
- # the +count+ reaches zero.
45
- def count_down
46
- @mutex.synchronize do
47
- @count -= 1 if @count > 0
48
- @condition.broadcast if @count == 0
49
- end
50
- end
51
-
52
- # The current value of the counter.
53
- #
54
- # @return [Fixnum] the current value of the counter
55
- def count
56
- @mutex.synchronize { @count }
57
- end
58
-
59
- end
60
- end
@@ -1,80 +0,0 @@
1
- require 'thread'
2
- require 'concurrent/utilities'
3
- require 'concurrent/condition'
4
-
5
- module Concurrent
6
-
7
- # Old school kernel-style event reminiscent of Win32 programming in C++.
8
- #
9
- # When an +Event+ is created it is in the +unset+ state. Threads can choose to
10
- # +#wait+ on the event, blocking until released by another thread. When one
11
- # thread wants to alert all blocking threads it calls the +#set+ method which
12
- # will then wake up all listeners. Once an +Event+ has been set it remains set.
13
- # New threads calling +#wait+ will return immediately. An +Event+ may be
14
- # +#reset+ at any time once it has been set.
15
- #
16
- # @see http://msdn.microsoft.com/en-us/library/windows/desktop/ms682655.aspx
17
- class Event
18
-
19
- # Creates a new +Event+ in the unset state. Threads calling +#wait+ on the
20
- # +Event+ will block.
21
- def initialize
22
- @set = false
23
- @mutex = Mutex.new
24
- @condition = Condition.new
25
- end
26
-
27
- # Is the object in the set state?
28
- #
29
- # @return [Boolean] indicating whether or not the +Event+ has been set
30
- def set?
31
- @mutex.synchronize do
32
- @set
33
- end
34
- end
35
-
36
- # Trigger the event, setting the state to +set+ and releasing all threads
37
- # waiting on the event. Has no effect if the +Event+ has already been set.
38
- #
39
- # @return [Boolean] should always return +true+
40
- def set
41
- @mutex.synchronize do
42
- return true if @set
43
- @set = true
44
- @condition.broadcast
45
- end
46
-
47
- true
48
- end
49
-
50
- # Reset a previously set event back to the +unset+ state.
51
- # Has no effect if the +Event+ has not yet been set.
52
- #
53
- # @return [Boolean] should always return +true+
54
- def reset
55
- @mutex.synchronize do
56
- @set = false
57
- end
58
-
59
- true
60
- end
61
-
62
- # Wait a given number of seconds for the +Event+ to be set by another
63
- # thread. Will wait forever when no +timeout+ value is given. Returns
64
- # immediately if the +Event+ has already been set.
65
- #
66
- # @return [Boolean] true if the +Event+ was set before timeout else false
67
- def wait(timeout = nil)
68
- @mutex.synchronize do
69
- return true if @set
70
-
71
- remaining = Condition::Result.new(timeout)
72
- while !@set && remaining.can_wait?
73
- remaining = @condition.wait(@mutex, remaining.remaining_time)
74
- end
75
-
76
- @set
77
- end
78
- end
79
- end
80
- end
@@ -1,45 +0,0 @@
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
@@ -1,37 +0,0 @@
1
- require 'concurrent/ruby_thread_pool_executor'
2
-
3
- module Concurrent
4
-
5
- # @!macro cached_thread_pool
6
- class RubyCachedThreadPool < RubyThreadPoolExecutor
7
-
8
- # Create a new thread pool.
9
- #
10
- # @param [Hash] opts the options defining pool behavior.
11
- # @option opts [Integer] :max_threads (+DEFAULT_MAX_POOL_SIZE+) maximum number
12
- # of threads which may be created in the pool
13
- # @option opts [Integer] :idletime (+DEFAULT_THREAD_IDLETIMEOUT+) maximum
14
- # number of seconds a thread may be idle before it is reclaimed
15
- #
16
- # @raise [ArgumentError] if +max_threads+ is less than or equal to zero
17
- # @raise [ArgumentError] if +idletime+ is less than or equal to zero
18
- # @raise [ArgumentError] if +overflow_policy+ is not a known policy
19
- def initialize(opts = {})
20
- max_length = opts.fetch(:max_threads, DEFAULT_MAX_POOL_SIZE).to_i
21
- idletime = opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT).to_i
22
- overflow_policy = opts.fetch(:overflow_policy, :abort)
23
-
24
- raise ArgumentError.new('idletime must be greater than zero') if idletime <= 0
25
- raise ArgumentError.new('max_threads must be greater than zero') if max_length <= 0
26
- raise ArgumentError.new("#{overflow_policy} is not a valid overflow policy") unless OVERFLOW_POLICIES.include?(overflow_policy)
27
-
28
- opts = opts.merge(
29
- min_threads: 0,
30
- max_threads: max_length,
31
- num_threads: overflow_policy,
32
- idletime: idletime
33
- )
34
- super(opts)
35
- end
36
- end
37
- end
@@ -1,268 +0,0 @@
1
- require 'thread'
2
-
3
- require 'concurrent/event'
4
- require 'concurrent/ruby_thread_pool_worker'
5
-
6
- module Concurrent
7
-
8
- RejectedExecutionError = Class.new(StandardError) unless defined? RejectedExecutionError
9
-
10
- # @!macro thread_pool_executor
11
- class RubyThreadPoolExecutor
12
-
13
- # The maximum number of threads that will be created in the pool
14
- # (unless overridden during construction).
15
- DEFAULT_MAX_POOL_SIZE = 2**15 # 32768
16
-
17
- # The minimum number of threads that will be created in the pool
18
- # (unless overridden during construction).
19
- DEFAULT_MIN_POOL_SIZE = 0
20
-
21
- DEFAULT_MAX_QUEUE_SIZE = 0
22
-
23
- # The maximum number of seconds a thread in the pool may remain idle before
24
- # being reclaimed (unless overridden during construction).
25
- DEFAULT_THREAD_IDLETIMEOUT = 60
26
-
27
- OVERFLOW_POLICIES = [:abort, :discard, :caller_runs]
28
-
29
- # The maximum number of threads that may be created in the pool.
30
- attr_reader :max_length
31
- attr_reader :min_length
32
-
33
- attr_reader :largest_length
34
-
35
- attr_reader :scheduled_task_count
36
- attr_reader :completed_task_count
37
-
38
- attr_reader :idletime
39
-
40
- attr_reader :max_queue
41
-
42
- attr_reader :overflow_policy
43
-
44
- # Create a new thread pool.
45
- #
46
- # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html
47
- def initialize(opts = {})
48
- @min_length = opts.fetch(:min_threads, DEFAULT_MIN_POOL_SIZE).to_i
49
- @max_length = opts.fetch(:max_threads, DEFAULT_MAX_POOL_SIZE).to_i
50
- @idletime = opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT).to_i
51
- @max_queue = opts.fetch(:max_queue, DEFAULT_MAX_QUEUE_SIZE).to_i
52
- @overflow_policy = opts.fetch(:overflow_policy, :abort)
53
-
54
- raise ArgumentError.new('max_threads must be greater than zero') if @max_length <= 0
55
- raise ArgumentError.new('min_threads cannot be less than zero') if @min_length < 0
56
- raise ArgumentError.new("#{overflow_policy} is not a valid overflow policy") unless OVERFLOW_POLICIES.include?(@overflow_policy)
57
-
58
- @state = :running
59
- @pool = []
60
- @terminator = Event.new
61
- @queue = Queue.new
62
- @mutex = Mutex.new
63
- @scheduled_task_count = 0
64
- @completed_task_count = 0
65
- @largest_length = 0
66
-
67
- @gc_interval = opts.fetch(:gc_interval, 1).to_i # undocumented
68
- @last_gc_time = Time.now.to_f - [1.0, (@gc_interval * 2.0)].max
69
- end
70
-
71
- def length
72
- @mutex.synchronize do
73
- @state != :shutdown ? @pool.length : 0
74
- end
75
- end
76
- alias_method :current_length, :length
77
-
78
- def queue_length
79
- @queue.length
80
- end
81
-
82
- def remaining_capacity
83
- @mutex.synchronize { @max_queue == 0 ? -1 : @max_queue - @queue.length }
84
- end
85
-
86
- # Is the thread pool running?
87
- #
88
- # @return [Boolean] +true+ when running, +false+ when shutting down or shutdown
89
- def running?
90
- @mutex.synchronize { @state == :running }
91
- end
92
-
93
- # Returns an array with the status of each thread in the pool
94
- #
95
- # This method is deprecated and will be removed soon.
96
- def status
97
- warn '[DEPRECATED] `status` is deprecated and will be removed soon.'
98
- @mutex.synchronize { @pool.collect { |worker| worker.status } }
99
- end
100
-
101
- # Is the thread pool shutdown?
102
- #
103
- # @return [Boolean] +true+ when shutdown, +false+ when shutting down or running
104
- def shutdown?
105
- @mutex.synchronize { @state != :running }
106
- end
107
-
108
- # Block until thread pool shutdown is complete or until +timeout+ seconds have
109
- # passed.
110
- #
111
- # @note Does not initiate shutdown or termination. Either +shutdown+ or +kill+
112
- # must be called before this method (or on another thread).
113
- #
114
- # @param [Integer] timeout the maximum number of seconds to wait for shutdown to complete
115
- #
116
- # @return [Boolean] +true+ if shutdown complete or false on +timeout+
117
- def wait_for_termination(timeout)
118
- return @terminator.wait(timeout.to_i)
119
- end
120
-
121
- # Submit a task to the thread pool for asynchronous processing.
122
- #
123
- # @param [Array] args zero or more arguments to be passed to the task
124
- #
125
- # @yield the asynchronous task to perform
126
- #
127
- # @return [Boolean] +true+ if the task is queued, +false+ if the thread pool
128
- # is not running
129
- #
130
- # @raise [ArgumentError] if no task is given
131
- def post(*args, &task)
132
- raise ArgumentError.new('no block given') unless block_given?
133
- @mutex.synchronize do
134
- break false unless @state == :running
135
- return handle_overflow(*args, &task) if @max_queue != 0 && @queue.length >= @max_queue
136
- @scheduled_task_count += 1
137
- @queue << [args, task]
138
- if Time.now.to_f - @gc_interval >= @last_gc_time
139
- prune_pool
140
- @last_gc_time = Time.now.to_f
141
- end
142
- grow_pool
143
- true
144
- end
145
- end
146
-
147
- # Submit a task to the thread pool for asynchronous processing.
148
- #
149
- # @param [Proc] task the asynchronous task to perform
150
- #
151
- # @return [self] returns itself
152
- def <<(task)
153
- self.post(&task)
154
- return self
155
- end
156
-
157
- # Begin an orderly shutdown. Tasks already in the queue will be executed,
158
- # but no new tasks will be accepted. Has no additional effect if the
159
- # thread pool is not running.
160
- def shutdown
161
- @mutex.synchronize do
162
- break unless @state == :running
163
- @queue.clear
164
- if @pool.empty?
165
- @state = :shutdown
166
- @terminator.set
167
- else
168
- @state = :shuttingdown
169
- @pool.length.times{ @queue << :stop }
170
- end
171
- end
172
- end
173
-
174
- # Begin an immediate shutdown. In-progress tasks will be allowed to
175
- # complete but enqueued tasks will be dismissed and no new tasks
176
- # will be accepted. Has no additional effect if the thread pool is
177
- # not running.
178
- def kill
179
- @mutex.synchronize do
180
- break if @state == :shutdown
181
- @queue.clear
182
- @state = :shutdown
183
- drain_pool
184
- @terminator.set
185
- end
186
- end
187
-
188
- # @!visibility private
189
- def on_end_task # :nodoc:
190
- @mutex.synchronize do
191
- @completed_task_count += 1 #if success
192
- break unless @state == :running
193
- end
194
- end
195
-
196
- # @!visibility private
197
- def on_worker_exit(worker) # :nodoc:
198
- @mutex.synchronize do
199
- @pool.delete(worker)
200
- if @pool.empty? && @state != :running
201
- @state = :shutdown
202
- @terminator.set
203
- end
204
- end
205
- end
206
-
207
- protected
208
-
209
- # @!visibility private
210
- def handle_overflow(*args) # :nodoc:
211
- case @overflow_policy
212
- when :abort
213
- raise RejectedExecutionError
214
- when :discard
215
- false
216
- when :caller_runs
217
- begin
218
- yield(*args)
219
- rescue
220
- # let it fail
221
- end
222
- true
223
- end
224
- end
225
-
226
- # @!visibility private
227
- def prune_pool # :nodoc:
228
- @pool.delete_if do |worker|
229
- worker.dead? ||
230
- (@idletime == 0 ? false : Time.now.to_f - @idletime > worker.last_activity)
231
- end
232
- end
233
-
234
- # @!visibility private
235
- def grow_pool # :nodoc:
236
- if @min_length > @pool.length
237
- additional = @min_length - @pool.length
238
- elsif @pool.length < @max_length && ! @queue.empty?
239
- # NOTE: does not take into account idle threads
240
- additional = 1
241
- else
242
- additional = 0
243
- end
244
- additional.times do
245
- break if @pool.length >= @max_length
246
- @pool << create_worker_thread
247
- end
248
- @largest_length = [@largest_length, @pool.length].max
249
- end
250
-
251
- # @!visibility private
252
- def drain_pool # :nodoc:
253
- @pool.each {|worker| worker.kill }
254
- @pool.clear
255
- end
256
-
257
- # @!visibility private
258
- def create_worker_thread # :nodoc:
259
- wrkr = RubyThreadPoolWorker.new(@queue, self)
260
- Thread.new(wrkr, self) do |worker, parent|
261
- Thread.current.abort_on_exception = false
262
- worker.run
263
- parent.on_worker_exit(worker)
264
- end
265
- return wrkr
266
- end
267
- end
268
- end