concurrent-ruby 1.2.0 → 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -0
- data/Gemfile +1 -1
- data/README.md +2 -0
- data/Rakefile +7 -6
- data/lib/concurrent-ruby/concurrent/array.rb +3 -3
- data/lib/concurrent-ruby/concurrent/atomic/locals.rb +1 -0
- data/lib/concurrent-ruby/concurrent/collection/map/mri_map_backend.rb +2 -2
- data/lib/concurrent-ruby/concurrent/collection/map/non_concurrent_map_backend.rb +16 -8
- data/lib/concurrent-ruby/concurrent/collection/map/synchronized_map_backend.rb +23 -20
- data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
- data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +4 -0
- data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +4 -7
- data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +5 -0
- data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +7 -0
- data/lib/concurrent-ruby/concurrent/executor/timer_set.rb +6 -2
- data/lib/concurrent-ruby/concurrent/hash.rb +5 -3
- data/lib/concurrent-ruby/concurrent/map.rb +36 -33
- data/lib/concurrent-ruby/concurrent/promises.rb +33 -23
- data/lib/concurrent-ruby/concurrent/timer_task.rb +59 -9
- data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +65 -0
- data/lib/concurrent-ruby/concurrent/version.rb +1 -1
- metadata +2 -4
- data/lib/concurrent-ruby/concurrent/collection/map/atomic_reference_map_backend.rb +0 -927
- data/lib/concurrent-ruby/concurrent/thread_safe/util/cheap_lockable.rb +0 -81
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7841a791b1b148d8e8efea668fc70f3acc98c247f8d49fea58a4c672b45a62e6
|
4
|
+
data.tar.gz: 380ca8252b5b9251f30cd925d8b08f2afae56db19a210a8726b44c7304e5cd84
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e479a5475e8d6dd7b3589a76300ccc73162cbceb8a15b721462394ff92f02054ba06e8ec5ee3ab08aa391a846d00492c708c893416b4c0c159c53d9b065f755f
|
7
|
+
data.tar.gz: 52daa298a6820a10756bfbf472aff566f5b5508f4e014352aa7a8d2516873b1e69b6392cde1379c0feaeba7c054c6c6c919613c3e09ac4d2328939cde3f01444
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,27 @@
|
|
1
1
|
## Current
|
2
2
|
|
3
|
+
## Release v1.3.1 (29 May 2024)
|
4
|
+
|
5
|
+
* Release 1.3.0 was broken when pushed to RubyGems. 1.3.1 is a packaging fix.
|
6
|
+
|
7
|
+
## Release v1.3.0 (28 May 2024)
|
8
|
+
|
9
|
+
* (#1042) Align Java Executor Service behavior for `shuttingdown?`, `shutdown?`
|
10
|
+
* (#1038) Add `Concurrent.usable_processor_count` that is cgroups aware.
|
11
|
+
|
12
|
+
## Release v1.2.3 (16 Jan 2024)
|
13
|
+
|
14
|
+
* See [the GitHub release](https://github.com/ruby-concurrency/concurrent-ruby/releases/tag/v1.2.3) for details.
|
15
|
+
|
16
|
+
## Release v1.2.2 (24 Feb 2023)
|
17
|
+
|
18
|
+
* (#993) Fix arguments passed to `Concurrent::Map`'s `default_proc`.
|
19
|
+
|
20
|
+
## Release v1.2.1 (24 Feb 2023)
|
21
|
+
|
22
|
+
* (#990) Add missing `require 'fiber'` for `FiberLocalVar`.
|
23
|
+
* (#989) Optimize `Concurrent::Map#[]` on CRuby by letting the backing Hash handle the `default_proc`.
|
24
|
+
|
3
25
|
## Release v1.2.0 (23 Jan 2023)
|
4
26
|
|
5
27
|
* (#962) Fix ReentrantReadWriteLock to use the same granularity for locals as for Mutex it uses.
|
data/Gemfile
CHANGED
@@ -12,7 +12,7 @@ gem 'concurrent-ruby-ext', Concurrent::VERSION, options.merge(platform: :mri)
|
|
12
12
|
|
13
13
|
group :development do
|
14
14
|
gem 'rake', '~> 13.0'
|
15
|
-
gem 'rake-compiler', '~> 1.0', '>= 1.0.7'
|
15
|
+
gem 'rake-compiler', '~> 1.0', '>= 1.0.7', '!= 1.2.4'
|
16
16
|
gem 'rake-compiler-dock', '~> 1.0'
|
17
17
|
gem 'pry', '~> 0.11', platforms: :mri
|
18
18
|
end
|
data/README.md
CHANGED
@@ -375,6 +375,8 @@ best practice is to depend on `concurrent-ruby` and let users to decide if they
|
|
375
375
|
* [Benoit Daloze](https://github.com/eregon)
|
376
376
|
* [Matthew Draper](https://github.com/matthewd)
|
377
377
|
* [Rafael França](https://github.com/rafaelfranca)
|
378
|
+
* [Charles Oliver Nutter](https://github.com/headius)
|
379
|
+
* [Ben Sheldon](https://github.com/bensheldon)
|
378
380
|
* [Samuel Williams](https://github.com/ioquatix)
|
379
381
|
|
380
382
|
### Special Thanks to
|
data/Rakefile
CHANGED
@@ -271,6 +271,7 @@ namespace :release do
|
|
271
271
|
task :publish => ['publish:ask', 'publish:tag', 'publish:rubygems', 'publish:post_steps']
|
272
272
|
|
273
273
|
namespace :publish do
|
274
|
+
publish_base = true
|
274
275
|
publish_edge = false
|
275
276
|
|
276
277
|
task :ask do
|
@@ -289,8 +290,8 @@ namespace :release do
|
|
289
290
|
desc '** tag HEAD with current version and push to github'
|
290
291
|
task :tag => :ask do
|
291
292
|
Dir.chdir(__dir__) do
|
292
|
-
sh "git tag v#{Concurrent::VERSION}"
|
293
|
-
sh "git push origin v#{Concurrent::VERSION}"
|
293
|
+
sh "git tag v#{Concurrent::VERSION}" if publish_base
|
294
|
+
sh "git push origin v#{Concurrent::VERSION}" if publish_base
|
294
295
|
sh "git tag edge-v#{Concurrent::EDGE_VERSION}" if publish_edge
|
295
296
|
sh "git push origin edge-v#{Concurrent::EDGE_VERSION}" if publish_edge
|
296
297
|
end
|
@@ -299,11 +300,11 @@ namespace :release do
|
|
299
300
|
desc '** push all *.gem files to rubygems'
|
300
301
|
task :rubygems => :ask do
|
301
302
|
Dir.chdir(__dir__) do
|
302
|
-
sh "gem push pkg/concurrent-ruby-#{Concurrent::VERSION}.gem"
|
303
|
+
sh "gem push pkg/concurrent-ruby-#{Concurrent::VERSION}.gem" if publish_base
|
303
304
|
sh "gem push pkg/concurrent-ruby-edge-#{Concurrent::EDGE_VERSION}.gem" if publish_edge
|
304
|
-
sh "gem push pkg/concurrent-ruby-ext-#{Concurrent::VERSION}.gem"
|
305
|
-
sh "gem push pkg/concurrent-ruby-ext-#{Concurrent::VERSION}-x64-mingw32.gem"
|
306
|
-
sh "gem push pkg/concurrent-ruby-ext-#{Concurrent::VERSION}-x86-mingw32.gem"
|
305
|
+
sh "gem push pkg/concurrent-ruby-ext-#{Concurrent::VERSION}.gem" if publish_base
|
306
|
+
sh "gem push pkg/concurrent-ruby-ext-#{Concurrent::VERSION}-x64-mingw32.gem" if publish_base
|
307
|
+
sh "gem push pkg/concurrent-ruby-ext-#{Concurrent::VERSION}-x86-mingw32.gem" if publish_base
|
307
308
|
end
|
308
309
|
end
|
309
310
|
|
@@ -21,9 +21,9 @@ module Concurrent
|
|
21
21
|
# @!macro internal_implementation_note
|
22
22
|
ArrayImplementation = case
|
23
23
|
when Concurrent.on_cruby?
|
24
|
-
# Array is thread-safe
|
25
|
-
#
|
26
|
-
#
|
24
|
+
# Array is not fully thread-safe on CRuby, see
|
25
|
+
# https://github.com/ruby-concurrency/concurrent-ruby/issues/929
|
26
|
+
# So we will need to add synchronization here
|
27
27
|
::Array
|
28
28
|
|
29
29
|
when Concurrent.on_jruby?
|
@@ -9,8 +9,8 @@ module Concurrent
|
|
9
9
|
# @!visibility private
|
10
10
|
class MriMapBackend < NonConcurrentMapBackend
|
11
11
|
|
12
|
-
def initialize(options = nil)
|
13
|
-
super(options)
|
12
|
+
def initialize(options = nil, &default_proc)
|
13
|
+
super(options, &default_proc)
|
14
14
|
@write_lock = Mutex.new
|
15
15
|
end
|
16
16
|
|
@@ -12,8 +12,10 @@ module Concurrent
|
|
12
12
|
# directly without calling each other. This is important because of the
|
13
13
|
# SynchronizedMapBackend which uses a non-reentrant mutex for performance
|
14
14
|
# reasons.
|
15
|
-
def initialize(options = nil)
|
16
|
-
|
15
|
+
def initialize(options = nil, &default_proc)
|
16
|
+
validate_options_hash!(options) if options.kind_of?(::Hash)
|
17
|
+
set_backend(default_proc)
|
18
|
+
@default_proc = default_proc
|
17
19
|
end
|
18
20
|
|
19
21
|
def [](key)
|
@@ -55,7 +57,7 @@ module Concurrent
|
|
55
57
|
end
|
56
58
|
|
57
59
|
def compute(key)
|
58
|
-
store_computed_value(key, yield(
|
60
|
+
store_computed_value(key, yield(get_or_default(key, nil)))
|
59
61
|
end
|
60
62
|
|
61
63
|
def merge_pair(key, value)
|
@@ -67,7 +69,7 @@ module Concurrent
|
|
67
69
|
end
|
68
70
|
|
69
71
|
def get_and_set(key, value)
|
70
|
-
stored_value =
|
72
|
+
stored_value = get_or_default(key, nil)
|
71
73
|
@backend[key] = value
|
72
74
|
stored_value
|
73
75
|
end
|
@@ -109,13 +111,19 @@ module Concurrent
|
|
109
111
|
@backend.fetch(key, default_value)
|
110
112
|
end
|
111
113
|
|
112
|
-
alias_method :_get, :[]
|
113
|
-
alias_method :_set, :[]=
|
114
|
-
private :_get, :_set
|
115
114
|
private
|
115
|
+
|
116
|
+
def set_backend(default_proc)
|
117
|
+
if default_proc
|
118
|
+
@backend = ::Hash.new { |_h, key| default_proc.call(self, key) }
|
119
|
+
else
|
120
|
+
@backend = {}
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
116
124
|
def initialize_copy(other)
|
117
125
|
super
|
118
|
-
@
|
126
|
+
set_backend(@default_proc)
|
119
127
|
self
|
120
128
|
end
|
121
129
|
|
@@ -8,74 +8,77 @@ module Concurrent
|
|
8
8
|
# @!visibility private
|
9
9
|
class SynchronizedMapBackend < NonConcurrentMapBackend
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
def initialize(*args, &block)
|
12
|
+
super
|
13
|
+
|
14
|
+
# WARNING: Mutex is a non-reentrant lock, so the synchronized methods are
|
15
|
+
# not allowed to call each other.
|
16
|
+
@mutex = Mutex.new
|
17
|
+
end
|
15
18
|
|
16
19
|
def [](key)
|
17
|
-
synchronize { super }
|
20
|
+
@mutex.synchronize { super }
|
18
21
|
end
|
19
22
|
|
20
23
|
def []=(key, value)
|
21
|
-
synchronize { super }
|
24
|
+
@mutex.synchronize { super }
|
22
25
|
end
|
23
26
|
|
24
27
|
def compute_if_absent(key)
|
25
|
-
synchronize { super }
|
28
|
+
@mutex.synchronize { super }
|
26
29
|
end
|
27
30
|
|
28
31
|
def compute_if_present(key)
|
29
|
-
synchronize { super }
|
32
|
+
@mutex.synchronize { super }
|
30
33
|
end
|
31
34
|
|
32
35
|
def compute(key)
|
33
|
-
synchronize { super }
|
36
|
+
@mutex.synchronize { super }
|
34
37
|
end
|
35
38
|
|
36
39
|
def merge_pair(key, value)
|
37
|
-
synchronize { super }
|
40
|
+
@mutex.synchronize { super }
|
38
41
|
end
|
39
42
|
|
40
43
|
def replace_pair(key, old_value, new_value)
|
41
|
-
synchronize { super }
|
44
|
+
@mutex.synchronize { super }
|
42
45
|
end
|
43
46
|
|
44
47
|
def replace_if_exists(key, new_value)
|
45
|
-
synchronize { super }
|
48
|
+
@mutex.synchronize { super }
|
46
49
|
end
|
47
50
|
|
48
51
|
def get_and_set(key, value)
|
49
|
-
synchronize { super }
|
52
|
+
@mutex.synchronize { super }
|
50
53
|
end
|
51
54
|
|
52
55
|
def key?(key)
|
53
|
-
synchronize { super }
|
56
|
+
@mutex.synchronize { super }
|
54
57
|
end
|
55
58
|
|
56
59
|
def delete(key)
|
57
|
-
synchronize { super }
|
60
|
+
@mutex.synchronize { super }
|
58
61
|
end
|
59
62
|
|
60
63
|
def delete_pair(key, value)
|
61
|
-
synchronize { super }
|
64
|
+
@mutex.synchronize { super }
|
62
65
|
end
|
63
66
|
|
64
67
|
def clear
|
65
|
-
synchronize { super }
|
68
|
+
@mutex.synchronize { super }
|
66
69
|
end
|
67
70
|
|
68
71
|
def size
|
69
|
-
synchronize { super }
|
72
|
+
@mutex.synchronize { super }
|
70
73
|
end
|
71
74
|
|
72
75
|
def get_or_default(key, default_value)
|
73
|
-
synchronize { super }
|
76
|
+
@mutex.synchronize { super }
|
74
77
|
end
|
75
78
|
|
76
79
|
private
|
77
80
|
def dupped_backend
|
78
|
-
synchronize { super }
|
81
|
+
@mutex.synchronize { super }
|
79
82
|
end
|
80
83
|
end
|
81
84
|
end
|
Binary file
|
@@ -39,6 +39,10 @@ module Concurrent
|
|
39
39
|
# The number of tasks that have been completed by the pool since construction.
|
40
40
|
# @return [Integer] The number of tasks that have been completed by the pool since construction.
|
41
41
|
|
42
|
+
# @!macro thread_pool_executor_method_active_count
|
43
|
+
# The number of threads that are actively executing tasks.
|
44
|
+
# @return [Integer] The number of threads that are actively executing tasks.
|
45
|
+
|
42
46
|
# @!macro thread_pool_executor_attr_reader_idletime
|
43
47
|
# The number of seconds that a thread may be idle before being reclaimed.
|
44
48
|
# @return [Integer] The number of seconds that a thread may be idle before being reclaimed.
|
@@ -57,15 +57,11 @@ if Concurrent.on_jruby?
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def ns_shuttingdown?
|
60
|
-
|
61
|
-
@executor.isTerminating
|
62
|
-
else
|
63
|
-
false
|
64
|
-
end
|
60
|
+
@executor.isShutdown && !@executor.isTerminated
|
65
61
|
end
|
66
62
|
|
67
63
|
def ns_shutdown?
|
68
|
-
@executor.
|
64
|
+
@executor.isTerminated
|
69
65
|
end
|
70
66
|
|
71
67
|
class Job
|
@@ -88,10 +84,11 @@ if Concurrent.on_jruby?
|
|
88
84
|
|
89
85
|
def initialize(daemonize = true)
|
90
86
|
@daemonize = daemonize
|
87
|
+
@java_thread_factory = java.util.concurrent.Executors.defaultThreadFactory
|
91
88
|
end
|
92
89
|
|
93
90
|
def newThread(runnable)
|
94
|
-
thread =
|
91
|
+
thread = @java_thread_factory.newThread(runnable)
|
95
92
|
thread.setDaemon(@daemonize)
|
96
93
|
return thread
|
97
94
|
end
|
@@ -73,6 +73,11 @@ if Concurrent.on_jruby?
|
|
73
73
|
@executor.getCompletedTaskCount
|
74
74
|
end
|
75
75
|
|
76
|
+
# @!macro thread_pool_executor_method_active_count
|
77
|
+
def active_count
|
78
|
+
@executor.getActiveCount
|
79
|
+
end
|
80
|
+
|
76
81
|
# @!macro thread_pool_executor_attr_reader_idletime
|
77
82
|
def idletime
|
78
83
|
@executor.getKeepAliveTime(java.util.concurrent.TimeUnit::SECONDS)
|
@@ -61,6 +61,13 @@ module Concurrent
|
|
61
61
|
synchronize { @completed_task_count }
|
62
62
|
end
|
63
63
|
|
64
|
+
# @!macro thread_pool_executor_method_active_count
|
65
|
+
def active_count
|
66
|
+
synchronize do
|
67
|
+
@pool.length - @ready.length
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
64
71
|
# @!macro executor_service_method_can_overflow_question
|
65
72
|
def can_overflow?
|
66
73
|
synchronize { ns_limited_queue? }
|
@@ -3,7 +3,7 @@ require 'concurrent/atomic/event'
|
|
3
3
|
require 'concurrent/collection/non_concurrent_priority_queue'
|
4
4
|
require 'concurrent/executor/executor_service'
|
5
5
|
require 'concurrent/executor/single_thread_executor'
|
6
|
-
|
6
|
+
require 'concurrent/errors'
|
7
7
|
require 'concurrent/options'
|
8
8
|
|
9
9
|
module Concurrent
|
@@ -162,7 +162,11 @@ module Concurrent
|
|
162
162
|
# queue now must have the same pop time, or a closer one, as
|
163
163
|
# when we peeked).
|
164
164
|
task = synchronize { @queue.pop }
|
165
|
-
|
165
|
+
begin
|
166
|
+
task.executor.post { task.process_task }
|
167
|
+
rescue RejectedExecutionError
|
168
|
+
# ignore and continue
|
169
|
+
end
|
166
170
|
else
|
167
171
|
@condition.wait([diff, 60].min)
|
168
172
|
end
|
@@ -15,9 +15,11 @@ module Concurrent
|
|
15
15
|
# @!macro internal_implementation_note
|
16
16
|
HashImplementation = case
|
17
17
|
when Concurrent.on_cruby?
|
18
|
-
# Hash is thread-safe
|
19
|
-
#
|
20
|
-
#
|
18
|
+
# Hash is not fully thread-safe on CRuby, see
|
19
|
+
# https://bugs.ruby-lang.org/issues/19237
|
20
|
+
# https://github.com/ruby/ruby/commit/ffd52412ab
|
21
|
+
# https://github.com/ruby-concurrency/concurrent-ruby/issues/929
|
22
|
+
# So we will need to add synchronization here (similar to Concurrent::Map).
|
21
23
|
::Hash
|
22
24
|
|
23
25
|
when Concurrent.on_jruby?
|
@@ -20,8 +20,8 @@ module Concurrent
|
|
20
20
|
require 'concurrent/collection/map/truffleruby_map_backend'
|
21
21
|
TruffleRubyMapBackend
|
22
22
|
else
|
23
|
-
require 'concurrent/collection/map/
|
24
|
-
|
23
|
+
require 'concurrent/collection/map/synchronized_map_backend'
|
24
|
+
SynchronizedMapBackend
|
25
25
|
end
|
26
26
|
else
|
27
27
|
warn 'Concurrent::Map: unsupported Ruby engine, using a fully synchronized Concurrent::Map implementation'
|
@@ -46,6 +46,12 @@ module Concurrent
|
|
46
46
|
# @note Atomic methods taking a block do not allow the `self` instance
|
47
47
|
# to be used within the block. Doing so will cause a deadlock.
|
48
48
|
|
49
|
+
# @!method []=(key, value)
|
50
|
+
# Set a value with key
|
51
|
+
# @param [Object] key
|
52
|
+
# @param [Object] value
|
53
|
+
# @return [Object] the new value
|
54
|
+
|
49
55
|
# @!method compute_if_absent(key)
|
50
56
|
# Compute and store new value for key if the key is absent.
|
51
57
|
# @param [Object] key
|
@@ -119,41 +125,38 @@ module Concurrent
|
|
119
125
|
# @return [true, false] true if deleted
|
120
126
|
# @!macro map.atomic_method
|
121
127
|
|
122
|
-
#
|
123
|
-
|
124
|
-
if options.kind_of?(::Hash)
|
125
|
-
validate_options_hash!(options)
|
126
|
-
else
|
127
|
-
options = nil
|
128
|
-
end
|
128
|
+
# NonConcurrentMapBackend handles default_proc natively
|
129
|
+
unless defined?(Collection::NonConcurrentMapBackend) and self < Collection::NonConcurrentMapBackend
|
129
130
|
|
130
|
-
|
131
|
-
@default_proc
|
132
|
-
|
131
|
+
# @param [Hash, nil] options options to set the :initial_capacity or :load_factor. Ignored on some Rubies.
|
132
|
+
# @param [Proc] default_proc Optional block to compute the default value if the key is not set, like `Hash#default_proc`
|
133
|
+
def initialize(options = nil, &default_proc)
|
134
|
+
if options.kind_of?(::Hash)
|
135
|
+
validate_options_hash!(options)
|
136
|
+
else
|
137
|
+
options = nil
|
138
|
+
end
|
133
139
|
|
134
|
-
|
135
|
-
|
136
|
-
# @return [Object] the value
|
137
|
-
def [](key)
|
138
|
-
if value = super # non-falsy value is an existing mapping, return it right away
|
139
|
-
value
|
140
|
-
# re-check is done with get_or_default(key, NULL) instead of a simple !key?(key) in order to avoid a race condition, whereby by the time the current thread gets to the key?(key) call
|
141
|
-
# a key => value mapping might have already been created by a different thread (key?(key) would then return true, this elsif branch wouldn't be taken and an incorrent +nil+ value
|
142
|
-
# would be returned)
|
143
|
-
# note: nil == value check is not technically necessary
|
144
|
-
elsif @default_proc && nil == value && NULL == (value = get_or_default(key, NULL))
|
145
|
-
@default_proc.call(self, key)
|
146
|
-
else
|
147
|
-
value
|
140
|
+
super(options)
|
141
|
+
@default_proc = default_proc
|
148
142
|
end
|
149
|
-
end
|
150
143
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
144
|
+
# Get a value with key
|
145
|
+
# @param [Object] key
|
146
|
+
# @return [Object] the value
|
147
|
+
def [](key)
|
148
|
+
if value = super # non-falsy value is an existing mapping, return it right away
|
149
|
+
value
|
150
|
+
# re-check is done with get_or_default(key, NULL) instead of a simple !key?(key) in order to avoid a race condition, whereby by the time the current thread gets to the key?(key) call
|
151
|
+
# a key => value mapping might have already been created by a different thread (key?(key) would then return true, this elsif branch wouldn't be taken and an incorrent +nil+ value
|
152
|
+
# would be returned)
|
153
|
+
# note: nil == value check is not technically necessary
|
154
|
+
elsif @default_proc && nil == value && NULL == (value = get_or_default(key, NULL))
|
155
|
+
@default_proc.call(self, key)
|
156
|
+
else
|
157
|
+
value
|
158
|
+
end
|
159
|
+
end
|
157
160
|
end
|
158
161
|
|
159
162
|
alias_method :get, :[]
|
@@ -5,6 +5,7 @@ require 'concurrent/collection/lock_free_stack'
|
|
5
5
|
require 'concurrent/configuration'
|
6
6
|
require 'concurrent/errors'
|
7
7
|
require 'concurrent/re_include'
|
8
|
+
require 'concurrent/utility/monotonic_time'
|
8
9
|
|
9
10
|
module Concurrent
|
10
11
|
|
@@ -22,7 +23,7 @@ module Concurrent
|
|
22
23
|
#
|
23
24
|
# @!macro promises.param.args
|
24
25
|
# @param [Object] args arguments which are passed to the task when it's executed.
|
25
|
-
# (It might be prepended with other arguments, see the @
|
26
|
+
# (It might be prepended with other arguments, see the @yield section).
|
26
27
|
#
|
27
28
|
# @!macro promises.shortcut.on
|
28
29
|
# Shortcut of {#$0_on} with default `:io` executor supplied.
|
@@ -63,8 +64,8 @@ module Concurrent
|
|
63
64
|
resolvable_event_on default_executor
|
64
65
|
end
|
65
66
|
|
66
|
-
#
|
67
|
-
# {Promises::ResolvableEvent#resolve}.
|
67
|
+
# Creates a resolvable event, user is responsible for resolving the event once
|
68
|
+
# by calling {Promises::ResolvableEvent#resolve}.
|
68
69
|
#
|
69
70
|
# @!macro promises.param.default_executor
|
70
71
|
# @return [ResolvableEvent]
|
@@ -94,7 +95,7 @@ module Concurrent
|
|
94
95
|
future_on(default_executor, *args, &task)
|
95
96
|
end
|
96
97
|
|
97
|
-
# Constructs new Future which will be resolved after block is evaluated on default executor.
|
98
|
+
# Constructs a new Future which will be resolved after block is evaluated on default executor.
|
98
99
|
# Evaluation begins immediately.
|
99
100
|
#
|
100
101
|
# @!macro promises.param.default_executor
|
@@ -106,7 +107,7 @@ module Concurrent
|
|
106
107
|
ImmediateEventPromise.new(default_executor).future.then(*args, &task)
|
107
108
|
end
|
108
109
|
|
109
|
-
# Creates resolved future with will be either fulfilled with the given value or
|
110
|
+
# Creates a resolved future with will be either fulfilled with the given value or rejected with
|
110
111
|
# the given reason.
|
111
112
|
#
|
112
113
|
# @param [true, false] fulfilled
|
@@ -118,7 +119,7 @@ module Concurrent
|
|
118
119
|
ImmediateFuturePromise.new(default_executor, fulfilled, value, reason).future
|
119
120
|
end
|
120
121
|
|
121
|
-
# Creates resolved future
|
122
|
+
# Creates a resolved future which will be fulfilled with the given value.
|
122
123
|
#
|
123
124
|
# @!macro promises.param.default_executor
|
124
125
|
# @param [Object] value
|
@@ -127,7 +128,7 @@ module Concurrent
|
|
127
128
|
resolved_future true, value, nil, default_executor
|
128
129
|
end
|
129
130
|
|
130
|
-
# Creates resolved future
|
131
|
+
# Creates a resolved future which will be rejected with the given reason.
|
131
132
|
#
|
132
133
|
# @!macro promises.param.default_executor
|
133
134
|
# @param [Object] reason
|
@@ -190,7 +191,7 @@ module Concurrent
|
|
190
191
|
delay_on default_executor, *args, &task
|
191
192
|
end
|
192
193
|
|
193
|
-
# Creates new event or future which is resolved only after it is touched,
|
194
|
+
# Creates a new event or future which is resolved only after it is touched,
|
194
195
|
# see {Concurrent::AbstractEventFuture#touch}.
|
195
196
|
#
|
196
197
|
# @!macro promises.param.default_executor
|
@@ -214,7 +215,7 @@ module Concurrent
|
|
214
215
|
schedule_on default_executor, intended_time, *args, &task
|
215
216
|
end
|
216
217
|
|
217
|
-
# Creates new event or future which is resolved in intended_time.
|
218
|
+
# Creates a new event or future which is resolved in intended_time.
|
218
219
|
#
|
219
220
|
# @!macro promises.param.default_executor
|
220
221
|
# @!macro promises.param.intended_time
|
@@ -240,8 +241,8 @@ module Concurrent
|
|
240
241
|
zip_futures_on default_executor, *futures_and_or_events
|
241
242
|
end
|
242
243
|
|
243
|
-
# Creates new future which is resolved after all futures_and_or_events are resolved.
|
244
|
-
# Its value is array of zipped future values. Its reason is array of reasons for rejection.
|
244
|
+
# Creates a new future which is resolved after all futures_and_or_events are resolved.
|
245
|
+
# Its value is an array of zipped future values. Its reason is an array of reasons for rejection.
|
245
246
|
# If there is an error it rejects.
|
246
247
|
# @!macro promises.event-conversion
|
247
248
|
# If event is supplied, which does not have value and can be only resolved, it's
|
@@ -262,7 +263,7 @@ module Concurrent
|
|
262
263
|
zip_events_on default_executor, *futures_and_or_events
|
263
264
|
end
|
264
265
|
|
265
|
-
# Creates new event which is resolved after all futures_and_or_events are resolved.
|
266
|
+
# Creates a new event which is resolved after all futures_and_or_events are resolved.
|
266
267
|
# (Future is resolved when fulfilled or rejected.)
|
267
268
|
#
|
268
269
|
# @!macro promises.param.default_executor
|
@@ -280,8 +281,8 @@ module Concurrent
|
|
280
281
|
|
281
282
|
alias_method :any, :any_resolved_future
|
282
283
|
|
283
|
-
# Creates new future which is resolved after first futures_and_or_events is resolved.
|
284
|
-
# Its result equals result of the first resolved future.
|
284
|
+
# Creates a new future which is resolved after the first futures_and_or_events is resolved.
|
285
|
+
# Its result equals the result of the first resolved future.
|
285
286
|
# @!macro promises.any-touch
|
286
287
|
# If resolved it does not propagate {Concurrent::AbstractEventFuture#touch}, leaving delayed
|
287
288
|
# futures un-executed if they are not required any more.
|
@@ -300,9 +301,9 @@ module Concurrent
|
|
300
301
|
any_fulfilled_future_on default_executor, *futures_and_or_events
|
301
302
|
end
|
302
303
|
|
303
|
-
# Creates new future which is resolved after first
|
304
|
-
# Its result equals result of the first resolved future or if all futures_and_or_events reject,
|
305
|
-
# it has reason of the last
|
304
|
+
# Creates a new future which is resolved after the first futures_and_or_events is fulfilled.
|
305
|
+
# Its result equals the result of the first resolved future or if all futures_and_or_events reject,
|
306
|
+
# it has reason of the last rejected future.
|
306
307
|
# @!macro promises.any-touch
|
307
308
|
# @!macro promises.event-conversion
|
308
309
|
#
|
@@ -319,7 +320,7 @@ module Concurrent
|
|
319
320
|
any_event_on default_executor, *futures_and_or_events
|
320
321
|
end
|
321
322
|
|
322
|
-
# Creates new event which becomes resolved after first
|
323
|
+
# Creates a new event which becomes resolved after the first futures_and_or_events resolves.
|
323
324
|
# @!macro promises.any-touch
|
324
325
|
#
|
325
326
|
# @!macro promises.param.default_executor
|
@@ -611,7 +612,7 @@ module Concurrent
|
|
611
612
|
# @yieldparam [Object] value
|
612
613
|
# @yieldparam [Object] reason
|
613
614
|
def chain_on(executor, *args, &task)
|
614
|
-
ChainPromise.new_blocked_by1(self,
|
615
|
+
ChainPromise.new_blocked_by1(self, executor, executor, args, &task).future
|
615
616
|
end
|
616
617
|
|
617
618
|
# @return [String] Short string representation.
|
@@ -772,8 +773,17 @@ module Concurrent
|
|
772
773
|
@Lock.synchronize do
|
773
774
|
@Waiters.increment
|
774
775
|
begin
|
775
|
-
|
776
|
-
|
776
|
+
if timeout
|
777
|
+
start = Concurrent.monotonic_time
|
778
|
+
until resolved?
|
779
|
+
break if @Condition.wait(@Lock, timeout) == nil # nil means timeout
|
780
|
+
timeout -= (Concurrent.monotonic_time - start)
|
781
|
+
break if timeout <= 0
|
782
|
+
end
|
783
|
+
else
|
784
|
+
until resolved?
|
785
|
+
@Condition.wait(@Lock, timeout)
|
786
|
+
end
|
777
787
|
end
|
778
788
|
ensure
|
779
789
|
# JRuby may raise ConcurrencyError
|
@@ -1034,7 +1044,7 @@ module Concurrent
|
|
1034
1044
|
# @return [Future]
|
1035
1045
|
# @yield [value, *args] to the task.
|
1036
1046
|
def then_on(executor, *args, &task)
|
1037
|
-
ThenPromise.new_blocked_by1(self,
|
1047
|
+
ThenPromise.new_blocked_by1(self, executor, executor, args, &task).future
|
1038
1048
|
end
|
1039
1049
|
|
1040
1050
|
# @!macro promises.shortcut.on
|
@@ -1052,7 +1062,7 @@ module Concurrent
|
|
1052
1062
|
# @return [Future]
|
1053
1063
|
# @yield [reason, *args] to the task.
|
1054
1064
|
def rescue_on(executor, *args, &task)
|
1055
|
-
RescuePromise.new_blocked_by1(self,
|
1065
|
+
RescuePromise.new_blocked_by1(self, executor, executor, args, &task).future
|
1056
1066
|
end
|
1057
1067
|
|
1058
1068
|
# @!macro promises.method.zip
|