concurrent-ruby 0.7.0.rc2-java → 0.7.1-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/CHANGELOG.md +138 -0
  2. data/README.md +108 -95
  3. data/lib/concurrent/actor.rb +12 -13
  4. data/lib/concurrent/actor/behaviour/errors_on_unknown_message.rb +1 -1
  5. data/lib/concurrent/actor/behaviour/executes_context.rb +1 -1
  6. data/lib/concurrent/actor/behaviour/linking.rb +4 -1
  7. data/lib/concurrent/actor/behaviour/pausing.rb +2 -2
  8. data/lib/concurrent/actor/behaviour/supervised.rb +3 -2
  9. data/lib/concurrent/actor/behaviour/terminates_children.rb +1 -1
  10. data/lib/concurrent/actor/behaviour/termination.rb +1 -1
  11. data/lib/concurrent/actor/context.rb +2 -1
  12. data/lib/concurrent/actor/core.rb +8 -4
  13. data/lib/concurrent/actor/utils.rb +10 -0
  14. data/lib/concurrent/actor/utils/ad_hoc.rb +21 -0
  15. data/lib/concurrent/actor/utils/balancer.rb +42 -0
  16. data/lib/concurrent/actor/utils/broadcast.rb +22 -6
  17. data/lib/concurrent/actor/utils/pool.rb +59 -0
  18. data/lib/concurrent/agent.rb +1 -22
  19. data/lib/concurrent/async.rb +1 -79
  20. data/lib/concurrent/atomic.rb +20 -26
  21. data/lib/concurrent/atomic/atomic_boolean.rb +4 -1
  22. data/lib/concurrent/atomic/atomic_fixnum.rb +4 -1
  23. data/lib/concurrent/atomic/thread_local_var.rb +71 -24
  24. data/lib/concurrent/atomic_reference/jruby.rb +10 -6
  25. data/lib/concurrent/atomic_reference/ruby.rb +14 -10
  26. data/lib/concurrent/atomics.rb +0 -1
  27. data/lib/concurrent/configuration.rb +11 -5
  28. data/lib/concurrent/dataflow.rb +1 -30
  29. data/lib/concurrent/dereferenceable.rb +9 -2
  30. data/lib/concurrent/executor/indirect_immediate_executor.rb +46 -0
  31. data/lib/concurrent/executor/java_thread_pool_executor.rb +2 -4
  32. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +24 -22
  33. data/lib/concurrent/executor/serialized_execution.rb +36 -23
  34. data/lib/concurrent/executor/thread_pool_executor.rb +2 -0
  35. data/lib/concurrent/executor/timer_set.rb +7 -8
  36. data/lib/concurrent/executors.rb +1 -0
  37. data/lib/concurrent/future.rb +7 -29
  38. data/lib/concurrent/ivar.rb +9 -0
  39. data/lib/concurrent/logging.rb +3 -0
  40. data/lib/concurrent/mvar.rb +26 -9
  41. data/lib/concurrent/observable.rb +33 -0
  42. data/lib/concurrent/promise.rb +59 -1
  43. data/lib/concurrent/scheduled_task.rb +1 -0
  44. data/lib/concurrent/timer_task.rb +18 -18
  45. data/lib/concurrent/tvar.rb +3 -1
  46. data/lib/concurrent/version.rb +1 -1
  47. data/lib/concurrent_ruby_ext.jar +0 -0
  48. data/lib/concurrent_ruby_ext.so +0 -0
  49. data/lib/extension_helper.rb +25 -6
  50. metadata +15 -7
  51. data/lib/concurrent/actor/ad_hoc.rb +0 -19
  52. data/lib/concurrent/actor/utills.rb +0 -7
@@ -1,3 +1,6 @@
1
+ require_relative '../../extension_helper'
2
+ Concurrent.safe_require_c_extensions
3
+
1
4
  module Concurrent
2
5
 
3
6
  # @!macro [attach] atomic_boolean
@@ -159,7 +162,7 @@ module Concurrent
159
162
  class AtomicBoolean < JavaAtomicBoolean
160
163
  end
161
164
 
162
- elsif defined? Concurrent::CAtomicBoolean
165
+ elsif Concurrent.allow_c_native_class?('CAtomicBoolean')
163
166
 
164
167
  # @!macro atomic_boolean
165
168
  class CAtomicBoolean
@@ -1,3 +1,6 @@
1
+ require_relative '../../extension_helper'
2
+ Concurrent.safe_require_c_extensions
3
+
1
4
  module Concurrent
2
5
 
3
6
  # @!macro [attach] atomic_fixnum
@@ -163,7 +166,7 @@ module Concurrent
163
166
  class AtomicFixnum < JavaAtomicFixnum
164
167
  end
165
168
 
166
- elsif defined? Concurrent::CAtomicFixnum
169
+ elsif Concurrent.allow_c_native_class?('CAtomicFixnum')
167
170
 
168
171
  # @!macro atomic_fixnum
169
172
  class CAtomicFixnum
@@ -2,41 +2,83 @@ require 'concurrent/atomic'
2
2
 
3
3
  module Concurrent
4
4
 
5
- module ThreadLocalRubyStorage
5
+ # @!macro [attach] abstract_thread_local_var
6
+ # A `ThreadLocalVar` is a variable where the value is different for each thread.
7
+ # Each variable may have a default value, but when you modify the variable only
8
+ # the current thread will ever see that change.
9
+ #
10
+ # @example
11
+ # v = ThreadLocalVar.new(14)
12
+ # v.value #=> 14
13
+ # v.value = 2
14
+ # v.value #=> 2
15
+ #
16
+ # @example
17
+ # v = ThreadLocalVar.new(14)
18
+ #
19
+ # t1 = Thread.new do
20
+ # v.value #=> 14
21
+ # v.value = 1
22
+ # v.value #=> 1
23
+ # end
24
+ #
25
+ # t2 = Thread.new do
26
+ # v.value #=> 14
27
+ # v.value = 2
28
+ # v.value #=> 2
29
+ # end
30
+ #
31
+ # v.value #=> 14
32
+ class AbstractThreadLocalVar
6
33
 
7
- def allocate_storage
8
- @storage = Atomic.new Hash.new
9
- end
34
+ module ThreadLocalRubyStorage
10
35
 
11
- def get
12
- @storage.get[Thread.current]
13
- end
36
+ protected
14
37
 
15
- def set(value)
16
- @storage.update { |s| s.merge Thread.current => value }
17
- end
38
+ unless RUBY_PLATFORM == 'java'
39
+ require 'ref'
40
+ end
18
41
 
19
- end
42
+ def allocate_storage
43
+ @storage = Ref::WeakKeyMap.new
44
+ end
20
45
 
21
- module ThreadLocalJavaStorage
46
+ def get
47
+ @storage[Thread.current]
48
+ end
22
49
 
23
- protected
50
+ def set(value, &block)
51
+ key = Thread.current
24
52
 
25
- def allocate_storage
26
- @var = java.lang.ThreadLocal.new
27
- end
53
+ @storage[key] = value
28
54
 
29
- def get
30
- @var.get
55
+ if block_given?
56
+ begin
57
+ block.call
58
+ ensure
59
+ @storage.delete key
60
+ end
61
+ end
62
+ end
31
63
  end
32
64
 
33
- def set(value)
34
- @var.set(value)
35
- end
65
+ module ThreadLocalJavaStorage
36
66
 
37
- end
67
+ protected
38
68
 
39
- class AbstractThreadLocalVar
69
+ def allocate_storage
70
+ @var = java.lang.ThreadLocal.new
71
+ end
72
+
73
+ def get
74
+ @var.get
75
+ end
76
+
77
+ def set(value)
78
+ @var.set(value)
79
+ end
80
+
81
+ end
40
82
 
41
83
  NIL_SENTINEL = Object.new
42
84
 
@@ -58,19 +100,24 @@ module Concurrent
58
100
  end
59
101
 
60
102
  def value=(value)
103
+ bind value
104
+ end
105
+
106
+ def bind(value, &block)
61
107
  if value.nil?
62
108
  stored_value = NIL_SENTINEL
63
109
  else
64
110
  stored_value = value
65
111
  end
66
112
 
67
- set stored_value
113
+ set stored_value, &block
68
114
 
69
115
  value
70
116
  end
71
117
 
72
118
  end
73
119
 
120
+ # @!macro abstract_thread_local_var
74
121
  class ThreadLocalVar < AbstractThreadLocalVar
75
122
  if RUBY_PLATFORM == 'java'
76
123
  include ThreadLocalJavaStorage
@@ -1,10 +1,14 @@
1
- require 'concurrent_ruby_ext'
2
- require 'concurrent/atomic_reference/direct_update'
1
+ require_relative '../../extension_helper'
2
+ Concurrent.safe_require_java_extensions
3
3
 
4
- module Concurrent
4
+ if defined?(Concurrent::JavaAtomic)
5
+ require 'concurrent/atomic_reference/direct_update'
5
6
 
6
- # @!macro atomic_reference
7
- class JavaAtomic
8
- include Concurrent::AtomicDirectUpdate
7
+ module Concurrent
8
+
9
+ # @!macro atomic_reference
10
+ class JavaAtomic
11
+ include Concurrent::AtomicDirectUpdate
12
+ end
9
13
  end
10
14
  end
@@ -1,8 +1,12 @@
1
- begin
2
- require 'concurrent_ruby_ext'
3
- rescue LoadError
4
- # may be a Windows cross-compiled native gem
5
- require "#{RUBY_VERSION[0..2]}/concurrent_ruby_ext"
1
+ require_relative '../../extension_helper'
2
+
3
+ if Concurrent.allow_c_extensions?
4
+ begin
5
+ require 'concurrent_ruby_ext'
6
+ rescue LoadError
7
+ # may be a Windows cross-compiled native gem
8
+ require "#{RUBY_VERSION[0..2]}/concurrent_ruby_ext"
9
+ end
6
10
  end
7
11
 
8
12
  require 'concurrent/atomic_reference/direct_update'
@@ -14,19 +18,19 @@ module Concurrent
14
18
  class CAtomic
15
19
  include Concurrent::AtomicDirectUpdate
16
20
  include Concurrent::AtomicNumericCompareAndSetWrapper
17
-
21
+
18
22
  # @!method initialize
19
23
  # @!macro atomic_reference_method_initialize
20
-
24
+
21
25
  # @!method get
22
26
  # @!macro atomic_reference_method_get
23
-
27
+
24
28
  # @!method set
25
29
  # @!macro atomic_reference_method_set
26
-
30
+
27
31
  # @!method get_and_set
28
32
  # @!macro atomic_reference_method_get_and_set
29
-
33
+
30
34
  # @!method _compare_and_set
31
35
  # @!macro atomic_reference_method_compare_and_set
32
36
  end
@@ -7,5 +7,4 @@ require 'concurrent/atomic/copy_on_write_observer_set'
7
7
  require 'concurrent/atomic/cyclic_barrier'
8
8
  require 'concurrent/atomic/count_down_latch'
9
9
  require 'concurrent/atomic/event'
10
- require 'concurrent/atomic/thread_local_var'
11
10
  require 'concurrent/atomic/synchronization'
@@ -17,6 +17,9 @@ module Concurrent
17
17
  # lambda { |level, progname, message = nil, &block| _ }
18
18
  attr_accessor :logger
19
19
 
20
+ # defines if executors should be auto-terminated in at_exit callback
21
+ attr_accessor :auto_terminate
22
+
20
23
  # Create a new configuration object.
21
24
  def initialize
22
25
  immediate_executor = ImmediateExecutor.new
@@ -24,6 +27,7 @@ module Concurrent
24
27
  @global_operation_pool = Delay.new(executor: immediate_executor) { new_operation_pool }
25
28
  @global_timer_set = Delay.new(executor: immediate_executor) { Concurrent::TimerSet.new }
26
29
  @logger = no_logger
30
+ @auto_terminate = true
27
31
  end
28
32
 
29
33
  # if assigned to {#logger}, it will log nothing.
@@ -129,6 +133,12 @@ module Concurrent
129
133
  yield(configuration)
130
134
  end
131
135
 
136
+ def self.finalize_global_executors
137
+ self.finalize_executor(self.configuration.global_timer_set)
138
+ self.finalize_executor(self.configuration.global_task_pool)
139
+ self.finalize_executor(self.configuration.global_operation_pool)
140
+ end
141
+
132
142
  private
133
143
 
134
144
  # Attempt to properly shutdown the given executor using the `shutdown` or
@@ -150,12 +160,8 @@ module Concurrent
150
160
  false
151
161
  end
152
162
 
153
-
154
163
  # set exit hook to shutdown global thread pools
155
164
  at_exit do
156
- self.finalize_executor(self.configuration.global_timer_set)
157
- self.finalize_executor(self.configuration.global_task_pool)
158
- self.finalize_executor(self.configuration.global_operation_pool)
159
- # TODO may break other test suites using concurrent-ruby, terminates before test is run
165
+ finalize_global_executors if configuration.auto_terminate
160
166
  end
161
167
  end
@@ -19,36 +19,7 @@ module Concurrent
19
19
  end
20
20
  end
21
21
 
22
- # Dataflow allows you to create a task that will be scheduled then all of its
23
- # data dependencies are available. Data dependencies are `Future` values. The
24
- # dataflow task itself is also a `Future` value, so you can build up a graph of
25
- # these tasks, each of which is run when all the data and other tasks it depends
26
- # on are available or completed.
27
- #
28
- # Our syntax is somewhat related to that of Akka's `flow` and Habanero Java's
29
- # `DataDrivenFuture`. However unlike Akka we don't schedule a task at all until
30
- # it is ready to run, and unlike Habanero Java we pass the data values into the
31
- # task instead of dereferencing them again in the task.
32
- #
33
- # The theory of dataflow goes back to the 80s. In the terminology of the literature,
34
- # our implementation is coarse-grained, in that each task can be many instructions,
35
- # and dynamic in that you can create more tasks within other tasks.
36
- #
37
- # @example Parallel Fibonacci calculator
38
- # def fib(n)
39
- # if n < 2
40
- # Concurrent::dataflow { n }
41
- # else
42
- # n1 = fib(n - 1)
43
- # n2 = fib(n - 2)
44
- # Concurrent::dataflow(n1, n2) { |v1, v2| v1 + v2 }
45
- # end
46
- # end
47
- #
48
- # f = fib(14) #=> #<Concurrent::Future:0x000001019a26d8 ...
49
- #
50
- # # wait up to 1 second for the answer...
51
- # f.value(1) #=> 377
22
+ # {include:file:doc/dataflow.md}
52
23
  #
53
24
  # @param [Future] inputs zero or more `Future` operations that this dataflow depends upon
54
25
  #
@@ -3,8 +3,15 @@ module Concurrent
3
3
  # Object references in Ruby are mutable. This can lead to serious problems when
4
4
  # the `#value` of a concurrent object is a mutable reference. Which is always the
5
5
  # case unless the value is a `Fixnum`, `Symbol`, or similar "primitive" data type.
6
- # Most classes in this library that expose a `#value` getter method do so using
7
- # this mixin module.
6
+ # Most classes in this library that expose a `#value` getter method do so using the
7
+ # `Dereferenceable` mixin module.
8
+ #
9
+ # Objects with this mixin can be configured with a few options that can help protect
10
+ # the program from potentially dangerous operations.
11
+ #
12
+ # * `:dup_on_deref` when true will call the `#dup` method on the `value` object every time the `#value` method is called (default: false)
13
+ # * `:freeze_on_deref` when true will call the `#freeze` method on the `value` object every time the `#value` method is called (default: false)
14
+ # * `:copy_on_deref` when given a `Proc` object the `Proc` will be run every time the `#value` method is called. The `Proc` will be given the current `value` as its only parameter and the result returned by the block will be the return value of the `#value` call. When `nil` this option will be ignored (default: nil)
8
15
  module Dereferenceable
9
16
 
10
17
  # Return the value this object represents after applying the options specified
@@ -0,0 +1,46 @@
1
+ require 'concurrent/executor/executor'
2
+
3
+ module Concurrent
4
+ # An executor service which runs all operations on a new thread, blocking
5
+ # until it completes. Operations are performed in the order they are received
6
+ # and no two operations can be performed simultaneously.
7
+ #
8
+ # This executor service exists mainly for testing an debugging. When used it
9
+ # immediately runs every `#post` operation on a new thread, blocking the
10
+ # current thread until the operation is complete. This is similar to how the
11
+ # ImmediateExecutor works, but the operation has the full stack of the new
12
+ # thread at its disposal. This can be helpful when the operations will spawn
13
+ # more operations on the same executor and so on - such a situation might
14
+ # overflow the single stack in case of an ImmediateExecutor, which is
15
+ # inconsistent with how it would behave for a threaded executor.
16
+ #
17
+ # @note Intended for use primarily in testing and debugging.
18
+ class IndirectImmediateExecutor < ImmediateExecutor
19
+ # Creates a new executor
20
+ def initialize
21
+ super
22
+ @internal_executor = PerThreadExecutor.new
23
+ end
24
+
25
+ # @!macro executor_method_post
26
+ def post(*args, &task)
27
+ raise ArgumentError.new("no block given") unless block_given?
28
+ return false unless running?
29
+
30
+ event = Concurrent::Event.new
31
+ internal_executor.post do
32
+ begin
33
+ task.call(*args)
34
+ ensure
35
+ event.set
36
+ end
37
+ end
38
+ event.wait
39
+
40
+ true
41
+ end
42
+
43
+ private
44
+ attr_reader :internal_executor
45
+ end
46
+ end
@@ -74,9 +74,7 @@ if RUBY_PLATFORM == 'java'
74
74
  raise ArgumentError.new('min_threads cannot be more than max_threads') if min_length > max_length
75
75
  raise ArgumentError.new("#{@overflow_policy} is not a valid overflow policy") unless OVERFLOW_POLICIES.keys.include?(@overflow_policy)
76
76
 
77
- if min_length == 0 && @max_queue == 0
78
- queue = java.util.concurrent.SynchronousQueue.new
79
- elsif @max_queue == 0
77
+ if @max_queue == 0
80
78
  queue = java.util.concurrent.LinkedBlockingQueue.new
81
79
  else
82
80
  queue = java.util.concurrent.LinkedBlockingQueue.new(@max_queue)
@@ -90,7 +88,7 @@ if RUBY_PLATFORM == 'java'
90
88
  set_shutdown_hook
91
89
  end
92
90
 
93
- # @!macro executor_module_method_can_overflow_question
91
+ # @!macro executor_module_method_can_overflow_question
94
92
  def can_overflow?
95
93
  @max_queue != 0
96
94
  end
@@ -11,20 +11,20 @@ module Concurrent
11
11
  include RubyExecutor
12
12
 
13
13
  # Default maximum number of threads that will be created in the pool.
14
- DEFAULT_MAX_POOL_SIZE = 2**15 # 32768
14
+ DEFAULT_MAX_POOL_SIZE = 2**15 # 32768
15
15
 
16
16
  # Default minimum number of threads that will be retained in the pool.
17
- DEFAULT_MIN_POOL_SIZE = 0
17
+ DEFAULT_MIN_POOL_SIZE = 0
18
18
 
19
19
  # Default maximum number of tasks that may be added to the task queue.
20
- DEFAULT_MAX_QUEUE_SIZE = 0
20
+ DEFAULT_MAX_QUEUE_SIZE = 0
21
21
 
22
22
  # Default maximum number of seconds a thread in the pool may remain idle
23
23
  # before being reclaimed.
24
24
  DEFAULT_THREAD_IDLETIMEOUT = 60
25
25
 
26
26
  # The set of possible overflow policies that may be set at thread pool creation.
27
- OVERFLOW_POLICIES = [:abort, :discard, :caller_runs]
27
+ OVERFLOW_POLICIES = [:abort, :discard, :caller_runs]
28
28
 
29
29
  # The maximum number of threads that may be created in the pool.
30
30
  attr_reader :max_length
@@ -77,10 +77,10 @@ module Concurrent
77
77
  #
78
78
  # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html
79
79
  def initialize(opts = {})
80
- @min_length = opts.fetch(:min_threads, DEFAULT_MIN_POOL_SIZE).to_i
81
- @max_length = opts.fetch(:max_threads, DEFAULT_MAX_POOL_SIZE).to_i
82
- @idletime = opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT).to_i
83
- @max_queue = opts.fetch(:max_queue, DEFAULT_MAX_QUEUE_SIZE).to_i
80
+ @min_length = opts.fetch(:min_threads, DEFAULT_MIN_POOL_SIZE).to_i
81
+ @max_length = opts.fetch(:max_threads, DEFAULT_MAX_POOL_SIZE).to_i
82
+ @idletime = opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT).to_i
83
+ @max_queue = opts.fetch(:max_queue, DEFAULT_MAX_QUEUE_SIZE).to_i
84
84
  @overflow_policy = opts.fetch(:overflow_policy, :abort)
85
85
 
86
86
  raise ArgumentError.new('max_threads must be greater than zero') if @max_length <= 0
@@ -90,13 +90,13 @@ module Concurrent
90
90
 
91
91
  init_executor
92
92
 
93
- @pool = []
94
- @queue = Queue.new
93
+ @pool = []
94
+ @queue = Queue.new
95
95
  @scheduled_task_count = 0
96
96
  @completed_task_count = 0
97
- @largest_length = 0
97
+ @largest_length = 0
98
98
 
99
- @gc_interval = opts.fetch(:gc_interval, 1).to_i # undocumented
99
+ @gc_interval = opts.fetch(:gc_interval, 1).to_i # undocumented
100
100
  @last_gc_time = Time.now.to_f - [1.0, (@gc_interval * 2.0)].max
101
101
  end
102
102
 
@@ -109,15 +109,16 @@ module Concurrent
109
109
  #
110
110
  # @return [Integer] the length
111
111
  def length
112
- mutex.synchronize{ running? ? @pool.length : 0 }
112
+ mutex.synchronize { running? ? @pool.length : 0 }
113
113
  end
114
+
114
115
  alias_method :current_length, :length
115
116
 
116
117
  # The number of tasks in the queue awaiting execution.
117
118
  #
118
119
  # @return [Integer] the queue_length
119
120
  def queue_length
120
- mutex.synchronize{ running? ? @queue.length : 0 }
121
+ mutex.synchronize { running? ? @queue.length : 0 }
121
122
  end
122
123
 
123
124
  # Number of tasks that may be enqueued before reaching `max_queue` and rejecting
@@ -152,7 +153,7 @@ module Concurrent
152
153
  def on_worker_exit(worker)
153
154
  mutex.synchronize do
154
155
  @pool.delete(worker)
155
- if @pool.empty? && ! running?
156
+ if @pool.empty? && !running?
156
157
  stop_event.set
157
158
  stopped_event.set
158
159
  end
@@ -177,7 +178,7 @@ module Concurrent
177
178
  if @pool.empty?
178
179
  stopped_event.set
179
180
  else
180
- @pool.length.times{ @queue << :stop }
181
+ @pool.length.times { @queue << :stop }
181
182
  end
182
183
  end
183
184
 
@@ -196,7 +197,7 @@ module Concurrent
196
197
  # @!visibility private
197
198
  def ensure_capacity?
198
199
  additional = 0
199
- capacity = true
200
+ capacity = true
200
201
 
201
202
  if @pool.size < @min_length
202
203
  additional = @min_length - @pool.size
@@ -254,10 +255,11 @@ module Concurrent
254
255
  # @!visibility private
255
256
  def prune_pool
256
257
  if Time.now.to_f - @gc_interval >= @last_gc_time
257
- @pool.delete_if do |worker|
258
- worker.dead? ||
259
- (@idletime == 0 ? false : Time.now.to_f - @idletime > worker.last_activity)
260
- end
258
+ @pool.delete_if { |worker| worker.dead? }
259
+ # send :stop for each thread over idletime
260
+ @pool.
261
+ select { |worker| @idletime != 0 && Time.now.to_f - @idletime > worker.last_activity }.
262
+ each { @queue << :stop }
261
263
  @last_gc_time = Time.now.to_f
262
264
  end
263
265
  end
@@ -266,7 +268,7 @@ module Concurrent
266
268
  #
267
269
  # @!visibility private
268
270
  def drain_pool
269
- @pool.each {|worker| worker.kill }
271
+ @pool.each { |worker| worker.kill }
270
272
  @pool.clear
271
273
  end
272
274