concurrent-ruby 0.7.0.rc0-x86-linux → 0.7.0.rc1-x86-linux

Sign up to get free protection for your applications and to get access to all the features.
@@ -17,14 +17,22 @@ if RUBY_PLATFORM == 'java'
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 = {})
20
- @overflow_policy = opts.fetch(:overflow_policy, :abort)
21
- @max_queue = 0
22
20
 
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)
21
+ opts = {
22
+ min_threads: num_threads,
23
+ max_threads: num_threads
24
+ }.merge(opts)
25
+ super(opts)
25
26
 
26
- @executor = java.util.concurrent.Executors.newFixedThreadPool(num_threads)
27
- @executor.setRejectedExecutionHandler(OVERFLOW_POLICIES[@overflow_policy].new)
27
+
28
+ #@overflow_policy = opts.fetch(:overflow_policy, :abort)
29
+ #@max_queue = 0
30
+ #
31
+ #raise ArgumentError.new('number of threads must be greater than zero') if num_threads < 1
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.Executors.newFixedThreadPool(num_threads)
35
+ #@executor.setRejectedExecutionHandler(OVERFLOW_POLICIES[@overflow_policy].new)
28
36
 
29
37
  set_shutdown_hook
30
38
  end
@@ -6,6 +6,7 @@ if RUBY_PLATFORM == 'java'
6
6
  # @!macro single_thread_executor
7
7
  class JavaSingleThreadExecutor
8
8
  include JavaExecutor
9
+ include SerialExecutor
9
10
 
10
11
  # Create a new thread pool.
11
12
  #
@@ -71,6 +71,7 @@ if RUBY_PLATFORM == 'java'
71
71
 
72
72
  raise ArgumentError.new('max_threads must be greater than zero') if max_length <= 0
73
73
  raise ArgumentError.new('min_threads cannot be less than zero') if min_length < 0
74
+ raise ArgumentError.new('min_threads cannot be more than max_threads') if min_length > max_length
74
75
  raise ArgumentError.new("#{@overflow_policy} is not a valid overflow policy") unless OVERFLOW_POLICIES.keys.include?(@overflow_policy)
75
76
 
76
77
  if min_length == 0 && @max_queue == 0
@@ -89,6 +90,7 @@ if RUBY_PLATFORM == 'java'
89
90
  set_shutdown_hook
90
91
  end
91
92
 
93
+ # @!macro executor_module_method_can_overflow_question
92
94
  def can_overflow?
93
95
  @max_queue != 0
94
96
  end
@@ -171,16 +173,7 @@ if RUBY_PLATFORM == 'java'
171
173
  #
172
174
  # @return [Boolean] `true` when running, `false` when shutting down or shutdown
173
175
  def running?
174
- super && ! @executor.isTerminating
175
- end
176
-
177
- # Begin an orderly shutdown. Tasks already in the queue will be executed,
178
- # but no new tasks will be accepted. Has no additional effect if the
179
- # thread pool is not running.
180
- def shutdown
181
- super
182
- @executor.getQueue.clear
183
- nil
176
+ super && !@executor.isTerminating
184
177
  end
185
178
  end
186
179
  end
@@ -1,24 +1,100 @@
1
+ require 'concurrent/atomics'
2
+ require 'concurrent/executor/executor'
3
+
1
4
  module Concurrent
2
5
 
6
+ # An executor service in which every operation spawns a new,
7
+ # independently operating thread.
8
+ #
9
+ # This is perhaps the most inefficient executor service in this
10
+ # library. It exists mainly for testing an debugging. Thread creation
11
+ # and management is expensive in Ruby and this executor performs no
12
+ # resource pooling. This can be very beneficial during testing and
13
+ # debugging because it decouples the using code from the underlying
14
+ # executor implementation. In production this executor will likely
15
+ # lead to suboptimal performance.
16
+ #
17
+ # @note Intended for use primarily in testing and debugging.
3
18
  class PerThreadExecutor
4
19
  include Executor
5
20
 
21
+ # Creates a new executor
22
+ def initialize
23
+ @running = Concurrent::AtomicBoolean.new(true)
24
+ @stopped = Concurrent::Event.new
25
+ @count = Concurrent::AtomicFixnum.new(0)
26
+ end
27
+
28
+ # @!macro executor_method_post
6
29
  def self.post(*args)
7
30
  raise ArgumentError.new('no block given') unless block_given?
8
31
  Thread.new(*args) do
9
32
  Thread.current.abort_on_exception = false
10
33
  yield(*args)
11
34
  end
12
- return true
35
+ true
13
36
  end
14
37
 
38
+ # @!macro executor_method_left_shift
39
+ def self.<<(task)
40
+ post(&task)
41
+ self
42
+ end
43
+
44
+ # @!macro executor_method_post
15
45
  def post(*args, &task)
16
- return PerThreadExecutor.post(*args, &task)
46
+ raise ArgumentError.new('no block given') unless block_given?
47
+ return false unless running?
48
+ @count.increment
49
+ Thread.new(*args) do
50
+ Thread.current.abort_on_exception = false
51
+ begin
52
+ yield(*args)
53
+ ensure
54
+ @count.decrement
55
+ @stopped.set if @running.false? && @count.value == 0
56
+ end
57
+ end
17
58
  end
18
59
 
60
+ # @!macro executor_method_left_shift
19
61
  def <<(task)
20
- PerThreadExecutor.post(&task)
21
- return self
62
+ post(&task)
63
+ self
64
+ end
65
+
66
+ # @!macro executor_method_running_question
67
+ def running?
68
+ @running.true?
69
+ end
70
+
71
+ # @!macro executor_method_shuttingdown_question
72
+ def shuttingdown?
73
+ @running.false? && ! @stopped.set?
74
+ end
75
+
76
+ # @!macro executor_method_shutdown_question
77
+ def shutdown?
78
+ @stopped.set?
79
+ end
80
+
81
+ # @!macro executor_method_shutdown
82
+ def shutdown
83
+ @running.make_false
84
+ @stopped.set if @count.value == 0
85
+ true
86
+ end
87
+
88
+ # @!macro executor_method_kill
89
+ def kill
90
+ @running.make_false
91
+ @stopped.set
92
+ true
93
+ end
94
+
95
+ # @!macro executor_method_wait_for_termination
96
+ def wait_for_termination(timeout = nil)
97
+ @stopped.wait(timeout)
22
98
  end
23
99
  end
24
100
  end
@@ -19,7 +19,7 @@ module Concurrent
19
19
  opts = opts.merge(
20
20
  min_threads: 0,
21
21
  max_threads: DEFAULT_MAX_POOL_SIZE,
22
- num_threads: overflow_policy,
22
+ overflow_policy: overflow_policy,
23
23
  max_queue: DEFAULT_MAX_QUEUE_SIZE,
24
24
  idletime: DEFAULT_THREAD_IDLETIMEOUT
25
25
  )
@@ -19,13 +19,13 @@ module Concurrent
19
19
  raise ArgumentError.new('number of threads must be greater than zero') if num_threads < 1
20
20
  raise ArgumentError.new("#{overflow_policy} is not a valid overflow policy") unless OVERFLOW_POLICIES.include?(overflow_policy)
21
21
 
22
- opts = opts.merge(
22
+ opts = {
23
23
  min_threads: num_threads,
24
24
  max_threads: num_threads,
25
- num_threads: overflow_policy,
25
+ overflow_policy: overflow_policy,
26
26
  max_queue: DEFAULT_MAX_QUEUE_SIZE,
27
- idletime: 0
28
- )
27
+ idletime: DEFAULT_THREAD_IDLETIMEOUT,
28
+ }.merge(opts)
29
29
  super(opts)
30
30
  end
31
31
  end
@@ -5,6 +5,7 @@ module Concurrent
5
5
  # @!macro single_thread_executor
6
6
  class RubySingleThreadExecutor
7
7
  include RubyExecutor
8
+ include SerialExecutor
8
9
 
9
10
  # Create a new thread pool.
10
11
  #
@@ -86,6 +86,7 @@ module Concurrent
86
86
  raise ArgumentError.new('max_threads must be greater than zero') if @max_length <= 0
87
87
  raise ArgumentError.new('min_threads cannot be less than zero') if @min_length < 0
88
88
  raise ArgumentError.new("#{overflow_policy} is not a valid overflow policy") unless OVERFLOW_POLICIES.include?(@overflow_policy)
89
+ raise ArgumentError.new('min_threads cannot be more than max_threads') if min_length > max_length
89
90
 
90
91
  init_executor
91
92
 
@@ -99,6 +100,7 @@ module Concurrent
99
100
  @last_gc_time = Time.now.to_f - [1.0, (@gc_interval * 2.0)].max
100
101
  end
101
102
 
103
+ # @!macro executor_module_method_can_overflow_question
102
104
  def can_overflow?
103
105
  @max_queue != 0
104
106
  end
@@ -1,3 +1,5 @@
1
+ require 'delegate'
2
+ require 'concurrent/executor/executor'
1
3
  require 'concurrent/logging'
2
4
 
3
5
  module Concurrent
@@ -85,6 +87,27 @@ module Concurrent
85
87
 
86
88
  call_job job if job
87
89
  end
90
+ end
91
+
92
+ # A wrapper/delegator for any `Executor` or `ExecutorService` that
93
+ # guarantees serialized execution of tasks.
94
+ #
95
+ # @see [SimpleDelegator](http://www.ruby-doc.org/stdlib-2.1.2/libdoc/delegate/rdoc/SimpleDelegator.html)
96
+ # @see Concurrent::SerializedExecution
97
+ class SerializedExecutionDelegator < SimpleDelegator
98
+ include SerialExecutor
88
99
 
100
+ def initialize(executor)
101
+ @executor = executor
102
+ @serializer = SerializedExecution.new
103
+ super(executor)
104
+ end
105
+
106
+ # @!macro executor_method_post
107
+ def post(*args, &task)
108
+ raise ArgumentError.new('no block given') unless block_given?
109
+ return false unless running?
110
+ @serializer.post(@executor, *args, &task)
111
+ end
89
112
  end
90
113
  end
@@ -1,3 +1,3 @@
1
1
  module Concurrent
2
- VERSION = '0.7.0.rc0'
2
+ VERSION = '0.7.0.rc1'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: concurrent-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0.rc0
4
+ version: 0.7.0.rc1
5
5
  platform: x86-linux
6
6
  authors:
7
7
  - Jerry D'Antonio
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-06-18 00:00:00.000000000 Z
11
+ date: 2014-06-27 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: ! " Modern concurrency tools including agents, futures, promises,
14
14
  thread pools, actors, supervisors, and more.\n Inspired by Erlang, Clojure, Go,
@@ -49,7 +49,6 @@ files:
49
49
  - lib/concurrent/atomic/event.rb
50
50
  - lib/concurrent/atomic/thread_local_var.rb
51
51
  - lib/concurrent/atomic_reference/concurrent_update_error.rb
52
- - lib/concurrent/atomic_reference/delegated_update.rb
53
52
  - lib/concurrent/atomic_reference/direct_update.rb
54
53
  - lib/concurrent/atomic_reference/jruby.rb
55
54
  - lib/concurrent/atomic_reference/mutex_atomic.rb
@@ -101,7 +100,6 @@ files:
101
100
  - lib/concurrent/options_parser.rb
102
101
  - lib/concurrent/promise.rb
103
102
  - lib/concurrent/scheduled_task.rb
104
- - lib/concurrent/supervisor.rb
105
103
  - lib/concurrent/timer_task.rb
106
104
  - lib/concurrent/tvar.rb
107
105
  - lib/concurrent/utilities.rb
@@ -1,28 +0,0 @@
1
- require 'concurrent/atomic_reference/concurrent_update_error'
2
-
3
- module Concurrent
4
-
5
- # Define update methods that delegate to @ref field
6
- class Atomic
7
- # Pass the current value to the given block, replacing it
8
- # with the block's result. May retry if the value changes
9
- # during the block's execution.
10
- def update
11
- true until @ref.compare_and_set(old_value = @ref.get, new_value = yield(old_value))
12
- new_value
13
- end
14
-
15
- def try_update
16
- old_value = @ref.get
17
- new_value = yield old_value
18
- unless @ref.compare_and_set(old_value, new_value)
19
- if $VERBOSE
20
- raise ConcurrentUpdateError, "Update failed"
21
- else
22
- raise ConcurrentUpdateError, "Update failed", ConcurrentUpdateError::CONC_UP_ERR_BACKTRACE
23
- end
24
- end
25
- new_value
26
- end
27
- end
28
- end
@@ -1,343 +0,0 @@
1
- require 'thread'
2
-
3
- require 'concurrent/errors'
4
-
5
- module Concurrent
6
-
7
- class Supervisor
8
-
9
- DEFAULT_MONITOR_INTERVAL = 1
10
- RESTART_STRATEGIES = [:one_for_one, :one_for_all, :rest_for_one]
11
- DEFAULT_MAX_RESTART = 5
12
- DEFAULT_MAX_TIME = 60
13
- WORKER_API = {run: 0, stop: 0, running?: 0}
14
-
15
- CHILD_TYPES = [:worker, :supervisor]
16
- CHILD_RESTART_OPTIONS = [:permanent, :transient, :temporary]
17
-
18
- WorkerContext = Struct.new(:worker, :type, :restart) do
19
- attr_accessor :thread
20
- attr_accessor :terminated
21
-
22
- def alive?() return thread && thread.alive?; end
23
-
24
- def needs_restart?
25
- return false if thread && thread.alive?
26
- return false if terminated
27
- case self.restart
28
- when :permanent
29
- return true
30
- when :transient
31
- return thread.nil? || thread.status.nil?
32
- else #when :temporary
33
- return false
34
- end
35
- end
36
- end
37
-
38
- WorkerCounts = Struct.new(:specs, :supervisors, :workers) do
39
- attr_accessor :status
40
- def add(context)
41
- self.specs += 1
42
- self.supervisors += 1 if context.type == :supervisor
43
- self.workers += 1 if context.type == :worker
44
- end
45
- def active() sleeping + running + aborting end
46
- def sleeping() @status.reduce(0){|x, s| x += (s == 'sleep' ? 1 : 0) } end
47
- def running() @status.reduce(0){|x, s| x += (s == 'run' ? 1 : 0) } end
48
- def aborting() @status.reduce(0){|x, s| x += (s == 'aborting' ? 1 : 0) } end
49
- def stopped() @status.reduce(0){|x, s| x += (s == false ? 1 : 0) } end
50
- def abend() @status.reduce(0){|x, s| x += (s.nil? ? 1 : 0) } end
51
- end
52
-
53
- attr_reader :monitor_interval
54
- attr_reader :restart_strategy
55
- attr_reader :max_restart
56
- attr_reader :max_time
57
-
58
- alias_method :strategy, :restart_strategy
59
- alias_method :max_r, :max_restart
60
- alias_method :max_t, :max_time
61
-
62
- def initialize(opts = {})
63
- warn '[EXPERIMENTAL] Supervisor is being completely rewritten and will change soon.'
64
- @restart_strategy = opts[:restart_strategy] || opts[:strategy] || :one_for_one
65
- @monitor_interval = (opts[:monitor_interval] || DEFAULT_MONITOR_INTERVAL).to_f
66
- @max_restart = (opts[:max_restart] || opts[:max_r] || DEFAULT_MAX_RESTART).to_i
67
- @max_time = (opts[:max_time] || opts[:max_t] || DEFAULT_MAX_TIME).to_i
68
-
69
- raise ArgumentError.new(":#{@restart_strategy} is not a valid restart strategy") unless RESTART_STRATEGIES.include?(@restart_strategy)
70
- raise ArgumentError.new(':monitor_interval must be greater than zero') unless @monitor_interval > 0.0
71
- raise ArgumentError.new(':max_restart must be greater than zero') unless @max_restart > 0
72
- raise ArgumentError.new(':max_time must be greater than zero') unless @max_time > 0
73
-
74
- @running = false
75
- @mutex = Mutex.new
76
- @workers = []
77
- @monitor = nil
78
-
79
- @count = WorkerCounts.new(0, 0, 0)
80
- @restart_times = []
81
-
82
- add_worker(opts[:worker]) unless opts[:worker].nil?
83
- add_workers(opts[:workers]) unless opts[:workers].nil?
84
- end
85
-
86
- def run!
87
- @mutex.synchronize do
88
- raise StandardError.new('already running') if @running
89
- @running = true
90
- @monitor = Thread.new do
91
- Thread.current.abort_on_exception = false
92
- monitor
93
- end
94
- end
95
- Thread.pass
96
- end
97
-
98
- def run
99
- @mutex.synchronize do
100
- raise StandardError.new('already running') if @running
101
- @running = true
102
- end
103
- monitor
104
- true
105
- end
106
-
107
- def stop
108
- @mutex.synchronize do
109
- return true unless @running
110
-
111
- @running = false
112
- unless @monitor.nil?
113
- @monitor.run if @monitor.status == 'sleep'
114
- if @monitor.join(0.1).nil?
115
- @monitor.kill
116
- end
117
- @monitor = nil
118
- end
119
- @restart_times.clear
120
-
121
- @workers.length.times do |i|
122
- context = @workers[-1-i]
123
- terminate_worker(context)
124
- end
125
- prune_workers
126
- end
127
-
128
- true
129
- end
130
-
131
- def running?
132
- @mutex.synchronize { @running }
133
- end
134
-
135
- def length
136
- @mutex.synchronize { @workers.length }
137
- end
138
- alias_method :size, :length
139
-
140
- def current_restart_count
141
- @restart_times.length
142
- end
143
-
144
- def count
145
- @mutex.synchronize do
146
- @count.status = @workers.collect{|w| w.thread ? w.thread.status : false }
147
- @count.dup.freeze
148
- end
149
- end
150
-
151
- def add_worker(worker, opts = {})
152
- return nil if worker.nil? || ! behaves_as_worker?(worker)
153
- @mutex.synchronize {
154
- restart = opts[:restart] || :permanent
155
- type = opts[:type] || (worker.is_a?(Supervisor) ? :supervisor : nil) || :worker
156
- raise ArgumentError.new(":#{restart} is not a valid restart option") unless CHILD_RESTART_OPTIONS.include?(restart)
157
- raise ArgumentError.new(":#{type} is not a valid child type") unless CHILD_TYPES.include?(type)
158
- context = WorkerContext.new(worker, type, restart)
159
- @workers << context
160
- @count.add(context)
161
- worker.run if @running
162
- context.object_id
163
- }
164
- end
165
- alias_method :add_child, :add_worker
166
-
167
- def add_workers(workers, opts = {})
168
- workers.collect do |worker|
169
- add_worker(worker, opts)
170
- end
171
- end
172
- alias_method :add_children, :add_workers
173
-
174
- def remove_worker(worker_id)
175
- @mutex.synchronize do
176
- index, context = find_worker(worker_id)
177
- break(nil) if context.nil?
178
- break(false) if context.alive?
179
- @workers.delete_at(index)
180
- context.worker
181
- end
182
- end
183
- alias_method :remove_child, :remove_worker
184
-
185
- def stop_worker(worker_id)
186
- @mutex.synchronize do
187
- return true unless @running
188
-
189
- index, context = find_worker(worker_id)
190
- break(nil) if index.nil?
191
- context.terminated = true
192
- terminate_worker(context)
193
- @workers.delete_at(index) if @workers[index].restart == :temporary
194
- true
195
- end
196
- end
197
- alias_method :stop_child, :stop_worker
198
-
199
- def start_worker(worker_id)
200
- @mutex.synchronize do
201
- return false unless @running
202
-
203
- index, context = find_worker(worker_id)
204
- break(nil) if context.nil?
205
- context.terminated = false
206
- run_worker(context) unless context.alive?
207
- true
208
- end
209
- end
210
- alias_method :start_child, :start_worker
211
-
212
- def restart_worker(worker_id)
213
- @mutex.synchronize do
214
- return false unless @running
215
-
216
- index, context = find_worker(worker_id)
217
- break(nil) if context.nil?
218
- break(false) if context.restart == :temporary
219
- context.terminated = false
220
- terminate_worker(context)
221
- run_worker(context)
222
- true
223
- end
224
- end
225
- alias_method :restart_child, :restart_worker
226
-
227
- private
228
-
229
- def behaves_as_worker?(obj)
230
- WORKER_API.each do |method, arity|
231
- break(false) unless obj.respond_to?(method) && obj.method(method).arity == arity
232
- true
233
- end
234
- end
235
-
236
- def monitor
237
- @workers.each{|context| run_worker(context)}
238
- loop do
239
- sleep(@monitor_interval)
240
- break unless running?
241
- @mutex.synchronize do
242
- prune_workers
243
- self.send(@restart_strategy)
244
- end
245
- break unless running?
246
- end
247
- rescue MaxRestartFrequencyError => ex
248
- stop
249
- end
250
-
251
- def run_worker(context)
252
- context.thread = Thread.new do
253
- Thread.current.abort_on_exception = false
254
- context.worker.run
255
- end
256
- context
257
- end
258
-
259
- def terminate_worker(context)
260
- if context.alive?
261
- context.worker.stop
262
- Thread.pass
263
- end
264
- rescue Exception => ex
265
- begin
266
- Thread.kill(context.thread)
267
- rescue
268
- # suppress
269
- end
270
- ensure
271
- context.thread = nil
272
- end
273
-
274
- def prune_workers
275
- @workers.delete_if{|w| w.restart == :temporary && ! w.alive? }
276
- end
277
-
278
- def find_worker(worker_id)
279
- index = @workers.find_index{|worker| worker.object_id == worker_id}
280
- if index.nil?
281
- [nil, nil]
282
- else
283
- [index, @workers[index]]
284
- end
285
- end
286
-
287
- def exceeded_max_restart_frequency?
288
- @restart_times.unshift(Time.now.to_i)
289
- diff = (@restart_times.first - @restart_times.last).abs
290
- if @restart_times.length >= @max_restart && diff <= @max_time
291
- return true
292
- elsif diff >= @max_time
293
- @restart_times.pop
294
- end
295
- false
296
- end
297
-
298
- #----------------------------------------------------------------
299
- # restart strategies
300
-
301
- def one_for_one
302
- @workers.each do |context|
303
- if context.needs_restart?
304
- raise MaxRestartFrequencyError if exceeded_max_restart_frequency?
305
- run_worker(context)
306
- end
307
- end
308
- end
309
-
310
- def one_for_all
311
- restart = false
312
-
313
- restart = @workers.each do |context|
314
- if context.needs_restart?
315
- raise MaxRestartFrequencyError if exceeded_max_restart_frequency?
316
- break(true)
317
- end
318
- end
319
-
320
- if restart
321
- @workers.each do |context|
322
- terminate_worker(context)
323
- end
324
- @workers.each{|context| run_worker(context)}
325
- end
326
- end
327
-
328
- def rest_for_one
329
- restart = false
330
-
331
- @workers.each do |context|
332
- if restart
333
- terminate_worker(context)
334
- elsif context.needs_restart?
335
- raise MaxRestartFrequencyError if exceeded_max_restart_frequency?
336
- restart = true
337
- end
338
- end
339
-
340
- one_for_one if restart
341
- end
342
- end
343
- end