concurrent-ruby 1.1.0.pre2 → 1.1.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 +21 -0
- data/Gemfile +2 -2
- data/README.md +6 -2
- data/Rakefile +51 -16
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +13 -11
- data/lib/concurrent.rb +2 -0
- data/lib/concurrent/atomic/atomic_reference.rb +11 -1
- data/lib/concurrent/atomic/java_count_down_latch.rb +4 -2
- data/lib/concurrent/atomic/mutex_atomic_boolean.rb +2 -0
- data/lib/concurrent/concurrent_ruby.jar +0 -0
- data/lib/concurrent/exchanger.rb +10 -6
- data/lib/concurrent/map.rb +159 -57
- data/lib/concurrent/thread_safe/util/data_structures.rb +10 -2
- data/lib/concurrent/utility/193.rb +17 -0
- data/lib/concurrent/utility/engine.rb +2 -2
- data/lib/concurrent/version.rb +2 -2
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8d042c846e692138ba0eea6efbabb53794abbaae207477dc5e549abac9fab266
|
4
|
+
data.tar.gz: 34678c0ec6d519d5beac0b12a8c6116d4eb1ff29dd620ba03df851d086b04adc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9b75559855ce216316a81bb68a9fcb1e74dceed4baa8e816d2e02cd12af50745e88f9afba7470681a8f47297a297fe9fd79e82ace4b5f63410197469eab45523
|
7
|
+
data.tar.gz: 79e63eb2ef9ab15e9fd5d1d777ae027e68b399ec885d85bc8156f9edad741ecf4eb20fc063357068018ef932c18e7263466ef64cee7592bf50b39433e83b94a7
|
data/CHANGELOG.md
CHANGED
@@ -1,9 +1,30 @@
|
|
1
1
|
## Current
|
2
2
|
|
3
|
+
## Release v1.1.1, edge v0.4.1 (1 Nov 2018)
|
4
|
+
|
5
|
+
* (#768) add support for 1.9.3 back
|
6
|
+
|
7
|
+
## Release v1.1.0, edge v0.4.0 (31 OCt 2018) (yanked)
|
8
|
+
|
9
|
+
* (#768) yanked because of issues with removed 1.9.3 support
|
10
|
+
|
11
|
+
## Release v1.1.0.pre2, edge v0.4.0.pre2 (18 Sep 2018)
|
12
|
+
|
13
|
+
concurrent-ruby:
|
14
|
+
|
15
|
+
* fixed documentation and README links
|
16
|
+
* fix Set for TruffleRuby and Rubinius
|
17
|
+
* use properly supported TruffleRuby APIs
|
18
|
+
|
19
|
+
concurrent-ruby-edge:
|
20
|
+
|
21
|
+
* add Promises.zip_futures_over_on
|
22
|
+
|
3
23
|
## Release v1.1.0.pre1, edge v0.4.0.pre1 (15 Aug 2018)
|
4
24
|
|
5
25
|
concurrent-ruby:
|
6
26
|
|
27
|
+
* requires at least Ruby 2.0
|
7
28
|
* [Promises](http://ruby-concurrency.github.io/concurrent-ruby/1.1.0/Concurrent/Promises.html)
|
8
29
|
are moved from `concurrent-ruby-edge` to `concurrent-ruby`
|
9
30
|
* Add support for TruffleRuby
|
data/Gemfile
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
|
3
|
+
require File.join(File.dirname(__FILE__ ), 'lib/concurrent/version')
|
4
4
|
|
5
5
|
no_path = ENV['NO_PATH']
|
6
6
|
options = no_path ? {} : { path: '.' }
|
@@ -19,7 +19,7 @@ end
|
|
19
19
|
group :documentation, optional: true do
|
20
20
|
gem 'yard', '~> 0.9.0', :require => false
|
21
21
|
gem 'redcarpet', '~> 3.0', platforms: :mri # understands github markdown
|
22
|
-
gem 'md-ruby-eval', '~> 0.
|
22
|
+
gem 'md-ruby-eval', '~> 0.3'
|
23
23
|
end
|
24
24
|
|
25
25
|
group :testing do
|
data/README.md
CHANGED
@@ -227,8 +227,12 @@ be obeyed though. Features developed in `concurrent-ruby-edge` are expected to m
|
|
227
227
|
|
228
228
|
## Supported Ruby versions
|
229
229
|
|
230
|
-
MRI 2.0 and above
|
231
|
-
|
230
|
+
* MRI 2.0 and above
|
231
|
+
* JRuby 9000
|
232
|
+
* TruffleRuby are supported.
|
233
|
+
* Any Ruby interpreter that is compliant with Ruby 2.0 or newer.
|
234
|
+
|
235
|
+
Actually we still support mri 1.9.3 and jruby 1.7.27 but we are looking at ways how to drop the support.
|
232
236
|
Java 8 is preferred for JRuby but every Java version on which JRuby 9000 runs is supported.
|
233
237
|
|
234
238
|
The legacy support for Rubinius is kept but it is no longer maintained, if you would like to help
|
data/Rakefile
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require_relative 'lib/concurrent/version'
|
4
4
|
require_relative 'lib/concurrent/utility/engine'
|
5
|
+
require_relative 'lib/concurrent/utility/193'
|
5
6
|
|
6
7
|
core_gemspec = Gem::Specification.load File.join(__dir__, 'concurrent-ruby.gemspec')
|
7
8
|
ext_gemspec = Gem::Specification.load File.join(__dir__, 'concurrent-ruby-ext.gemspec')
|
@@ -9,7 +10,29 @@ edge_gemspec = Gem::Specification.load File.join(__dir__, 'concurrent-ruby-edge.
|
|
9
10
|
|
10
11
|
require 'rake/javaextensiontask'
|
11
12
|
|
12
|
-
|
13
|
+
JRUBY_JAR_PATH = '/usr/local/opt/rbenv/versions/jruby-9.1.17.0/lib/jruby.jar'
|
14
|
+
|
15
|
+
class ConcurrentRubyJavaExtensionTask < Rake::JavaExtensionTask
|
16
|
+
def java_classpath_arg(*args)
|
17
|
+
jruby_cpath = nil
|
18
|
+
if RUBY_PLATFORM =~ /java/
|
19
|
+
begin
|
20
|
+
cpath = Java::java.lang.System.getProperty('java.class.path').split(File::PATH_SEPARATOR)
|
21
|
+
cpath += Java::java.lang.System.getProperty('sun.boot.class.path').split(File::PATH_SEPARATOR)
|
22
|
+
jruby_cpath = cpath.compact.join(File::PATH_SEPARATOR)
|
23
|
+
rescue => e
|
24
|
+
end
|
25
|
+
end
|
26
|
+
unless jruby_cpath
|
27
|
+
jruby_cpath = JRUBY_JAR_PATH
|
28
|
+
raise "#{jruby_cpath} does not exist" unless File.exist? jruby_cpath
|
29
|
+
end
|
30
|
+
jruby_cpath += File::PATH_SEPARATOR + args.join(File::PATH_SEPARATOR) unless args.empty?
|
31
|
+
jruby_cpath ? "-cp \"#{jruby_cpath}\"" : ""
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
ConcurrentRubyJavaExtensionTask.new('concurrent_ruby', core_gemspec) do |ext|
|
13
36
|
ext.ext_dir = 'ext/concurrent-ruby'
|
14
37
|
ext.lib_dir = 'lib/concurrent'
|
15
38
|
end
|
@@ -70,9 +93,9 @@ begin
|
|
70
93
|
desc '* test packaged and installed gems instead of local files'
|
71
94
|
task :installed do
|
72
95
|
Dir.chdir(__dir__) do
|
73
|
-
sh
|
74
|
-
sh
|
75
|
-
sh
|
96
|
+
sh "gem install pkg/concurrent-ruby-#{Concurrent::VERSION}.gem"
|
97
|
+
sh "gem install pkg/concurrent-ruby-ext-#{Concurrent::VERSION}.gem" if Concurrent.on_cruby?
|
98
|
+
sh "gem install pkg/concurrent-ruby-edge-#{Concurrent::EDGE_VERSION}.gem"
|
76
99
|
ENV['NO_PATH'] = 'true'
|
77
100
|
sh 'bundle update'
|
78
101
|
sh 'bundle exec rake spec:ci'
|
@@ -132,6 +155,7 @@ begin
|
|
132
155
|
"{Concurrent::#{$1} #{$1}}"
|
133
156
|
end
|
134
157
|
end
|
158
|
+
FileUtils.mkpath 'tmp'
|
135
159
|
File.write 'tmp/README.md', content
|
136
160
|
end
|
137
161
|
end
|
@@ -199,18 +223,29 @@ namespace :release do
|
|
199
223
|
# Depends on environment of @pitr-ch
|
200
224
|
|
201
225
|
mri_version = '2.5.1'
|
202
|
-
jruby_version = 'jruby-9.1.17.
|
226
|
+
jruby_version = 'jruby-9.1.17.1'
|
203
227
|
|
204
228
|
task :checks => "yard:#{current_yard_version_name}:uptodate" do
|
205
229
|
Dir.chdir(__dir__) do
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
230
|
+
sh 'test -z "$(git status --porcelain)"' do |ok, res|
|
231
|
+
unless ok
|
232
|
+
begin
|
233
|
+
STDOUT.puts 'Command failed. Continue? (y/n)'
|
234
|
+
input = STDIN.gets.strip.downcase
|
235
|
+
end until %w(y n).include?(input)
|
236
|
+
exit 1 if input == 'n'
|
237
|
+
end
|
238
|
+
end
|
239
|
+
sh 'git fetch'
|
240
|
+
sh 'test $(git show-ref --verify --hash refs/heads/master) = ' +
|
241
|
+
'$(git show-ref --verify --hash refs/remotes/origin/master)' do |ok, res|
|
242
|
+
unless ok
|
243
|
+
begin
|
244
|
+
STDOUT.puts 'Command failed. Continue? (y/n)'
|
245
|
+
input = STDIN.gets.strip.downcase
|
246
|
+
end until %w(y n).include?(input)
|
247
|
+
exit 1 if input == 'n'
|
248
|
+
end
|
214
249
|
end
|
215
250
|
end
|
216
251
|
end
|
@@ -243,10 +278,10 @@ namespace :release do
|
|
243
278
|
namespace :publish do
|
244
279
|
task :ask do
|
245
280
|
begin
|
246
|
-
STDOUT.puts
|
281
|
+
STDOUT.puts 'Do you want to publish? (y/n)'
|
247
282
|
input = STDIN.gets.strip.downcase
|
248
283
|
end until %w(y n).include?(input)
|
249
|
-
|
284
|
+
exit 1 if input == 'n'
|
250
285
|
end
|
251
286
|
|
252
287
|
desc '** tag HEAD with current version and push to github'
|
@@ -254,7 +289,7 @@ namespace :release do
|
|
254
289
|
Dir.chdir(__dir__) do
|
255
290
|
sh "git tag v#{Concurrent::VERSION}"
|
256
291
|
sh "git tag edge-v#{Concurrent::EDGE_VERSION}"
|
257
|
-
sh "git push
|
292
|
+
sh "git push origin v#{Concurrent::VERSION} edge-v#{Concurrent::EDGE_VERSION}"
|
258
293
|
end
|
259
294
|
end
|
260
295
|
|
@@ -284,21 +284,23 @@ public class SynchronizationLibrary implements Library {
|
|
284
284
|
}
|
285
285
|
|
286
286
|
@JRubyMethod(name = "sleep_interruptibly", visibility = Visibility.PUBLIC, module = true)
|
287
|
-
public static IRubyObject sleepInterruptibly(ThreadContext context, IRubyObject receiver, Block block) {
|
287
|
+
public static IRubyObject sleepInterruptibly(final ThreadContext context, IRubyObject receiver, final Block block) {
|
288
288
|
try {
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
289
|
+
context.getThread().executeBlockingTask(new RubyThread.BlockingTask() {
|
290
|
+
@Override
|
291
|
+
public void run() throws InterruptedException {
|
292
|
+
block.call(context);
|
293
|
+
}
|
294
|
+
|
295
|
+
@Override
|
296
|
+
public void wakeup() {
|
297
|
+
context.getThread().getNativeThread().interrupt();
|
298
|
+
}
|
299
|
+
});
|
299
300
|
} catch (InterruptedException e) {
|
300
301
|
throw context.runtime.newThreadError("interrupted in Concurrent::Synchronization::JRuby.sleep_interruptibly");
|
301
302
|
}
|
303
|
+
return context.nil;
|
302
304
|
}
|
303
305
|
}
|
304
306
|
}
|
data/lib/concurrent.rb
CHANGED
@@ -2,6 +2,14 @@ require 'concurrent/synchronization'
|
|
2
2
|
require 'concurrent/utility/engine'
|
3
3
|
require 'concurrent/atomic_reference/numeric_cas_wrapper'
|
4
4
|
|
5
|
+
# Shim for TruffleRuby::AtomicReference
|
6
|
+
if Concurrent.on_truffleruby? && !defined?(TruffleRuby::AtomicReference)
|
7
|
+
# @!visibility private
|
8
|
+
module TruffleRuby
|
9
|
+
AtomicReference = Truffle::AtomicReference
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
5
13
|
module Concurrent
|
6
14
|
|
7
15
|
# Define update methods that use direct paths
|
@@ -155,8 +163,10 @@ module Concurrent
|
|
155
163
|
end
|
156
164
|
JavaAtomicReference
|
157
165
|
when Concurrent.on_truffleruby?
|
158
|
-
class TruffleRubyAtomicReference <
|
166
|
+
class TruffleRubyAtomicReference < TruffleRuby::AtomicReference
|
159
167
|
include AtomicDirectUpdate
|
168
|
+
alias_method :value, :get
|
169
|
+
alias_method :value=, :set
|
160
170
|
alias_method :compare_and_swap, :compare_and_set
|
161
171
|
alias_method :swap, :get_and_set
|
162
172
|
end
|
@@ -16,14 +16,16 @@ if Concurrent.on_jruby?
|
|
16
16
|
|
17
17
|
# @!macro count_down_latch_method_wait
|
18
18
|
def wait(timeout = nil)
|
19
|
+
result = nil
|
19
20
|
if timeout.nil?
|
20
21
|
Synchronization::JRuby.sleep_interruptibly { @latch.await }
|
21
|
-
true
|
22
|
+
result = true
|
22
23
|
else
|
23
24
|
Synchronization::JRuby.sleep_interruptibly do
|
24
|
-
@latch.await(1000 * timeout, java.util.concurrent.TimeUnit::MILLISECONDS)
|
25
|
+
result = @latch.await(1000 * timeout, java.util.concurrent.TimeUnit::MILLISECONDS)
|
25
26
|
end
|
26
27
|
end
|
28
|
+
result
|
27
29
|
end
|
28
30
|
|
29
31
|
# @!macro count_down_latch_method_count_down
|
Binary file
|
data/lib/concurrent/exchanger.rb
CHANGED
@@ -141,8 +141,8 @@ module Concurrent
|
|
141
141
|
|
142
142
|
def initialize(item)
|
143
143
|
super()
|
144
|
-
@Item
|
145
|
-
@Latch
|
144
|
+
@Item = item
|
145
|
+
@Latch = Concurrent::CountDownLatch.new
|
146
146
|
self.value = nil
|
147
147
|
end
|
148
148
|
|
@@ -252,8 +252,8 @@ module Concurrent
|
|
252
252
|
# - Wake the sleeping occupier
|
253
253
|
# - Return the occupier's item
|
254
254
|
|
255
|
-
value
|
256
|
-
me
|
255
|
+
value = NULL if value.nil? # The sentinel allows nil to be a valid value
|
256
|
+
me = Node.new(value) # create my node in case I need to occupy
|
257
257
|
end_at = Concurrent.monotonic_time + timeout.to_f # The time to give up
|
258
258
|
|
259
259
|
result = loop do
|
@@ -304,13 +304,17 @@ module Concurrent
|
|
304
304
|
#
|
305
305
|
# @return [Object, CANCEL] the value exchanged by the other thread; {CANCEL} on timeout
|
306
306
|
def do_exchange(value, timeout)
|
307
|
+
result = nil
|
307
308
|
if timeout.nil?
|
308
|
-
Synchronization::JRuby.sleep_interruptibly
|
309
|
+
Synchronization::JRuby.sleep_interruptibly do
|
310
|
+
result = @exchanger.exchange(value)
|
311
|
+
end
|
309
312
|
else
|
310
313
|
Synchronization::JRuby.sleep_interruptibly do
|
311
|
-
@exchanger.exchange(value, 1000 * timeout, java.util.concurrent.TimeUnit::MILLISECONDS)
|
314
|
+
result = @exchanger.exchange(value, 1000 * timeout, java.util.concurrent.TimeUnit::MILLISECONDS)
|
312
315
|
end
|
313
316
|
end
|
317
|
+
result
|
314
318
|
rescue java.util.concurrent.TimeoutException
|
315
319
|
CANCEL
|
316
320
|
end
|
data/lib/concurrent/map.rb
CHANGED
@@ -31,46 +31,89 @@ module Concurrent
|
|
31
31
|
# -- for instance, it does not necessarily retain ordering by insertion time as `Hash`
|
32
32
|
# does. For most uses it should do fine though, and we recommend you consider
|
33
33
|
# `Concurrent::Map` instead of `Concurrent::Hash` for your concurrency-safe hash needs.
|
34
|
-
#
|
35
|
-
# > require 'concurrent'
|
36
|
-
# >
|
37
|
-
# > map = Concurrent::Map.new
|
38
34
|
class Map < Collection::MapImplementation
|
39
35
|
|
40
|
-
# @!macro
|
41
|
-
# This method is atomic.
|
42
|
-
|
43
|
-
#
|
36
|
+
# @!macro map.atomic_method
|
37
|
+
# This method is atomic.
|
38
|
+
|
39
|
+
# @!macro map.atomic_method_with_block
|
40
|
+
# This method is atomic.
|
41
|
+
# @note Atomic methods taking a block do not allow the `self` instance
|
42
|
+
# to be used within the block. Doing so will cause a deadlock.
|
43
|
+
|
44
|
+
# @!method compute_if_absent(key)
|
45
|
+
# Compute and store new value for key if the key is absent.
|
46
|
+
# @param [Object] key
|
47
|
+
# @yield new value
|
48
|
+
# @yieldreturn [Object] new value
|
49
|
+
# @return [Object] new value or current value
|
50
|
+
# @!macro map.atomic_method_with_block
|
51
|
+
|
52
|
+
# @!method compute_if_present(key)
|
53
|
+
# Compute and store new value for key if the key is present.
|
54
|
+
# @param [Object] key
|
55
|
+
# @yield new value
|
56
|
+
# @yieldparam old_value [Object]
|
57
|
+
# @yieldreturn [Object, nil] new value, when nil the key is removed
|
58
|
+
# @return [Object, nil] new value or nil
|
59
|
+
# @!macro map.atomic_method_with_block
|
60
|
+
|
61
|
+
# @!method compute(key)
|
62
|
+
# Compute and store new value for key.
|
63
|
+
# @param [Object] key
|
64
|
+
# @yield compute new value from old one
|
65
|
+
# @yieldparam old_value [Object, nil] old_value, or nil when key is absent
|
66
|
+
# @yieldreturn [Object, nil] new value, when nil the key is removed
|
67
|
+
# @return [Object, nil] new value or nil
|
68
|
+
# @!macro map.atomic_method_with_block
|
69
|
+
|
70
|
+
# @!method merge_pair(key, value)
|
71
|
+
# If the key is absent, the value is stored, otherwise new value is
|
72
|
+
# computed with a block.
|
73
|
+
# @param [Object] key
|
74
|
+
# @param [Object] value
|
75
|
+
# @yield compute new value from old one
|
76
|
+
# @yieldparam old_value [Object] old value
|
77
|
+
# @yieldreturn [Object, nil] new value, when nil the key is removed
|
78
|
+
# @return [Object, nil] new value or nil
|
79
|
+
# @!macro map.atomic_method_with_block
|
80
|
+
|
81
|
+
# @!method replace_pair(key, old_value, new_value)
|
82
|
+
# Replaces old_value with new_value if key exists and current value
|
83
|
+
# matches old_value
|
84
|
+
# @param [Object] key
|
85
|
+
# @param [Object] old_value
|
86
|
+
# @param [Object] new_value
|
87
|
+
# @return [true, false] true if replaced
|
88
|
+
# @!macro map.atomic_method
|
89
|
+
|
90
|
+
# @!method replace_if_exists(key, new_value)
|
91
|
+
# Replaces current value with new_value if key exists
|
92
|
+
# @param [Object] key
|
93
|
+
# @param [Object] new_value
|
94
|
+
# @return [Object, nil] old value or nil
|
95
|
+
# @!macro map.atomic_method
|
96
|
+
|
97
|
+
# @!method get_and_set(key, value)
|
98
|
+
# Get the current value under key and set new value.
|
99
|
+
# @param [Object] key
|
100
|
+
# @param [Object] value
|
101
|
+
# @return [Object, nil] old value or nil when the key was absent
|
102
|
+
# @!macro map.atomic_method
|
103
|
+
|
104
|
+
# @!method delete(key)
|
105
|
+
# Delete key and its value.
|
106
|
+
# @param [Object] key
|
107
|
+
# @return [Object, nil] old value or nil when the key was absent
|
108
|
+
# @!macro map.atomic_method
|
109
|
+
|
110
|
+
# @!method delete_pair(key, value)
|
111
|
+
# Delete pair and its value if current value equals the provided value.
|
112
|
+
# @param [Object] key
|
113
|
+
# @param [Object] value
|
114
|
+
# @return [true, false] true if deleted
|
115
|
+
# @!macro map.atomic_method
|
44
116
|
|
45
|
-
# @!method put_if_absent
|
46
|
-
# @!macro map_method_is_atomic
|
47
|
-
|
48
|
-
# @!method compute_if_absent
|
49
|
-
# @!macro map_method_is_atomic
|
50
|
-
|
51
|
-
# @!method compute_if_present
|
52
|
-
# @!macro map_method_is_atomic
|
53
|
-
|
54
|
-
# @!method compute
|
55
|
-
# @!macro map_method_is_atomic
|
56
|
-
|
57
|
-
# @!method merge_pair
|
58
|
-
# @!macro map_method_is_atomic
|
59
|
-
|
60
|
-
# @!method replace_pair
|
61
|
-
# @!macro map_method_is_atomic
|
62
|
-
|
63
|
-
# @!method replace_if_exists
|
64
|
-
# @!macro map_method_is_atomic
|
65
|
-
|
66
|
-
# @!method get_and_set
|
67
|
-
# @!macro map_method_is_atomic
|
68
|
-
|
69
|
-
# @!method delete
|
70
|
-
# @!macro map_method_is_atomic
|
71
|
-
|
72
|
-
# @!method delete_pair
|
73
|
-
# @!macro map_method_is_atomic
|
74
117
|
|
75
118
|
def initialize(options = nil, &block)
|
76
119
|
if options.kind_of?(::Hash)
|
@@ -83,6 +126,9 @@ module Concurrent
|
|
83
126
|
@default_proc = block
|
84
127
|
end
|
85
128
|
|
129
|
+
# Get a value with key
|
130
|
+
# @param [Object] key
|
131
|
+
# @return [Object] the value
|
86
132
|
def [](key)
|
87
133
|
if value = super # non-falsy value is an existing mapping, return it right away
|
88
134
|
value
|
@@ -98,17 +144,27 @@ module Concurrent
|
|
98
144
|
end
|
99
145
|
|
100
146
|
alias_method :get, :[]
|
147
|
+
# TODO (pitr-ch 30-Oct-2018): doc
|
101
148
|
alias_method :put, :[]=
|
102
149
|
|
150
|
+
# Get a value with key, or default_value when key is absent,
|
151
|
+
# or fail when no default_value is given.
|
152
|
+
# @param [Object] key
|
153
|
+
# @param [Object] default_value
|
154
|
+
# @yield default value for a key
|
155
|
+
# @yieldparam key [Object]
|
156
|
+
# @yieldreturn [Object] default value
|
157
|
+
# @return [Object] the value or default value
|
158
|
+
# @raise [KeyError] when key is missing and no default_value is provided
|
103
159
|
# @!macro map_method_not_atomic
|
104
|
-
# The "fetch-then-act" methods of `Map` are not atomic. `Map` is intended
|
105
|
-
#
|
106
|
-
#
|
107
|
-
#
|
108
|
-
#
|
109
|
-
#
|
110
|
-
#
|
111
|
-
#
|
160
|
+
# @note The "fetch-then-act" methods of `Map` are not atomic. `Map` is intended
|
161
|
+
# to be use as a concurrency primitive with strong happens-before
|
162
|
+
# guarantees. It is not intended to be used as a high-level abstraction
|
163
|
+
# supporting complex operations. All read and write operations are
|
164
|
+
# thread safe, but no guarantees are made regarding race conditions
|
165
|
+
# between the fetch operation and yielding to the block. Additionally,
|
166
|
+
# this method does not support recursion. This is due to internal
|
167
|
+
# constraints that are very unlikely to change in the near future.
|
112
168
|
def fetch(key, default_value = NULL)
|
113
169
|
if NULL != (value = get_or_default(key, NULL))
|
114
170
|
value
|
@@ -121,23 +177,39 @@ module Concurrent
|
|
121
177
|
end
|
122
178
|
end
|
123
179
|
|
124
|
-
#
|
180
|
+
# Fetch value with key, or store default value when key is absent,
|
181
|
+
# or fail when no default_value is given. This is a two step operation,
|
182
|
+
# therefore not atomic. The store can overwrite other concurrently
|
183
|
+
# stored value.
|
184
|
+
# @param [Object] key
|
185
|
+
# @param [Object] default_value
|
186
|
+
# @yield default value for a key
|
187
|
+
# @yieldparam key [Object]
|
188
|
+
# @yieldreturn [Object] default value
|
189
|
+
# @return [Object] the value or default value
|
190
|
+
# @!macro map.atomic_method_with_block
|
125
191
|
def fetch_or_store(key, default_value = NULL)
|
126
192
|
fetch(key) do
|
127
193
|
put(key, block_given? ? yield(key) : (NULL == default_value ? raise_fetch_no_key : default_value))
|
128
194
|
end
|
129
195
|
end
|
130
196
|
|
131
|
-
#
|
197
|
+
# Insert value into map with key if key is absent in one atomic step.
|
198
|
+
# @param [Object] key
|
199
|
+
# @param [Object] value
|
200
|
+
# @return [Object, nil] the value or nil when key was present
|
132
201
|
def put_if_absent(key, value)
|
133
202
|
computed = false
|
134
|
-
result
|
203
|
+
result = compute_if_absent(key) do
|
135
204
|
computed = true
|
136
205
|
value
|
137
206
|
end
|
138
207
|
computed ? nil : result
|
139
208
|
end unless method_defined?(:put_if_absent)
|
140
209
|
|
210
|
+
# Is the value stored in the map. Iterates over all values.
|
211
|
+
# @param [Object] value
|
212
|
+
# @return [true, false]
|
141
213
|
def value?(value)
|
142
214
|
each_value do |v|
|
143
215
|
return true if value.equal?(v)
|
@@ -145,26 +217,46 @@ module Concurrent
|
|
145
217
|
false
|
146
218
|
end
|
147
219
|
|
220
|
+
# All keys
|
221
|
+
# @return [::Array<Object>] keys
|
148
222
|
def keys
|
149
223
|
arr = []
|
150
|
-
each_pair {|k, v| arr << k}
|
224
|
+
each_pair { |k, v| arr << k }
|
151
225
|
arr
|
152
226
|
end unless method_defined?(:keys)
|
153
227
|
|
228
|
+
# All values
|
229
|
+
# @return [::Array<Object>] values
|
154
230
|
def values
|
155
231
|
arr = []
|
156
|
-
each_pair {|k, v| arr << v}
|
232
|
+
each_pair { |k, v| arr << v }
|
157
233
|
arr
|
158
234
|
end unless method_defined?(:values)
|
159
235
|
|
236
|
+
# Iterates over each key.
|
237
|
+
# @yield for each key in the map
|
238
|
+
# @yieldparam key [Object]
|
239
|
+
# @return [self]
|
240
|
+
# @!macro map.atomic_method_with_block
|
160
241
|
def each_key
|
161
|
-
each_pair {|k, v| yield k}
|
242
|
+
each_pair { |k, v| yield k }
|
162
243
|
end unless method_defined?(:each_key)
|
163
244
|
|
245
|
+
# Iterates over each value.
|
246
|
+
# @yield for each value in the map
|
247
|
+
# @yieldparam value [Object]
|
248
|
+
# @return [self]
|
249
|
+
# @!macro map.atomic_method_with_block
|
164
250
|
def each_value
|
165
|
-
each_pair {|k, v| yield v}
|
251
|
+
each_pair { |k, v| yield v }
|
166
252
|
end unless method_defined?(:each_value)
|
167
253
|
|
254
|
+
# Iterates over each key value pair.
|
255
|
+
# @yield for each key value pair in the map
|
256
|
+
# @yieldparam key [Object]
|
257
|
+
# @yieldparam value [Object]
|
258
|
+
# @return [self]
|
259
|
+
# @!macro map.atomic_method_with_block
|
168
260
|
def each_pair
|
169
261
|
return enum_for :each_pair unless block_given?
|
170
262
|
super
|
@@ -172,30 +264,39 @@ module Concurrent
|
|
172
264
|
|
173
265
|
alias_method :each, :each_pair unless method_defined?(:each)
|
174
266
|
|
267
|
+
# Find key of a value.
|
268
|
+
# @param [Object] value
|
269
|
+
# @return [Object, nil] key or nil when not found
|
175
270
|
def key(value)
|
176
|
-
each_pair {|k, v| return k if v == value}
|
271
|
+
each_pair { |k, v| return k if v == value }
|
177
272
|
nil
|
178
273
|
end unless method_defined?(:key)
|
179
274
|
alias_method :index, :key if RUBY_VERSION < '1.9'
|
180
275
|
|
276
|
+
# Is map empty?
|
277
|
+
# @return [true, false]
|
181
278
|
def empty?
|
182
|
-
each_pair {|k, v| return false}
|
279
|
+
each_pair { |k, v| return false }
|
183
280
|
true
|
184
281
|
end unless method_defined?(:empty?)
|
185
282
|
|
283
|
+
# The size of map.
|
284
|
+
# @return [Integer] size
|
186
285
|
def size
|
187
286
|
count = 0
|
188
|
-
each_pair {|k, v| count += 1}
|
287
|
+
each_pair { |k, v| count += 1 }
|
189
288
|
count
|
190
289
|
end unless method_defined?(:size)
|
191
290
|
|
291
|
+
# @!visibility private
|
192
292
|
def marshal_dump
|
193
293
|
raise TypeError, "can't dump hash with default proc" if @default_proc
|
194
294
|
h = {}
|
195
|
-
each_pair {|k, v| h[k] = v}
|
295
|
+
each_pair { |k, v| h[k] = v }
|
196
296
|
h
|
197
297
|
end
|
198
298
|
|
299
|
+
# @!visibility private
|
199
300
|
def marshal_load(hash)
|
200
301
|
initialize
|
201
302
|
populate_from(hash)
|
@@ -209,6 +310,7 @@ module Concurrent
|
|
209
310
|
end
|
210
311
|
|
211
312
|
private
|
313
|
+
|
212
314
|
def raise_fetch_no_key
|
213
315
|
raise KeyError, 'key not found'
|
214
316
|
end
|
@@ -219,7 +321,7 @@ module Concurrent
|
|
219
321
|
end
|
220
322
|
|
221
323
|
def populate_from(hash)
|
222
|
-
hash.each_pair {|k, v| self[k] = v}
|
324
|
+
hash.each_pair { |k, v| self[k] = v }
|
223
325
|
self
|
224
326
|
end
|
225
327
|
|
@@ -1,5 +1,14 @@
|
|
1
1
|
require 'concurrent/thread_safe/util'
|
2
2
|
|
3
|
+
# Shim for TruffleRuby.synchronized
|
4
|
+
if Concurrent.on_truffleruby? && !TruffleRuby.respond_to?(:synchronized)
|
5
|
+
module TruffleRuby
|
6
|
+
def self.synchronized(object, &block)
|
7
|
+
Truffle::System.synchronized(object, &block)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
3
12
|
module Concurrent
|
4
13
|
module ThreadSafe
|
5
14
|
module Util
|
@@ -44,8 +53,7 @@ module Concurrent
|
|
44
53
|
klass.superclass.instance_methods(false).each do |method|
|
45
54
|
klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
46
55
|
def #{method}(*args, &block)
|
47
|
-
|
48
|
-
Truffle::System.synchronized(self) { super(*args, &block) }
|
56
|
+
TruffleRuby.synchronized(self) { super(*args, &block) }
|
49
57
|
end
|
50
58
|
RUBY
|
51
59
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require_relative 'engine'
|
2
|
+
|
3
|
+
if Concurrent.ruby_version :<, 2, 0, 0
|
4
|
+
# @!visibility private
|
5
|
+
module Kernel
|
6
|
+
def __dir__
|
7
|
+
File.dirname __FILE__
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# @!visibility private
|
12
|
+
class LoadError < ScriptError
|
13
|
+
def path
|
14
|
+
message.split(' -- ').last
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -8,7 +8,7 @@ module Concurrent
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def on_jruby_9000?
|
11
|
-
on_jruby? && ruby_version(:>=, 9, 0, 0
|
11
|
+
on_jruby? && ruby_version(JRUBY_VERSION, :>=, 9, 0, 0)
|
12
12
|
end
|
13
13
|
|
14
14
|
def on_cruby?
|
@@ -39,7 +39,7 @@ module Concurrent
|
|
39
39
|
defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'
|
40
40
|
end
|
41
41
|
|
42
|
-
def ruby_version(comparison, major, minor, patch
|
42
|
+
def ruby_version(version = RUBY_VERSION, comparison, major, minor, patch)
|
43
43
|
result = (version.split('.').map(&:to_i) <=> [major, minor, patch])
|
44
44
|
comparisons = { :== => [0],
|
45
45
|
:>= => [1, 0],
|
data/lib/concurrent/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: concurrent-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jerry D'Antonio
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2018-
|
13
|
+
date: 2018-11-05 00:00:00.000000000 Z
|
14
14
|
dependencies: []
|
15
15
|
description: |
|
16
16
|
Modern concurrency tools including agents, futures, promises, thread pools, actors, supervisors, and more.
|
@@ -157,6 +157,7 @@ files:
|
|
157
157
|
- lib/concurrent/timer_task.rb
|
158
158
|
- lib/concurrent/tuple.rb
|
159
159
|
- lib/concurrent/tvar.rb
|
160
|
+
- lib/concurrent/utility/193.rb
|
160
161
|
- lib/concurrent/utility/at_exit.rb
|
161
162
|
- lib/concurrent/utility/engine.rb
|
162
163
|
- lib/concurrent/utility/monotonic_time.rb
|
@@ -176,12 +177,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
176
177
|
requirements:
|
177
178
|
- - ">="
|
178
179
|
- !ruby/object:Gem::Version
|
179
|
-
version:
|
180
|
+
version: 1.9.3
|
180
181
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
181
182
|
requirements:
|
182
|
-
- - "
|
183
|
+
- - ">="
|
183
184
|
- !ruby/object:Gem::Version
|
184
|
-
version:
|
185
|
+
version: '0'
|
185
186
|
requirements: []
|
186
187
|
rubyforge_project:
|
187
188
|
rubygems_version: 2.7.3
|