concurrent-ruby 1.0.3.pre3-java → 1.0.4-java

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +40 -12
  3. data/README.md +29 -39
  4. data/lib/concurrent.rb +3 -3
  5. data/lib/concurrent/async.rb +2 -2
  6. data/lib/concurrent/atom.rb +4 -3
  7. data/lib/concurrent/atomic/abstract_thread_local_var.rb +29 -3
  8. data/lib/concurrent/atomic/atomic_fixnum.rb +4 -0
  9. data/lib/concurrent/atomic/atomic_reference.rb +7 -0
  10. data/lib/concurrent/atomic/count_down_latch.rb +23 -0
  11. data/lib/concurrent/atomic/cyclic_barrier.rb +23 -3
  12. data/lib/concurrent/atomic/java_thread_local_var.rb +1 -14
  13. data/lib/concurrent/atomic/mutex_atomic_fixnum.rb +2 -18
  14. data/lib/concurrent/atomic/mutex_count_down_latch.rb +3 -3
  15. data/lib/concurrent/atomic/mutex_semaphore.rb +15 -15
  16. data/lib/concurrent/atomic/ruby_thread_local_var.rb +31 -42
  17. data/lib/concurrent/atomic/thread_local_var.rb +7 -5
  18. data/lib/concurrent/atomic_reference/jruby+truffle.rb +2 -1
  19. data/lib/concurrent/collection/map/non_concurrent_map_backend.rb +1 -0
  20. data/lib/concurrent/concern/obligation.rb +1 -0
  21. data/lib/concurrent/configuration.rb +56 -21
  22. data/lib/concurrent/errors.rb +24 -1
  23. data/lib/concurrent/executor/timer_set.rb +11 -0
  24. data/lib/concurrent/hash.rb +2 -1
  25. data/lib/concurrent/map.rb +5 -3
  26. data/lib/concurrent/promise.rb +10 -6
  27. data/lib/concurrent/synchronization/object.rb +2 -2
  28. data/lib/concurrent/synchronization/rbx_object.rb +1 -0
  29. data/lib/concurrent/synchronization/truffle_object.rb +1 -2
  30. data/lib/concurrent/thread_safe/util.rb +2 -0
  31. data/lib/concurrent/timer_task.rb +3 -3
  32. data/lib/concurrent/tvar.rb +1 -1
  33. data/lib/concurrent/utility/engine.rb +3 -3
  34. data/lib/concurrent/utility/native_integer.rb +53 -0
  35. data/lib/concurrent/utility/processor_counter.rb +15 -13
  36. data/lib/concurrent/version.rb +2 -2
  37. data/lib/concurrent_ruby_ext.jar +0 -0
  38. metadata +8 -6
@@ -30,7 +30,18 @@ module Concurrent
30
30
 
31
31
  # Raised when an attempt is made to modify an immutable object
32
32
  # (such as an `IVar`) after its final state has been set.
33
- MultipleAssignmentError = Class.new(Error)
33
+ class MultipleAssignmentError < Error
34
+ attr_reader :inspection_data
35
+
36
+ def initialize(message = nil, inspection_data = nil)
37
+ @inspection_data = inspection_data
38
+ super message
39
+ end
40
+
41
+ def inspect
42
+ format '%s %s>', super[0..-2], @inspection_data.inspect
43
+ end
44
+ end
34
45
 
35
46
  # Raised by an `Executor` when it is unable to process a given task,
36
47
  # possibly because of a reject policy or other internal error.
@@ -43,4 +54,16 @@ module Concurrent
43
54
  # Raised when an operation times out.
44
55
  TimeoutError = Class.new(Error)
45
56
 
57
+ # Aggregates multiple exceptions.
58
+ class MultipleErrors < Error
59
+ attr_reader :errors
60
+
61
+ def initialize(errors, message = "#{errors.size} errors")
62
+ @errors = errors
63
+ super [*message,
64
+ *errors.map { |e| [format('%s (%s)', e.message, e.class), *e.backtrace] }.flatten(1)
65
+ ].join("\n")
66
+ end
67
+ end
68
+
46
69
  end
@@ -78,6 +78,7 @@ module Concurrent
78
78
  @task_executor = Options.executor_from_options(opts) || Concurrent.global_io_executor
79
79
  @timer_executor = SingleThreadExecutor.new
80
80
  @condition = Event.new
81
+ @ruby_pid = $$ # detects if Ruby has forked
81
82
  self.auto_terminate = opts.fetch(:auto_terminate, true)
82
83
  end
83
84
 
@@ -95,6 +96,7 @@ module Concurrent
95
96
  # @!visibility private
96
97
  def ns_post_task(task)
97
98
  return false unless ns_running?
99
+ ns_reset_if_forked
98
100
  if (task.initial_delay) <= 0.01
99
101
  task.executor.post{ task.process_task }
100
102
  else
@@ -121,11 +123,20 @@ module Concurrent
121
123
  #
122
124
  # @!visibility private
123
125
  def ns_shutdown_execution
126
+ ns_reset_if_forked
124
127
  @queue.clear
125
128
  @timer_executor.kill
126
129
  stopped_event.set
127
130
  end
128
131
 
132
+ def ns_reset_if_forked
133
+ if $$ != @ruby_pid
134
+ @queue.clear
135
+ @condition.reset
136
+ @ruby_pid = $$
137
+ end
138
+ end
139
+
129
140
  # Run a loop and execute tasks in the scheduled order and at the approximate
130
141
  # scheduled time. If no tasks remain the thread will exit gracefully so that
131
142
  # garbage collection can occur. If there are no ready tasks it will sleep
@@ -8,7 +8,8 @@ module Concurrent
8
8
  #
9
9
  # A thread-safe subclass of Hash. This version locks against the object
10
10
  # itself for every method call, ensuring only one thread can be reading
11
- # or writing at a time. This includes iteration methods like `#each`.
11
+ # or writing at a time. This includes iteration methods like `#each`,
12
+ # which takes the lock repeatedly when reading an item.
12
13
  #
13
14
  # @see http://ruby-doc.org/core-2.2.0/Hash.html Ruby standard library `Hash`
14
15
  class Hash < ::Hash;
@@ -171,6 +171,8 @@ module Concurrent
171
171
  each_pair {|k, v| yield v}
172
172
  end unless method_defined?(:each_value)
173
173
 
174
+ alias_method :each, :each_pair unless method_defined?(:each)
175
+
174
176
  def key(value)
175
177
  each_pair {|k, v| return k if v == value}
176
178
  nil
@@ -203,7 +205,7 @@ module Concurrent
203
205
  undef :freeze
204
206
 
205
207
  # @!visibility private
206
- DEFAULT_OBJ_ID_STR_WIDTH = (2**50).class == Fixnum ? 14 : 7 # we want to look "native", 7 for 32-bit, 14 for 64-bit
208
+ DEFAULT_OBJ_ID_STR_WIDTH = 0.size == 4 ? 7 : 14 # we want to look "native", 7 for 32-bit, 14 for 64-bit
207
209
  # override default #inspect() method: firstly, we don't want to be spilling our guts (i-vars), secondly, MRI backend's
208
210
  # #inspect() call on its @backend i-var will bump @backend's iter level while possibly yielding GVL
209
211
  def inspect
@@ -227,8 +229,8 @@ module Concurrent
227
229
  end
228
230
 
229
231
  def validate_options_hash!(options)
230
- if (initial_capacity = options[:initial_capacity]) && (!initial_capacity.kind_of?(Fixnum) || initial_capacity < 0)
231
- raise ArgumentError, ":initial_capacity must be a positive Fixnum"
232
+ if (initial_capacity = options[:initial_capacity]) && (!initial_capacity.kind_of?(Integer) || initial_capacity < 0)
233
+ raise ArgumentError, ":initial_capacity must be a positive Integer"
232
234
  end
233
235
  if (load_factor = options[:load_factor]) && (!load_factor.kind_of?(Numeric) || load_factor <= 0 || load_factor > 1)
234
236
  raise ArgumentError, ":load_factor must be a number between 0 and 1"
@@ -2,6 +2,7 @@ require 'thread'
2
2
  require 'concurrent/constants'
3
3
  require 'concurrent/errors'
4
4
  require 'concurrent/ivar'
5
+ require 'concurrent/executor/safe_task_executor'
5
6
 
6
7
  require 'concurrent/options'
7
8
 
@@ -78,7 +79,7 @@ module Concurrent
78
79
  # ```
79
80
  #
80
81
  # Promises can be chained using the `then` method. The `then` method accepts a
81
- # block, to be executed on fulfillment, and a callable argument to be executed
82
+ # block and an executor, to be executed on fulfillment, and a callable argument to be executed
82
83
  # on rejection. The result of the each promise is passed as the block argument
83
84
  # to chained promises.
84
85
  #
@@ -92,7 +93,7 @@ module Concurrent
92
93
  # p = Concurrent::Promise.fulfill(20).
93
94
  # then{|result| result - 10 }.
94
95
  # then{|result| result * 3 }.
95
- # then{|result| result % 5 }.execute
96
+ # then(executor: different_executor){|result| result % 5 }.execute
96
97
  # ```
97
98
  #
98
99
  # The initial state of a newly created Promise depends on the state of its parent:
@@ -102,7 +103,7 @@ module Concurrent
102
103
  # - if parent is *rejected* the child will be *pending* (but will ultimately be *rejected*)
103
104
  #
104
105
  # Promises are executed asynchronously from the main thread. By the time a
105
- # child Promise finishes nitialization it may be in a different state that its
106
+ # child Promise finishes intialization it may be in a different state than its
106
107
  # parent (by the time a child is created its parent may have completed
107
108
  # execution and changed state). Despite being asynchronous, however, the order
108
109
  # of execution of Promise objects in a chain (or tree) is strictly defined.
@@ -300,15 +301,18 @@ module Concurrent
300
301
  # @param [Proc] rescuer An optional rescue block to be executed if the
301
302
  # promise is rejected.
302
303
  #
304
+ # @param [ThreadPool] executor An optional thread pool executor to be used
305
+ # in the new Promise
306
+ #
303
307
  # @yield The block operation to be performed asynchronously.
304
308
  #
305
309
  # @return [Promise] the new promise
306
- def then(rescuer = nil, &block)
310
+ def then(rescuer = nil, executor = @executor, &block)
307
311
  raise ArgumentError.new('rescuers and block are both missing') if rescuer.nil? && !block_given?
308
312
  block = Proc.new { |result| result } unless block_given?
309
313
  child = Promise.new(
310
314
  parent: self,
311
- executor: @executor,
315
+ executor: executor,
312
316
  on_fulfill: block,
313
317
  on_reject: rescuer
314
318
  )
@@ -524,7 +528,7 @@ module Concurrent
524
528
  # @!visibility private
525
529
  def realize(task)
526
530
  @executor.post do
527
- success, value, reason = SafeTaskExecutor.new(task).execute(*@args)
531
+ success, value, reason = SafeTaskExecutor.new(task, rescue_exception: true).execute(*@args)
528
532
  complete(success, value, reason)
529
533
  end
530
534
  end
@@ -70,8 +70,8 @@ module Concurrent
70
70
  # any instance variables with CamelCase names and isn't {.safe_initialization?}.
71
71
  def self.ensure_safe_initialization_when_final_fields_are_present
72
72
  Object.class_eval do
73
- def self.new(*)
74
- object = super
73
+ def self.new(*args, &block)
74
+ object = super(*args, &block)
75
75
  ensure
76
76
  has_final_field = object.instance_variables.any? { |v| v.to_s =~ /^@[A-Z]/ }
77
77
  if has_final_field && !safe_initialization?
@@ -30,6 +30,7 @@ module Concurrent
30
30
 
31
31
  def full_memory_barrier
32
32
  # Rubinius instance variables are not volatile so we need to insert barrier
33
+ # TODO (pitr 26-Nov-2015): check comments like ^
33
34
  Rubinius.memory_barrier
34
35
  end
35
36
  end
@@ -14,8 +14,7 @@ module Concurrent
14
14
  end
15
15
 
16
16
  def full_memory_barrier
17
- # Rubinius instance variables are not volatile so we need to insert barrier
18
- Rubinius.memory_barrier
17
+ Truffle::System.full_memory_barrier
19
18
  end
20
19
  end
21
20
 
@@ -6,8 +6,10 @@ module Concurrent
6
6
  # @!visibility private
7
7
  module Util
8
8
 
9
+ # TODO (pitr-ch 15-Oct-2016): migrate to Utility::NativeInteger
9
10
  FIXNUM_BIT_SIZE = (0.size * 8) - 2
10
11
  MAX_INT = (2 ** FIXNUM_BIT_SIZE) - 1
12
+ # TODO (pitr-ch 15-Oct-2016): migrate to Utility::ProcessorCounter
11
13
  CPU_COUNT = 16 # is there a way to determine this?
12
14
  end
13
15
  end
@@ -9,7 +9,7 @@ require 'concurrent/scheduled_task'
9
9
 
10
10
  module Concurrent
11
11
 
12
- # A very common currency pattern is to run a thread that performs a task at
12
+ # A very common concurrency pattern is to run a thread that performs a task at
13
13
  # regular intervals. The thread that performs the task sleeps for the given
14
14
  # interval then wakes up and performs the task. Lather, rinse, repeat... This
15
15
  # pattern causes two problems. First, it is difficult to test the business
@@ -23,7 +23,7 @@ module Concurrent
23
23
  # execution interval. The `TimerTask` thread does not perform the task,
24
24
  # however. Instead, the TimerTask launches the task on a separate thread.
25
25
  # Should the task experience an unrecoverable crash only the task thread will
26
- # crash. This makes the `TimerTask` very fault tolerant Additionally, the
26
+ # crash. This makes the `TimerTask` very fault tolerant. Additionally, the
27
27
  # `TimerTask` thread can respond to the success or failure of the task,
28
28
  # performing logging or ancillary operations. `TimerTask` can also be
29
29
  # configured with a timeout value allowing it to kill a task that runs too
@@ -41,7 +41,7 @@ module Concurrent
41
41
  #
42
42
  # The `TimerTask` class includes the `Dereferenceable` mixin module so the
43
43
  # result of the last execution is always available via the `#value` method.
44
- # Derefencing options can be passed to the `TimerTask` during construction or
44
+ # Dereferencing options can be passed to the `TimerTask` during construction or
45
45
  # at any later time using the `#set_deref_options` method.
46
46
  #
47
47
  # `TimerTask` supports notification through the Ruby standard library
@@ -151,7 +151,7 @@ module Concurrent
151
151
  raise Transaction::AbortError.new
152
152
  end
153
153
 
154
- # Leave a transaction without commiting or aborting - see `Concurrent::atomically`.
154
+ # Leave a transaction without committing or aborting - see `Concurrent::atomically`.
155
155
  def leave_transaction
156
156
  raise Transaction::LeaveError.new
157
157
  end
@@ -8,7 +8,7 @@ module Concurrent
8
8
  end
9
9
 
10
10
  def on_jruby_9000?
11
- on_jruby? && 0 == (JRUBY_VERSION =~ /^9\.0\.0\.0/)
11
+ on_jruby? && ruby_version(:>=, 9, 0, 0, JRUBY_VERSION)
12
12
  end
13
13
 
14
14
  def on_cruby?
@@ -39,8 +39,8 @@ module Concurrent
39
39
  defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'
40
40
  end
41
41
 
42
- def ruby_version(comparison, major, minor = 0, patch = 0)
43
- result = (RUBY_VERSION.split('.').map(&:to_i) <=> [major, minor, patch])
42
+ def ruby_version(comparison, major, minor, patch, version = RUBY_VERSION)
43
+ result = (version.split('.').map(&:to_i) <=> [major, minor, patch])
44
44
  comparisons = { :== => [0],
45
45
  :>= => [1, 0],
46
46
  :<= => [-1, 0],
@@ -0,0 +1,53 @@
1
+ module Concurrent
2
+ module Utility
3
+ # @private
4
+ module NativeInteger
5
+ # http://stackoverflow.com/questions/535721/ruby-max-integer
6
+ MIN_VALUE = -(2**(0.size * 8 - 2))
7
+ MAX_VALUE = (2**(0.size * 8 - 2) - 1)
8
+
9
+ def ensure_upper_bound(value)
10
+ if value > MAX_VALUE
11
+ raise RangeError.new("#{value} is greater than the maximum value of #{MAX_VALUE}")
12
+ end
13
+ value
14
+ end
15
+
16
+ def ensure_lower_bound(value)
17
+ if value < MIN_VALUE
18
+ raise RangeError.new("#{value} is less than the maximum value of #{MIN_VALUE}")
19
+ end
20
+ value
21
+ end
22
+
23
+ def ensure_integer(value)
24
+ unless value.is_a?(Integer)
25
+ raise ArgumentError.new("#{value} is not an Integer")
26
+ end
27
+ value
28
+ end
29
+
30
+ def ensure_integer_and_bounds(value)
31
+ ensure_integer value
32
+ ensure_upper_bound value
33
+ ensure_lower_bound value
34
+ end
35
+
36
+ def ensure_positive(value)
37
+ if value < 0
38
+ raise ArgumentError.new("#{value} cannot be negative")
39
+ end
40
+ value
41
+ end
42
+
43
+ def ensure_positive_and_no_zero(value)
44
+ if value < 1
45
+ raise ArgumentError.new("#{value} cannot be negative or zero")
46
+ end
47
+ value
48
+ end
49
+
50
+ extend self
51
+ end
52
+ end
53
+ end
@@ -28,6 +28,7 @@ module Concurrent
28
28
  # processor", which taked into account hyperthreading.
29
29
  #
30
30
  # * AIX: /usr/sbin/pmcycles (AIX 5+), /usr/sbin/lsdev
31
+ # * Alpha: /usr/bin/nproc (/proc/cpuinfo exists but cannot be used)
31
32
  # * BSD: /sbin/sysctl
32
33
  # * Cygwin: /proc/cpuinfo
33
34
  # * Darwin: /usr/bin/hwprefs, /usr/sbin/sysctl
@@ -84,27 +85,28 @@ module Concurrent
84
85
  result = WIN32OLE.connect("winmgmts://").ExecQuery(
85
86
  "select NumberOfLogicalProcessors from Win32_Processor")
86
87
  result.to_enum.collect(&:NumberOfLogicalProcessors).reduce(:+)
87
- elsif File.readable?("/proc/cpuinfo")
88
- IO.read("/proc/cpuinfo").scan(/^processor/).size
88
+ elsif File.readable?("/proc/cpuinfo") && (cpuinfo_count = IO.read("/proc/cpuinfo").scan(/^processor/).size) > 0
89
+ cpuinfo_count
90
+ elsif File.executable?("/usr/bin/nproc")
91
+ IO.popen("/usr/bin/nproc --all", &:read).to_i
89
92
  elsif File.executable?("/usr/bin/hwprefs")
90
- IO.popen("/usr/bin/hwprefs thread_count").read.to_i
93
+ IO.popen("/usr/bin/hwprefs thread_count", &:read).to_i
91
94
  elsif File.executable?("/usr/sbin/psrinfo")
92
- IO.popen("/usr/sbin/psrinfo").read.scan(/^.*on-*line/).size
95
+ IO.popen("/usr/sbin/psrinfo", &:read).scan(/^.*on-*line/).size
93
96
  elsif File.executable?("/usr/sbin/ioscan")
94
- IO.popen("/usr/sbin/ioscan -kC processor") do |out|
95
- out.read.scan(/^.*processor/).size
96
- end
97
+ IO.popen("/usr/sbin/ioscan -kC processor", &:read).scan(/^.*processor/).size
97
98
  elsif File.executable?("/usr/sbin/pmcycles")
98
- IO.popen("/usr/sbin/pmcycles -m").read.count("\n")
99
+ IO.popen("/usr/sbin/pmcycles -m", &:read).count("\n")
99
100
  elsif File.executable?("/usr/sbin/lsdev")
100
- IO.popen("/usr/sbin/lsdev -Cc processor -S 1").read.count("\n")
101
+ IO.popen("/usr/sbin/lsdev -Cc processor -S 1", &:read).count("\n")
101
102
  elsif File.executable?("/usr/sbin/sysconf") and os_name =~ /irix/i
102
- IO.popen("/usr/sbin/sysconf NPROC_ONLN").read.to_i
103
+ IO.popen("/usr/sbin/sysconf NPROC_ONLN", &:read).to_i
103
104
  elsif File.executable?("/usr/sbin/sysctl")
104
- IO.popen("/usr/sbin/sysctl -n hw.ncpu").read.to_i
105
+ IO.popen("/usr/sbin/sysctl -n hw.ncpu", &:read).to_i
105
106
  elsif File.executable?("/sbin/sysctl")
106
- IO.popen("/sbin/sysctl -n hw.ncpu").read.to_i
107
+ IO.popen("/sbin/sysctl -n hw.ncpu", &:read).to_i
107
108
  else
109
+ # TODO (pitr-ch 05-Nov-2016): warn about failures
108
110
  1
109
111
  end
110
112
  end
@@ -115,7 +117,7 @@ module Concurrent
115
117
  def compute_physical_processor_count
116
118
  ppc = case RbConfig::CONFIG["target_os"]
117
119
  when /darwin1/
118
- IO.popen("/usr/sbin/sysctl -n hw.physicalcpu").read.to_i
120
+ IO.popen("/usr/sbin/sysctl -n hw.physicalcpu", &:read).to_i
119
121
  when /linux/
120
122
  cores = {} # unique physical ID / core ID combinations
121
123
  phy = 0
@@ -1,4 +1,4 @@
1
1
  module Concurrent
2
- VERSION = '1.0.3.pre3'
3
- EDGE_VERSION = '0.2.3.pre3'
2
+ VERSION = '1.0.4'
3
+ EDGE_VERSION = '0.3.0'
4
4
  end
Binary file
metadata CHANGED
@@ -1,15 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: concurrent-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3.pre3
4
+ version: 1.0.4
5
5
  platform: java
6
6
  authors:
7
7
  - Jerry D'Antonio
8
+ - Petr Chalupa
8
9
  - The Ruby Concurrency Team
9
10
  autorequire:
10
11
  bindir: bin
11
12
  cert_chain: []
12
- date: 2016-06-23 00:00:00.000000000 Z
13
+ date: 2016-12-27 00:00:00.000000000 Z
13
14
  dependencies: []
14
15
  description: |
15
16
  Modern concurrency tools including agents, futures, promises, thread pools, actors, supervisors, and more.
@@ -147,6 +148,7 @@ files:
147
148
  - lib/concurrent/utility/engine.rb
148
149
  - lib/concurrent/utility/monotonic_time.rb
149
150
  - lib/concurrent/utility/native_extension_loader.rb
151
+ - lib/concurrent/utility/native_integer.rb
150
152
  - lib/concurrent/utility/processor_counter.rb
151
153
  - lib/concurrent/version.rb
152
154
  - lib/concurrent_ruby_ext.jar
@@ -160,17 +162,17 @@ require_paths:
160
162
  - lib
161
163
  required_ruby_version: !ruby/object:Gem::Requirement
162
164
  requirements:
163
- - - '>='
165
+ - - ">="
164
166
  - !ruby/object:Gem::Version
165
167
  version: 1.9.3
166
168
  required_rubygems_version: !ruby/object:Gem::Requirement
167
169
  requirements:
168
- - - '>'
170
+ - - ">="
169
171
  - !ruby/object:Gem::Version
170
- version: 1.3.1
172
+ version: '0'
171
173
  requirements: []
172
174
  rubyforge_project:
173
- rubygems_version: 2.4.5
175
+ rubygems_version: 2.6.8
174
176
  signing_key:
175
177
  specification_version: 4
176
178
  summary: Modern concurrency tools for Ruby. Inspired by Erlang, Clojure, Scala, Haskell, F#, C#, Java, and classic concurrency patterns.