concurrent-ruby 1.1.8 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +36 -0
  3. data/Gemfile +2 -8
  4. data/README.md +49 -28
  5. data/Rakefile +59 -75
  6. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +0 -0
  7. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +52 -22
  8. data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +10 -25
  9. data/lib/concurrent-ruby/concurrent/agent.rb +2 -1
  10. data/lib/concurrent-ruby/concurrent/array.rb +0 -10
  11. data/lib/concurrent-ruby/concurrent/async.rb +1 -0
  12. data/lib/concurrent-ruby/concurrent/atom.rb +1 -1
  13. data/lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb +5 -4
  14. data/lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb +5 -4
  15. data/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb +3 -0
  16. data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +82 -151
  17. data/lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb +1 -1
  18. data/lib/concurrent-ruby/concurrent/atomic/event.rb +3 -3
  19. data/lib/concurrent-ruby/concurrent/atomic/fiber_local_var.rb +109 -0
  20. data/lib/concurrent-ruby/concurrent/atomic/java_count_down_latch.rb +1 -0
  21. data/lib/concurrent-ruby/concurrent/atomic/locals.rb +188 -0
  22. data/lib/concurrent-ruby/concurrent/atomic/lock_local_var.rb +28 -0
  23. data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb +11 -5
  24. data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb +11 -5
  25. data/lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb +1 -1
  26. data/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb +19 -3
  27. data/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb +2 -1
  28. data/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb +9 -9
  29. data/lib/concurrent-ruby/concurrent/atomic/semaphore.rb +32 -14
  30. data/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb +96 -89
  31. data/lib/concurrent-ruby/concurrent/atomic_reference/atomic_direct_update.rb +37 -0
  32. data/lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb +15 -4
  33. data/lib/concurrent-ruby/concurrent/collection/copy_on_notify_observer_set.rb +1 -1
  34. data/lib/concurrent-ruby/concurrent/collection/copy_on_write_observer_set.rb +1 -1
  35. data/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb +2 -0
  36. data/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb +14 -0
  37. data/lib/concurrent-ruby/concurrent/collection/ruby_non_concurrent_priority_queue.rb +11 -1
  38. data/lib/concurrent-ruby/concurrent/concern/logging.rb +86 -2
  39. data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
  40. data/lib/concurrent-ruby/concurrent/configuration.rb +4 -87
  41. data/lib/concurrent-ruby/concurrent/delay.rb +2 -2
  42. data/lib/concurrent-ruby/concurrent/errors.rb +5 -0
  43. data/lib/concurrent-ruby/concurrent/exchanger.rb +1 -0
  44. data/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb +17 -14
  45. data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +13 -3
  46. data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +3 -3
  47. data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +4 -0
  48. data/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb +10 -4
  49. data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +26 -37
  50. data/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb +6 -6
  51. data/lib/concurrent-ruby/concurrent/executor/serialized_execution.rb +1 -1
  52. data/lib/concurrent-ruby/concurrent/executor/simple_executor_service.rb +4 -1
  53. data/lib/concurrent-ruby/concurrent/hash.rb +0 -9
  54. data/lib/concurrent-ruby/concurrent/immutable_struct.rb +1 -1
  55. data/lib/concurrent-ruby/concurrent/ivar.rb +2 -1
  56. data/lib/concurrent-ruby/concurrent/map.rb +18 -8
  57. data/lib/concurrent-ruby/concurrent/maybe.rb +1 -1
  58. data/lib/concurrent-ruby/concurrent/mutable_struct.rb +1 -1
  59. data/lib/concurrent-ruby/concurrent/mvar.rb +1 -1
  60. data/lib/concurrent-ruby/concurrent/promise.rb +2 -1
  61. data/lib/concurrent-ruby/concurrent/promises.rb +7 -6
  62. data/lib/concurrent-ruby/concurrent/re_include.rb +2 -0
  63. data/lib/concurrent-ruby/concurrent/scheduled_task.rb +30 -17
  64. data/lib/concurrent-ruby/concurrent/set.rb +12 -14
  65. data/lib/concurrent-ruby/concurrent/settable_struct.rb +2 -2
  66. data/lib/concurrent-ruby/concurrent/synchronization/abstract_lockable_object.rb +5 -1
  67. data/lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb +1 -3
  68. data/lib/concurrent-ruby/concurrent/synchronization/condition.rb +2 -0
  69. data/lib/concurrent-ruby/concurrent/synchronization/full_memory_barrier.rb +29 -0
  70. data/lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb +3 -1
  71. data/lib/concurrent-ruby/concurrent/synchronization/lock.rb +2 -0
  72. data/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb +6 -5
  73. data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +18 -5
  74. data/lib/concurrent-ruby/concurrent/synchronization/object.rb +12 -44
  75. data/lib/concurrent-ruby/concurrent/synchronization/safe_initialization.rb +36 -0
  76. data/lib/concurrent-ruby/concurrent/synchronization/volatile.rb +77 -12
  77. data/lib/concurrent-ruby/concurrent/synchronization.rb +1 -18
  78. data/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb +36 -39
  79. data/lib/concurrent-ruby/concurrent/thread_safe/util/cheap_lockable.rb +2 -39
  80. data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +16 -27
  81. data/lib/concurrent-ruby/concurrent/timer_task.rb +11 -33
  82. data/lib/concurrent-ruby/concurrent/tuple.rb +1 -5
  83. data/lib/concurrent-ruby/concurrent/tvar.rb +22 -61
  84. data/lib/concurrent-ruby/concurrent/utility/engine.rb +5 -16
  85. data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +7 -46
  86. data/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb +8 -10
  87. data/lib/concurrent-ruby/concurrent/utility/native_integer.rb +1 -0
  88. data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +36 -89
  89. data/lib/concurrent-ruby/concurrent/version.rb +1 -1
  90. data/lib/concurrent-ruby/concurrent-ruby.rb +5 -1
  91. metadata +11 -12
  92. data/lib/concurrent-ruby/concurrent/atomic/abstract_thread_local_var.rb +0 -66
  93. data/lib/concurrent-ruby/concurrent/atomic/java_thread_local_var.rb +0 -37
  94. data/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb +0 -181
  95. data/lib/concurrent-ruby/concurrent/synchronization/jruby_object.rb +0 -45
  96. data/lib/concurrent-ruby/concurrent/synchronization/mri_object.rb +0 -44
  97. data/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb +0 -65
  98. data/lib/concurrent-ruby/concurrent/synchronization/rbx_object.rb +0 -49
  99. data/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb +0 -47
@@ -1,7 +1,7 @@
1
1
  require 'concurrent/errors'
2
2
  require 'concurrent/concern/deprecation'
3
3
  require 'concurrent/executor/executor_service'
4
- require 'concurrent/synchronization'
4
+ require 'concurrent/synchronization/lockable_object'
5
5
 
6
6
  module Concurrent
7
7
 
@@ -75,28 +75,31 @@ module Concurrent
75
75
 
76
76
  private
77
77
 
78
- # Handler which executes the `fallback_policy` once the queue size
79
- # reaches `max_queue`.
78
+ # Returns an action which executes the `fallback_policy` once the queue
79
+ # size reaches `max_queue`. The reason for the indirection of an action
80
+ # is so that the work can be deferred outside of synchronization.
80
81
  #
81
82
  # @param [Array] args the arguments to the task which is being handled.
82
83
  #
83
84
  # @!visibility private
84
- def handle_fallback(*args)
85
+ def fallback_action(*args)
85
86
  case fallback_policy
86
87
  when :abort
87
- raise RejectedExecutionError
88
+ lambda { raise RejectedExecutionError }
88
89
  when :discard
89
- false
90
+ lambda { false }
90
91
  when :caller_runs
91
- begin
92
- yield(*args)
93
- rescue => ex
94
- # let it fail
95
- log DEBUG, ex
96
- end
97
- true
92
+ lambda {
93
+ begin
94
+ yield(*args)
95
+ rescue => ex
96
+ # let it fail
97
+ log DEBUG, ex
98
+ end
99
+ true
100
+ }
98
101
  else
99
- fail "Unknown fallback policy #{fallback_policy}"
102
+ lambda { fail "Unknown fallback policy #{fallback_policy}" }
100
103
  end
101
104
  end
102
105
 
@@ -71,9 +71,16 @@ module Concurrent
71
71
  # @return [Integer] Number of tasks that may be enqueued before reaching `max_queue` and rejecting
72
72
  # new tasks. A value of -1 indicates that the queue may grow without bound.
73
73
 
74
-
75
-
76
-
74
+ # @!macro thread_pool_executor_method_prune_pool
75
+ # Prune the thread pool of unneeded threads
76
+ #
77
+ # What is being pruned is controlled by the min_threads and idletime
78
+ # parameters passed at pool creation time
79
+ #
80
+ # This is a no-op on some pool implementation (e.g. the Java one). The Ruby
81
+ # pool will auto-prune each time a new job is posted. You will need to call
82
+ # this method explicitely in case your application post jobs in bursts (a
83
+ # lot of jobs and then nothing for long periods)
77
84
 
78
85
  # @!macro thread_pool_executor_public_api
79
86
  #
@@ -111,6 +118,9 @@ module Concurrent
111
118
  #
112
119
  # @!method can_overflow?
113
120
  # @!macro executor_service_method_can_overflow_question
121
+ #
122
+ # @!method prune_pool
123
+ # @!macro thread_pool_executor_method_prune_pool
114
124
 
115
125
 
116
126
 
@@ -1,7 +1,7 @@
1
- if Concurrent.on_jruby?
1
+ require 'concurrent/utility/engine'
2
2
 
3
+ if Concurrent.on_jruby?
3
4
  require 'concurrent/errors'
4
- require 'concurrent/utility/engine'
5
5
  require 'concurrent/executor/abstract_executor_service'
6
6
 
7
7
  module Concurrent
@@ -20,7 +20,7 @@ if Concurrent.on_jruby?
20
20
 
21
21
  def post(*args, &task)
22
22
  raise ArgumentError.new('no block given') unless block_given?
23
- return handle_fallback(*args, &task) unless running?
23
+ return fallback_action(*args, &task).call unless running?
24
24
  @executor.submit Job.new(args, task)
25
25
  true
26
26
  rescue Java::JavaUtilConcurrent::RejectedExecutionException
@@ -93,6 +93,10 @@ if Concurrent.on_jruby?
93
93
  super && !@executor.isTerminating
94
94
  end
95
95
 
96
+ # @!macro thread_pool_executor_method_prune_pool
97
+ def prune_pool
98
+ end
99
+
96
100
  private
97
101
 
98
102
  def ns_initialize(opts)
@@ -16,10 +16,16 @@ module Concurrent
16
16
 
17
17
  def post(*args, &task)
18
18
  raise ArgumentError.new('no block given') unless block_given?
19
- synchronize do
20
- # If the executor is shut down, reject this task
21
- return handle_fallback(*args, &task) unless running?
22
- ns_execute(*args, &task)
19
+ deferred_action = synchronize {
20
+ if running?
21
+ ns_execute(*args, &task)
22
+ else
23
+ fallback_action(*args, &task)
24
+ end
25
+ }
26
+ if deferred_action
27
+ deferred_action.call
28
+ else
23
29
  true
24
30
  end
25
31
  end
@@ -93,13 +93,8 @@ module Concurrent
93
93
  end
94
94
 
95
95
  # @!visibility private
96
- def ready_worker(worker)
97
- synchronize { ns_ready_worker worker }
98
- end
99
-
100
- # @!visibility private
101
- def worker_not_old_enough(worker)
102
- synchronize { ns_worker_not_old_enough worker }
96
+ def ready_worker(worker, last_message)
97
+ synchronize { ns_ready_worker worker, last_message }
103
98
  end
104
99
 
105
100
  # @!visibility private
@@ -112,6 +107,11 @@ module Concurrent
112
107
  synchronize { @completed_task_count += 1 }
113
108
  end
114
109
 
110
+ # @!macro thread_pool_executor_method_prune_pool
111
+ def prune_pool
112
+ synchronize { ns_prune_pool }
113
+ end
114
+
115
115
  private
116
116
 
117
117
  # @!visibility private
@@ -156,10 +156,11 @@ module Concurrent
156
156
  if ns_assign_worker(*args, &task) || ns_enqueue(*args, &task)
157
157
  @scheduled_task_count += 1
158
158
  else
159
- handle_fallback(*args, &task)
159
+ return fallback_action(*args, &task)
160
160
  end
161
161
 
162
162
  ns_prune_pool if @next_gc_time < Concurrent.monotonic_time
163
+ nil
163
164
  end
164
165
 
165
166
  # @!visibility private
@@ -192,7 +193,7 @@ module Concurrent
192
193
  # @!visibility private
193
194
  def ns_assign_worker(*args, &task)
194
195
  # keep growing if the pool is not at the minimum yet
195
- worker = (@ready.pop if @pool.size >= @min_length) || ns_add_busy_worker
196
+ worker, _ = (@ready.pop if @pool.size >= @min_length) || ns_add_busy_worker
196
197
  if worker
197
198
  worker << [task, args]
198
199
  true
@@ -223,7 +224,7 @@ module Concurrent
223
224
  def ns_worker_died(worker)
224
225
  ns_remove_busy_worker worker
225
226
  replacement_worker = ns_add_busy_worker
226
- ns_ready_worker replacement_worker, false if replacement_worker
227
+ ns_ready_worker replacement_worker, Concurrent.monotonic_time, false if replacement_worker
227
228
  end
228
229
 
229
230
  # creates new worker which has to receive work to do after it's added
@@ -242,29 +243,21 @@ module Concurrent
242
243
  # handle ready worker, giving it new job or assigning back to @ready
243
244
  #
244
245
  # @!visibility private
245
- def ns_ready_worker(worker, success = true)
246
+ def ns_ready_worker(worker, last_message, success = true)
246
247
  task_and_args = @queue.shift
247
248
  if task_and_args
248
249
  worker << task_and_args
249
250
  else
250
251
  # stop workers when !running?, do not return them to @ready
251
252
  if running?
252
- @ready.push(worker)
253
+ raise unless last_message
254
+ @ready.push([worker, last_message])
253
255
  else
254
256
  worker.stop
255
257
  end
256
258
  end
257
259
  end
258
260
 
259
- # returns back worker to @ready which was not idle for enough time
260
- #
261
- # @!visibility private
262
- def ns_worker_not_old_enough(worker)
263
- # let's put workers coming from idle_test back to the start (as the oldest worker)
264
- @ready.unshift(worker)
265
- true
266
- end
267
-
268
261
  # removes a worker which is not in not tracked in @ready
269
262
  #
270
263
  # @!visibility private
@@ -278,10 +271,17 @@ module Concurrent
278
271
  #
279
272
  # @!visibility private
280
273
  def ns_prune_pool
281
- return if @pool.size <= @min_length
282
-
283
- last_used = @ready.shift
284
- last_used << :idle_test if last_used
274
+ now = Concurrent.monotonic_time
275
+ stopped_workers = 0
276
+ while !@ready.empty? && (@pool.size - stopped_workers > @min_length)
277
+ worker, last_message = @ready.first
278
+ if now - last_message > self.idletime
279
+ stopped_workers += 1
280
+ @ready.shift
281
+ worker << :stop
282
+ else break
283
+ end
284
+ end
285
285
 
286
286
  @next_gc_time = Concurrent.monotonic_time + @gc_interval
287
287
  end
@@ -330,19 +330,10 @@ module Concurrent
330
330
 
331
331
  def create_worker(queue, pool, idletime)
332
332
  Thread.new(queue, pool, idletime) do |my_queue, my_pool, my_idletime|
333
- last_message = Concurrent.monotonic_time
334
333
  catch(:stop) do
335
334
  loop do
336
335
 
337
336
  case message = my_queue.pop
338
- when :idle_test
339
- if (Concurrent.monotonic_time - last_message) > my_idletime
340
- my_pool.remove_busy_worker(self)
341
- throw :stop
342
- else
343
- my_pool.worker_not_old_enough(self)
344
- end
345
-
346
337
  when :stop
347
338
  my_pool.remove_busy_worker(self)
348
339
  throw :stop
@@ -350,9 +341,7 @@ module Concurrent
350
341
  else
351
342
  task, args = message
352
343
  run_task my_pool, task, args
353
- last_message = Concurrent.monotonic_time
354
-
355
- my_pool.ready_worker(self)
344
+ my_pool.ready_worker(self, Concurrent.monotonic_time)
356
345
  end
357
346
  end
358
347
  end
@@ -1,4 +1,4 @@
1
- require 'concurrent/synchronization'
1
+ require 'concurrent/synchronization/lockable_object'
2
2
 
3
3
  module Concurrent
4
4
 
@@ -16,10 +16,10 @@ module Concurrent
16
16
 
17
17
  # @return [Array]
18
18
  def execute(*args)
19
- synchronize do
20
- success = false
21
- value = reason = nil
19
+ success = true
20
+ value = reason = nil
22
21
 
22
+ synchronize do
23
23
  begin
24
24
  value = @task.call(*args)
25
25
  success = true
@@ -27,9 +27,9 @@ module Concurrent
27
27
  reason = ex
28
28
  success = false
29
29
  end
30
-
31
- [success, value, reason]
32
30
  end
31
+
32
+ [success, value, reason]
33
33
  end
34
34
  end
35
35
  end
@@ -1,6 +1,6 @@
1
1
  require 'concurrent/errors'
2
2
  require 'concurrent/concern/logging'
3
- require 'concurrent/synchronization'
3
+ require 'concurrent/synchronization/lockable_object'
4
4
 
5
5
  module Concurrent
6
6
 
@@ -1,5 +1,8 @@
1
- require 'concurrent/atomics'
1
+ require 'concurrent/atomic/atomic_boolean'
2
+ require 'concurrent/atomic/atomic_fixnum'
3
+ require 'concurrent/atomic/event'
2
4
  require 'concurrent/executor/executor_service'
5
+ require 'concurrent/executor/ruby_executor_service'
3
6
 
4
7
  module Concurrent
5
8
 
@@ -28,15 +28,6 @@ module Concurrent
28
28
  end
29
29
  JRubyHash
30
30
 
31
- when Concurrent.on_rbx?
32
- require 'monitor'
33
- require 'concurrent/thread_safe/util/data_structures'
34
-
35
- class RbxHash < ::Hash
36
- end
37
- ThreadSafe::Util.make_synchronized_on_rbx RbxHash
38
- RbxHash
39
-
40
31
  when Concurrent.on_truffleruby?
41
32
  require 'concurrent/thread_safe/util/data_structures'
42
33
 
@@ -1,5 +1,5 @@
1
1
  require 'concurrent/synchronization/abstract_struct'
2
- require 'concurrent/synchronization'
2
+ require 'concurrent/synchronization/lockable_object'
3
3
 
4
4
  module Concurrent
5
5
 
@@ -3,7 +3,8 @@ require 'concurrent/errors'
3
3
  require 'concurrent/collection/copy_on_write_observer_set'
4
4
  require 'concurrent/concern/obligation'
5
5
  require 'concurrent/concern/observable'
6
- require 'concurrent/synchronization'
6
+ require 'concurrent/executor/safe_task_executor'
7
+ require 'concurrent/synchronization/lockable_object'
7
8
 
8
9
  module Concurrent
9
10
 
@@ -1,6 +1,5 @@
1
1
  require 'thread'
2
2
  require 'concurrent/constants'
3
- require 'concurrent/synchronization'
4
3
  require 'concurrent/utility/engine'
5
4
 
6
5
  module Concurrent
@@ -10,14 +9,20 @@ module Concurrent
10
9
  # @!visibility private
11
10
  MapImplementation = case
12
11
  when Concurrent.on_jruby?
12
+ require 'concurrent/utility/native_extension_loader'
13
13
  # noinspection RubyResolve
14
14
  JRubyMapBackend
15
15
  when Concurrent.on_cruby?
16
16
  require 'concurrent/collection/map/mri_map_backend'
17
17
  MriMapBackend
18
- when Concurrent.on_rbx? || Concurrent.on_truffleruby?
19
- require 'concurrent/collection/map/atomic_reference_map_backend'
20
- AtomicReferenceMapBackend
18
+ when Concurrent.on_truffleruby?
19
+ if defined?(::TruffleRuby::ConcurrentMap)
20
+ require 'concurrent/collection/map/truffleruby_map_backend'
21
+ TruffleRubyMapBackend
22
+ else
23
+ require 'concurrent/collection/map/atomic_reference_map_backend'
24
+ AtomicReferenceMapBackend
25
+ end
21
26
  else
22
27
  warn 'Concurrent::Map: unsupported Ruby engine, using a fully synchronized Concurrent::Map implementation'
23
28
  require 'concurrent/collection/map/synchronized_map_backend'
@@ -114,7 +119,7 @@ module Concurrent
114
119
  # @return [true, false] true if deleted
115
120
  # @!macro map.atomic_method
116
121
 
117
-
122
+ #
118
123
  def initialize(options = nil, &block)
119
124
  if options.kind_of?(::Hash)
120
125
  validate_options_hash!(options)
@@ -143,8 +148,15 @@ module Concurrent
143
148
  end
144
149
  end
145
150
 
151
+ # Set a value with key
152
+ # @param [Object] key
153
+ # @param [Object] value
154
+ # @return [Object] the new value
155
+ def []=(key, value)
156
+ super
157
+ end
158
+
146
159
  alias_method :get, :[]
147
- # TODO (pitr-ch 30-Oct-2018): doc
148
160
  alias_method :put, :[]=
149
161
 
150
162
  # Get a value with key, or default_value when key is absent,
@@ -187,7 +199,6 @@ module Concurrent
187
199
  # @yieldparam key [Object]
188
200
  # @yieldreturn [Object] default value
189
201
  # @return [Object] the value or default value
190
- # @!macro map.atomic_method_with_block
191
202
  def fetch_or_store(key, default_value = NULL)
192
203
  fetch(key) do
193
204
  put(key, block_given? ? yield(key) : (NULL == default_value ? raise_fetch_no_key : default_value))
@@ -271,7 +282,6 @@ module Concurrent
271
282
  each_pair { |k, v| return k if v == value }
272
283
  nil
273
284
  end unless method_defined?(:key)
274
- alias_method :index, :key if RUBY_VERSION < '1.9'
275
285
 
276
286
  # Is map empty?
277
287
  # @return [true, false]
@@ -1,4 +1,4 @@
1
- require 'concurrent/synchronization'
1
+ require 'concurrent/synchronization/object'
2
2
 
3
3
  module Concurrent
4
4
 
@@ -1,5 +1,5 @@
1
1
  require 'concurrent/synchronization/abstract_struct'
2
- require 'concurrent/synchronization'
2
+ require 'concurrent/synchronization/lockable_object'
3
3
 
4
4
  module Concurrent
5
5
 
@@ -1,5 +1,5 @@
1
1
  require 'concurrent/concern/dereferenceable'
2
- require 'concurrent/synchronization'
2
+ require 'concurrent/synchronization/object'
3
3
 
4
4
  module Concurrent
5
5
 
@@ -66,7 +66,7 @@ module Concurrent
66
66
  # Start by requiring promises
67
67
  #
68
68
  # ```ruby
69
- # require 'concurrent'
69
+ # require 'concurrent/promise'
70
70
  # ```
71
71
  #
72
72
  # Then create one
@@ -250,6 +250,7 @@ module Concurrent
250
250
  realize(@promise_body)
251
251
  end
252
252
  else
253
+ compare_and_set_state(:pending, :unscheduled)
253
254
  @parent.execute
254
255
  end
255
256
  self
@@ -1,7 +1,8 @@
1
- require 'concurrent/synchronization'
1
+ require 'concurrent/synchronization/object'
2
2
  require 'concurrent/atomic/atomic_boolean'
3
3
  require 'concurrent/atomic/atomic_fixnum'
4
4
  require 'concurrent/collection/lock_free_stack'
5
+ require 'concurrent/configuration'
5
6
  require 'concurrent/errors'
6
7
  require 'concurrent/re_include'
7
8
 
@@ -313,7 +314,7 @@ module Concurrent
313
314
  end
314
315
 
315
316
  # @!macro promises.shortcut.on
316
- # @return [Future]
317
+ # @return [Event]
317
318
  def any_event(*futures_and_or_events)
318
319
  any_event_on default_executor, *futures_and_or_events
319
320
  end
@@ -892,7 +893,7 @@ module Concurrent
892
893
  private
893
894
 
894
895
  def rejected_resolution(raise_on_reassign, state)
895
- Concurrent::MultipleAssignmentError.new('Event can be resolved only once') if raise_on_reassign
896
+ raise Concurrent::MultipleAssignmentError.new('Event can be resolved only once') if raise_on_reassign
896
897
  return false
897
898
  end
898
899
 
@@ -1298,7 +1299,7 @@ module Concurrent
1298
1299
 
1299
1300
  # @!macro promise.param.raise_on_reassign
1300
1301
  # @param [Boolean] raise_on_reassign should method raise exception if already resolved
1301
- # @return [self, false] false is returner when raise_on_reassign is false and the receiver
1302
+ # @return [self, false] false is returned when raise_on_reassign is false and the receiver
1302
1303
  # is already resolved.
1303
1304
  #
1304
1305
 
@@ -2074,8 +2075,8 @@ module Concurrent
2074
2075
 
2075
2076
  private
2076
2077
 
2077
- def resolvable?(countdown, future, index)
2078
- future.fulfilled? ||
2078
+ def resolvable?(countdown, event_or_future, index)
2079
+ (event_or_future.is_a?(Event) ? event_or_future.resolved? : event_or_future.fulfilled?) ||
2079
2080
  # inlined super from BlockedPromise
2080
2081
  countdown.zero?
2081
2082
  end
@@ -31,6 +31,8 @@ module Concurrent
31
31
  #
32
32
  # C1.new.respond_to? :a # => false
33
33
  # C2.new.respond_to? :a # => true
34
+ #
35
+ # @!visibility private
34
36
  module ReInclude
35
37
  # @!visibility private
36
38
  def included(base)
@@ -57,30 +57,43 @@ module Concurrent
57
57
  #
58
58
  # @example Basic usage
59
59
  #
60
- # require 'concurrent'
61
- # require 'thread' # for Queue
62
- # require 'open-uri' # for open(uri)
60
+ # require 'concurrent/scheduled_task'
61
+ # require 'csv'
62
+ # require 'open-uri'
63
63
  #
64
64
  # class Ticker
65
- # def get_year_end_closing(symbol, year)
66
- # uri = "http://ichart.finance.yahoo.com/table.csv?s=#{symbol}&a=11&b=01&c=#{year}&d=11&e=31&f=#{year}&g=m"
67
- # data = open(uri) {|f| f.collect{|line| line.strip } }
68
- # data[1].split(',')[4].to_f
69
- # end
65
+ # def get_year_end_closing(symbol, year, api_key)
66
+ # uri = "https://www.alphavantage.co/query?function=TIME_SERIES_MONTHLY&symbol=#{symbol}&apikey=#{api_key}&datatype=csv"
67
+ # data = []
68
+ # csv = URI.parse(uri).read
69
+ # if csv.include?('call frequency')
70
+ # return :rate_limit_exceeded
71
+ # end
72
+ # CSV.parse(csv, headers: true) do |row|
73
+ # data << row['close'].to_f if row['timestamp'].include?(year.to_s)
74
+ # end
75
+ # year_end = data.first
76
+ # year_end
77
+ # rescue => e
78
+ # p e
79
+ # end
70
80
  # end
71
81
  #
82
+ # api_key = ENV['ALPHAVANTAGE_KEY']
83
+ # abort(error_message) unless api_key
84
+ #
72
85
  # # Future
73
- # price = Concurrent::Future.execute{ Ticker.new.get_year_end_closing('TWTR', 2013) }
86
+ # price = Concurrent::Future.execute{ Ticker.new.get_year_end_closing('TWTR', 2013, api_key) }
74
87
  # price.state #=> :pending
75
- # sleep(1) # do other stuff
76
- # price.value #=> 63.65
77
- # price.state #=> :fulfilled
88
+ # price.pending? #=> true
89
+ # price.value(0) #=> nil (does not block)
78
90
  #
79
- # # ScheduledTask
80
- # task = Concurrent::ScheduledTask.execute(2){ Ticker.new.get_year_end_closing('INTC', 2013) }
81
- # task.state #=> :pending
82
- # sleep(3) # do other stuff
83
- # task.value #=> 25.96
91
+ # sleep(1) # do other stuff
92
+ #
93
+ # price.value #=> 63.65 (after blocking if necessary)
94
+ # price.state #=> :fulfilled
95
+ # price.fulfilled? #=> true
96
+ # price.value #=> 63.65
84
97
  #
85
98
  # @example Successful task execution
86
99
  #
@@ -19,13 +19,19 @@ module Concurrent
19
19
  #
20
20
  # @see http://ruby-doc.org/stdlib-2.4.0/libdoc/set/rdoc/Set.html Ruby standard library `Set`
21
21
 
22
-
23
22
  # @!macro internal_implementation_note
24
23
  SetImplementation = case
25
24
  when Concurrent.on_cruby?
26
- # Because MRI never runs code in parallel, the existing
27
- # non-thread-safe structures should usually work fine.
28
- ::Set
25
+ # The CRuby implementation of Set is written in Ruby itself and is
26
+ # not thread safe for certain methods.
27
+ require 'monitor'
28
+ require 'concurrent/thread_safe/util/data_structures'
29
+
30
+ class CRubySet < ::Set
31
+ end
32
+
33
+ ThreadSafe::Util.make_synchronized_on_cruby CRubySet
34
+ CRubySet
29
35
 
30
36
  when Concurrent.on_jruby?
31
37
  require 'jruby/synchronized'
@@ -33,16 +39,8 @@ module Concurrent
33
39
  class JRubySet < ::Set
34
40
  include JRuby::Synchronized
35
41
  end
36
- JRubySet
37
-
38
- when Concurrent.on_rbx?
39
- require 'monitor'
40
- require 'concurrent/thread_safe/util/data_structures'
41
42
 
42
- class RbxSet < ::Set
43
- end
44
- ThreadSafe::Util.make_synchronized_on_rbx Concurrent::RbxSet
45
- RbxSet
43
+ JRubySet
46
44
 
47
45
  when Concurrent.on_truffleruby?
48
46
  require 'concurrent/thread_safe/util/data_structures'
@@ -50,7 +48,7 @@ module Concurrent
50
48
  class TruffleRubySet < ::Set
51
49
  end
52
50
 
53
- ThreadSafe::Util.make_synchronized_on_truffleruby Concurrent::TruffleRubySet
51
+ ThreadSafe::Util.make_synchronized_on_truffleruby TruffleRubySet
54
52
  TruffleRubySet
55
53
 
56
54
  else
@@ -1,6 +1,6 @@
1
- require 'concurrent/synchronization/abstract_struct'
2
1
  require 'concurrent/errors'
3
- require 'concurrent/synchronization'
2
+ require 'concurrent/synchronization/abstract_struct'
3
+ require 'concurrent/synchronization/lockable_object'
4
4
 
5
5
  module Concurrent
6
6