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

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -13,7 +13,7 @@ if Concurrent.on_jruby?
13
13
  value = @var.get
14
14
 
15
15
  if value.nil?
16
- @default
16
+ default
17
17
  elsif value == NULL
18
18
  nil
19
19
  else
@@ -26,19 +26,6 @@ if Concurrent.on_jruby?
26
26
  @var.set(value)
27
27
  end
28
28
 
29
- # @!macro thread_local_var_method_bind
30
- def bind(value, &block)
31
- if block_given?
32
- old_value = @var.get
33
- begin
34
- @var.set(value)
35
- yield
36
- ensure
37
- @var.set(old_value)
38
- end
39
- end
40
- end
41
-
42
29
  protected
43
30
 
44
31
  # @!visibility private
@@ -1,4 +1,5 @@
1
1
  require 'concurrent/synchronization'
2
+ require 'concurrent/utility/native_integer'
2
3
 
3
4
  module Concurrent
4
5
 
@@ -7,10 +8,6 @@ module Concurrent
7
8
  # @!macro internal_implementation_note
8
9
  class MutexAtomicFixnum < Synchronization::LockableObject
9
10
 
10
- # http://stackoverflow.com/questions/535721/ruby-max-integer
11
- MIN_VALUE = -(2**(0.size * 8 - 2))
12
- MAX_VALUE = (2**(0.size * 8 - 2) - 1)
13
-
14
11
  # @!macro atomic_fixnum_method_initialize
15
12
  def initialize(initial = 0)
16
13
  super()
@@ -71,21 +68,8 @@ module Concurrent
71
68
 
72
69
  # @!visibility private
73
70
  def ns_set(value)
74
- range_check!(value)
71
+ Utility::NativeInteger.ensure_integer_and_bounds value
75
72
  @value = value
76
73
  end
77
-
78
- # @!visibility private
79
- def range_check!(value)
80
- if !value.is_a?(Fixnum)
81
- raise ArgumentError.new('value value must be a Fixnum')
82
- elsif value > MAX_VALUE
83
- raise RangeError.new("#{value} is greater than the maximum value of #{MAX_VALUE}")
84
- elsif value < MIN_VALUE
85
- raise RangeError.new("#{value} is less than the maximum value of #{MIN_VALUE}")
86
- else
87
- value
88
- end
89
- end
90
74
  end
91
75
  end
@@ -9,9 +9,9 @@ module Concurrent
9
9
 
10
10
  # @!macro count_down_latch_method_initialize
11
11
  def initialize(count = 1)
12
- unless count.is_a?(Fixnum) && count >= 0
13
- raise ArgumentError.new('count must be in integer greater than or equal zero')
14
- end
12
+ Utility::NativeInteger.ensure_integer_and_bounds count
13
+ Utility::NativeInteger.ensure_positive count
14
+
15
15
  super()
16
16
  synchronize { ns_initialize count }
17
17
  end
@@ -1,4 +1,5 @@
1
1
  require 'concurrent/synchronization'
2
+ require 'concurrent/utility/native_integer'
2
3
 
3
4
  module Concurrent
4
5
 
@@ -9,18 +10,17 @@ module Concurrent
9
10
 
10
11
  # @!macro semaphore_method_initialize
11
12
  def initialize(count)
12
- unless count.is_a?(Fixnum) && count >= 0
13
- fail ArgumentError, 'count must be an non-negative integer'
14
- end
13
+ Utility::NativeInteger.ensure_integer_and_bounds count
14
+
15
15
  super()
16
16
  synchronize { ns_initialize count }
17
17
  end
18
18
 
19
19
  # @!macro semaphore_method_acquire
20
20
  def acquire(permits = 1)
21
- unless permits.is_a?(Fixnum) && permits > 0
22
- fail ArgumentError, 'permits must be an integer greater than zero'
23
- end
21
+ Utility::NativeInteger.ensure_integer_and_bounds permits
22
+ Utility::NativeInteger.ensure_positive permits
23
+
24
24
  synchronize do
25
25
  try_acquire_timed(permits, nil)
26
26
  nil
@@ -45,9 +45,9 @@ module Concurrent
45
45
 
46
46
  # @!macro semaphore_method_try_acquire
47
47
  def try_acquire(permits = 1, timeout = nil)
48
- unless permits.is_a?(Fixnum) && permits > 0
49
- fail ArgumentError, 'permits must be an integer greater than zero'
50
- end
48
+ Utility::NativeInteger.ensure_integer_and_bounds permits
49
+ Utility::NativeInteger.ensure_positive permits
50
+
51
51
  synchronize do
52
52
  if timeout.nil?
53
53
  try_acquire_now(permits)
@@ -59,9 +59,9 @@ module Concurrent
59
59
 
60
60
  # @!macro semaphore_method_release
61
61
  def release(permits = 1)
62
- unless permits.is_a?(Fixnum) && permits > 0
63
- fail ArgumentError, 'permits must be an integer greater than zero'
64
- end
62
+ Utility::NativeInteger.ensure_integer_and_bounds permits
63
+ Utility::NativeInteger.ensure_positive permits
64
+
65
65
  synchronize do
66
66
  @free += permits
67
67
  permits.times { ns_signal }
@@ -81,9 +81,9 @@ module Concurrent
81
81
  #
82
82
  # @!visibility private
83
83
  def reduce_permits(reduction)
84
- unless reduction.is_a?(Fixnum) && reduction >= 0
85
- fail ArgumentError, 'reduction must be an non-negative integer'
86
- end
84
+ Utility::NativeInteger.ensure_integer_and_bounds reduction
85
+ Utility::NativeInteger.ensure_positive reduction
86
+
87
87
  synchronize { @free -= reduction }
88
88
  nil
89
89
  end
@@ -29,35 +29,25 @@ module Concurrent
29
29
  # array, so we don't leak memory
30
30
 
31
31
  # @!visibility private
32
- FREE = []
33
- LOCK = Mutex.new
32
+ FREE = []
33
+ LOCK = Mutex.new
34
34
  ARRAYS = {} # used as a hash set
35
35
  @@next = 0
36
36
  private_constant :FREE, :LOCK, :ARRAYS
37
37
 
38
- # @!macro [attach] thread_local_var_method_initialize
39
- #
40
- # Creates a thread local variable.
41
- #
42
- # @param [Object] default the default value when otherwise unset
43
- def initialize(default = nil)
44
- @default = default
45
- allocate_storage
46
- end
47
-
48
38
  # @!macro thread_local_var_method_get
49
39
  def value
50
40
  if array = get_threadlocal_array
51
41
  value = array[@index]
52
42
  if value.nil?
53
- @default
43
+ default
54
44
  elsif value.equal?(NULL)
55
45
  nil
56
46
  else
57
47
  value
58
48
  end
59
49
  else
60
- @default
50
+ default
61
51
  end
62
52
  end
63
53
 
@@ -76,28 +66,15 @@ module Concurrent
76
66
  value
77
67
  end
78
68
 
79
- # @!macro thread_local_var_method_bind
80
- def bind(value, &block)
81
- if block_given?
82
- old_value = self.value
83
- begin
84
- self.value = value
85
- yield
86
- ensure
87
- self.value = old_value
88
- end
89
- end
90
- end
91
-
92
69
  protected
93
70
 
94
71
  # @!visibility private
95
72
  def allocate_storage
96
73
  @index = LOCK.synchronize do
97
74
  FREE.pop || begin
98
- result = @@next
99
- @@next += 1
100
- result
75
+ result = @@next
76
+ @@next += 1
77
+ result
101
78
  end
102
79
  end
103
80
  ObjectSpace.define_finalizer(self, self.class.threadlocal_finalizer(@index))
@@ -106,13 +83,15 @@ module Concurrent
106
83
  # @!visibility private
107
84
  def self.threadlocal_finalizer(index)
108
85
  proc do
109
- LOCK.synchronize do
110
- FREE.push(index)
111
- # The cost of GC'ing a TLV is linear in the number of threads using TLVs
112
- # But that is natural! More threads means more storage is used per TLV
113
- # So naturally more CPU time is required to free more storage
114
- ARRAYS.each_value do |array|
115
- array[index] = nil
86
+ Thread.new do # avoid error: can't be called from trap context
87
+ LOCK.synchronize do
88
+ FREE.push(index)
89
+ # The cost of GC'ing a TLV is linear in the number of threads using TLVs
90
+ # But that is natural! More threads means more storage is used per TLV
91
+ # So naturally more CPU time is required to free more storage
92
+ ARRAYS.each_value do |array|
93
+ array[index] = nil
94
+ end
116
95
  end
117
96
  end
118
97
  end
@@ -121,10 +100,12 @@ module Concurrent
121
100
  # @!visibility private
122
101
  def self.thread_finalizer(array)
123
102
  proc do
124
- LOCK.synchronize do
125
- # The thread which used this thread-local array is now gone
126
- # So don't hold onto a reference to the array (thus blocking GC)
127
- ARRAYS.delete(array.object_id)
103
+ Thread.new do # avoid error: can't be called from trap context
104
+ LOCK.synchronize do
105
+ # The thread which used this thread-local array is now gone
106
+ # So don't hold onto a reference to the array (thus blocking GC)
107
+ ARRAYS.delete(array.object_id)
108
+ end
128
109
  end
129
110
  end
130
111
  end
@@ -158,12 +139,20 @@ module Concurrent
158
139
  if array = get_threadlocal_array(thread)
159
140
  value = array[@index]
160
141
  if value.nil?
161
- @default
142
+ default_for(thread)
162
143
  elsif value.equal?(NULL)
163
144
  nil
164
145
  else
165
146
  value
166
147
  end
148
+ else
149
+ default_for(thread)
150
+ end
151
+ end
152
+
153
+ def default_for(thread)
154
+ if @default_block
155
+ raise "Cannot use default_for with default block"
167
156
  else
168
157
  @default
169
158
  end
@@ -11,6 +11,8 @@ module Concurrent
11
11
  # Creates a thread local variable.
12
12
  #
13
13
  # @param [Object] default the default value when otherwise unset
14
+ # @param [Proc] default_block Optional block that gets called to obtain the
15
+ # default value for each thread
14
16
 
15
17
  # @!macro [new] thread_local_var_method_get
16
18
  #
@@ -70,28 +72,28 @@ module Concurrent
70
72
  # the current thread will ever see that change.
71
73
  #
72
74
  # @!macro thread_safe_variable_comparison
73
- #
75
+ #
74
76
  # @example
75
77
  # v = ThreadLocalVar.new(14)
76
78
  # v.value #=> 14
77
79
  # v.value = 2
78
80
  # v.value #=> 2
79
- #
81
+ #
80
82
  # @example
81
83
  # v = ThreadLocalVar.new(14)
82
- #
84
+ #
83
85
  # t1 = Thread.new do
84
86
  # v.value #=> 14
85
87
  # v.value = 1
86
88
  # v.value #=> 1
87
89
  # end
88
- #
90
+ #
89
91
  # t2 = Thread.new do
90
92
  # v.value #=> 14
91
93
  # v.value = 2
92
94
  # v.value #=> 2
93
95
  # end
94
- #
96
+ #
95
97
  # v.value #=> 14
96
98
  #
97
99
  # @see https://docs.oracle.com/javase/7/docs/api/java/lang/ThreadLocal.html Java ThreadLocal
@@ -1 +1,2 @@
1
- require 'concurrent/atomic_reference/mutex_atomic'
1
+ require 'atomic'
2
+ require 'concurrent/atomic_reference/rbx'
@@ -95,6 +95,7 @@ module Concurrent
95
95
  end
96
96
 
97
97
  def each_pair
98
+ return enum_for :each_pair unless block_given?
98
99
  dupped_backend.each_pair do |k, v|
99
100
  yield k, v
100
101
  end
@@ -138,6 +138,7 @@ module Concurrent
138
138
  # @!visibility private
139
139
  def init_obligation
140
140
  @event = Event.new
141
+ @value = @reason = nil
141
142
  end
142
143
 
143
144
  # @!visibility private
@@ -10,11 +10,43 @@ require 'concurrent/utility/processor_counter'
10
10
  module Concurrent
11
11
  extend Concern::Logging
12
12
 
13
- autoload :Options, 'concurrent/options'
14
- autoload :TimerSet, 'concurrent/executor/timer_set'
13
+ autoload :Options, 'concurrent/options'
14
+ autoload :TimerSet, 'concurrent/executor/timer_set'
15
15
  autoload :ThreadPoolExecutor, 'concurrent/executor/thread_pool_executor'
16
16
 
17
17
  # @return [Logger] Logger with provided level and output.
18
+ def self.create_simple_logger(level = Logger::FATAL, output = $stderr)
19
+ # TODO (pitr-ch 24-Dec-2016): figure out why it had to be replaced, stdlogger was deadlocking
20
+ lambda do |severity, progname, message = nil, &block|
21
+ return false if severity < level
22
+
23
+ message = block ? block.call : message
24
+ formatted_message = case message
25
+ when String
26
+ message
27
+ when Exception
28
+ format "%s (%s)\n%s",
29
+ message.message, message.class, (message.backtrace || []).join("\n")
30
+ else
31
+ message.inspect
32
+ end
33
+
34
+ output.print format "[%s] %5s -- %s: %s\n",
35
+ Time.now.strftime('%Y-%m-%d %H:%M:%S.%L'),
36
+ Logger::SEV_LABEL[severity],
37
+ progname,
38
+ formatted_message
39
+ true
40
+ end
41
+ end
42
+
43
+ # Use logger created by #create_simple_logger to log concurrent-ruby messages.
44
+ def self.use_simple_logger(level = Logger::FATAL, output = $stderr)
45
+ Concurrent.global_logger = create_simple_logger level, output
46
+ end
47
+
48
+ # @return [Logger] Logger with provided level and output.
49
+ # @deprecated
18
50
  def self.create_stdlib_logger(level = Logger::FATAL, output = $stderr)
19
51
  logger = Logger.new(output)
20
52
  logger.level = level
@@ -24,32 +56,35 @@ module Concurrent
24
56
  msg
25
57
  when Exception
26
58
  format "%s (%s)\n%s",
27
- msg.message, msg.class, (msg.backtrace || []).join("\n")
59
+ msg.message, msg.class, (msg.backtrace || []).join("\n")
28
60
  else
29
61
  msg.inspect
30
62
  end
31
63
  format "[%s] %5s -- %s: %s\n",
32
- datetime.strftime('%Y-%m-%d %H:%M:%S.%L'),
33
- severity,
34
- progname,
35
- formatted_message
64
+ datetime.strftime('%Y-%m-%d %H:%M:%S.%L'),
65
+ severity,
66
+ progname,
67
+ formatted_message
36
68
  end
37
69
 
38
70
  lambda do |loglevel, progname, message = nil, &block|
39
- logger.add loglevel, message, progname, &block
71
+ logger.add loglevel, message, progname, &block
40
72
  end
41
73
  end
42
74
 
43
75
  # Use logger created by #create_stdlib_logger to log concurrent-ruby messages.
76
+ # @deprecated
44
77
  def self.use_stdlib_logger(level = Logger::FATAL, output = $stderr)
45
78
  Concurrent.global_logger = create_stdlib_logger level, output
46
79
  end
47
80
 
81
+ # TODO (pitr-ch 27-Dec-2016): remove deadlocking stdlib_logger methods
82
+
48
83
  # Suppresses all output when used for logging.
49
84
  NULL_LOGGER = lambda { |level, progname, message = nil, &block| }
50
85
 
51
86
  # @!visibility private
52
- GLOBAL_LOGGER = AtomicReference.new(create_stdlib_logger(Logger::WARN))
87
+ GLOBAL_LOGGER = AtomicReference.new(create_simple_logger(Logger::WARN))
53
88
  private_constant :GLOBAL_LOGGER
54
89
 
55
90
  def self.global_logger
@@ -131,23 +166,23 @@ module Concurrent
131
166
 
132
167
  def self.new_fast_executor(opts = {})
133
168
  FixedThreadPool.new(
134
- [2, Concurrent.processor_count].max,
135
- auto_terminate: opts.fetch(:auto_terminate, true),
136
- idletime: 60, # 1 minute
137
- max_queue: 0, # unlimited
138
- fallback_policy: :abort # shouldn't matter -- 0 max queue
169
+ [2, Concurrent.processor_count].max,
170
+ auto_terminate: opts.fetch(:auto_terminate, true),
171
+ idletime: 60, # 1 minute
172
+ max_queue: 0, # unlimited
173
+ fallback_policy: :abort # shouldn't matter -- 0 max queue
139
174
  )
140
175
  end
141
176
 
142
177
  def self.new_io_executor(opts = {})
143
178
  ThreadPoolExecutor.new(
144
- min_threads: [2, Concurrent.processor_count].max,
145
- max_threads: ThreadPoolExecutor::DEFAULT_MAX_POOL_SIZE,
146
- # max_threads: 1000,
147
- auto_terminate: opts.fetch(:auto_terminate, true),
148
- idletime: 60, # 1 minute
149
- max_queue: 0, # unlimited
150
- fallback_policy: :abort # shouldn't matter -- 0 max queue
179
+ min_threads: [2, Concurrent.processor_count].max,
180
+ max_threads: ThreadPoolExecutor::DEFAULT_MAX_POOL_SIZE,
181
+ # max_threads: 1000,
182
+ auto_terminate: opts.fetch(:auto_terminate, true),
183
+ idletime: 60, # 1 minute
184
+ max_queue: 0, # unlimited
185
+ fallback_policy: :abort # shouldn't matter -- 0 max queue
151
186
  )
152
187
  end
153
188
  end