concurrent-ruby 1.1.6 → 1.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -0
- data/Gemfile +1 -1
- data/{LICENSE.md → LICENSE.txt} +18 -20
- data/README.md +31 -7
- data/Rakefile +30 -23
- data/lib/concurrent-ruby/concurrent/array.rb +1 -1
- data/lib/concurrent-ruby/concurrent/async.rb +9 -20
- data/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb +52 -42
- data/lib/concurrent-ruby/concurrent/collection/map/mri_map_backend.rb +1 -1
- data/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb +14 -0
- data/lib/concurrent-ruby/concurrent/collection/ruby_non_concurrent_priority_queue.rb +11 -1
- data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
- data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +7 -0
- data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +13 -1
- data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +11 -1
- data/lib/concurrent-ruby/concurrent/executor/thread_pool_executor.rb +2 -1
- data/lib/concurrent-ruby/concurrent/hash.rb +1 -1
- data/lib/concurrent-ruby/concurrent/immutable_struct.rb +1 -1
- data/lib/concurrent-ruby/concurrent/map.rb +13 -3
- data/lib/concurrent-ruby/concurrent/mutable_struct.rb +2 -2
- data/lib/concurrent-ruby/concurrent/promise.rb +1 -0
- data/lib/concurrent-ruby/concurrent/set.rb +14 -6
- data/lib/concurrent-ruby/concurrent/settable_struct.rb +1 -1
- data/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb +2 -2
- data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +12 -0
- data/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb +6 -0
- data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +26 -1
- data/lib/concurrent-ruby/concurrent/thread_safe/util/striped64.rb +1 -1
- data/lib/concurrent-ruby/concurrent/timer_task.rb +0 -1
- data/lib/concurrent-ruby/concurrent/tvar.rb +9 -6
- data/lib/concurrent-ruby/concurrent/version.rb +1 -1
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4e26dce842bfb7973c28f13017d50c3e729a9261f48427920db279055c4d089b
|
4
|
+
data.tar.gz: 501e9d485d4683657b49cd5820ef0462ce91133ad163ba9e790f254892d1e533
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7f894d3207ce129044970321fabe750cc4b9744a876f354dd791d045fb4ffed1d0a5a9a50dcb1ab0ca36df705b6b7578fe7eded26bcaa3dbeb0c7a12e655596c
|
7
|
+
data.tar.gz: 3cb4ccbbf13e2f73987eda48a32b444d7330c53a668ddef2fd4a73841caf18805d68cec45273a02de2c10087da4a4ce063c01b1bdccb96d4668b10cc4af79f88
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,31 @@
|
|
1
1
|
## Current
|
2
2
|
|
3
|
+
## Release v1.1.9 (5 Jun 2021)
|
4
|
+
|
5
|
+
concurrent-ruby:
|
6
|
+
|
7
|
+
* (#866) Child promise state not set to :pending immediately after #execute when parent has completed
|
8
|
+
* (#905, #872) Fix RubyNonConcurrentPriorityQueue#delete method
|
9
|
+
* (2df0337d) Make sure locks are not shared on shared when objects are dup/cloned
|
10
|
+
* (#900, #906, #796, #847, #911) Fix Concurrent::Set tread-safety issues on CRuby
|
11
|
+
* (#907) Add new ConcurrentMap backend for TruffleRuby
|
12
|
+
|
13
|
+
## Release v1.1.8 (20 January 2021)
|
14
|
+
|
15
|
+
concurrent-ruby:
|
16
|
+
|
17
|
+
* (#885) Fix race condition in TVar for stale reads
|
18
|
+
* (#884) RubyThreadLocalVar: Do not iterate over hash which might conflict with new pair addition
|
19
|
+
|
20
|
+
## Release v1.1.7 (6 August 2020)
|
21
|
+
|
22
|
+
concurrent-ruby:
|
23
|
+
|
24
|
+
* (#879) Consider falsy value on `Concurrent::Map#compute_if_absent` for fast non-blocking path
|
25
|
+
* (#876) Reset Async queue on forking, makes Async fork-safe
|
26
|
+
* (#856) Avoid running problematic code in RubyThreadLocalVar on MRI that occasionally results in segfault
|
27
|
+
* (#853) Introduce ThreadPoolExecutor without a Queue
|
28
|
+
|
3
29
|
## Release v1.1.6, edge v0.6.0 (10 Feb 2020)
|
4
30
|
|
5
31
|
concurrent-ruby:
|
data/Gemfile
CHANGED
@@ -14,7 +14,7 @@ gem 'concurrent-ruby-ext', Concurrent::VERSION, options.merge(platform: :mri)
|
|
14
14
|
group :development do
|
15
15
|
gem 'rake', (Concurrent.ruby_version :<, 2, 2, 0) ? '~> 12.0' : '~> 13.0'
|
16
16
|
gem 'rake-compiler', '~> 1.0', '>= 1.0.7'
|
17
|
-
gem 'rake-compiler-dock', '~>
|
17
|
+
gem 'rake-compiler-dock', '~> 1.0'
|
18
18
|
gem 'pry', '~> 0.11', platforms: :mri
|
19
19
|
end
|
20
20
|
|
data/{LICENSE.md → LICENSE.txt}
RENAMED
@@ -1,23 +1,21 @@
|
|
1
|
-
```
|
2
1
|
Copyright (c) Jerry D'Antonio -- released under the MIT license.
|
3
2
|
|
4
|
-
http://www.opensource.org/licenses/mit-license.php
|
3
|
+
http://www.opensource.org/licenses/mit-license.php
|
5
4
|
|
6
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
-
of this software and associated documentation files (the "Software"), to deal
|
8
|
-
in the Software without restriction, including without limitation the rights
|
9
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
-
copies of the Software, and to permit persons to whom the Software is
|
11
|
-
furnished to do so, subject to the following conditions:
|
12
|
-
|
13
|
-
The above copyright notice and this permission notice shall be included in
|
14
|
-
|
15
|
-
|
16
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
|
-
|
23
|
-
```
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
CHANGED
@@ -105,9 +105,9 @@ We also have a [IRC (gitter)](https://gitter.im/ruby-concurrency/concurrent-ruby
|
|
105
105
|
Collection classes that were originally part of the (deprecated) `thread_safe` gem:
|
106
106
|
|
107
107
|
* [Array](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Array.html) A thread-safe
|
108
|
-
subclass of Ruby's standard [Array](http://ruby-doc.org/core
|
108
|
+
subclass of Ruby's standard [Array](http://ruby-doc.org/core/Array.html).
|
109
109
|
* [Hash](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Hash.html) A thread-safe
|
110
|
-
subclass of Ruby's standard [Hash](http://ruby-doc.org/core
|
110
|
+
subclass of Ruby's standard [Hash](http://ruby-doc.org/core/Hash.html).
|
111
111
|
* [Set](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Set.html) A thread-safe
|
112
112
|
subclass of Ruby's standard [Set](http://ruby-doc.org/stdlib-2.4.0/libdoc/set/rdoc/Set.html).
|
113
113
|
* [Map](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/Map.html) A hash-like object
|
@@ -122,7 +122,7 @@ Value objects inspired by other languages:
|
|
122
122
|
immutable object representing an optional value, based on
|
123
123
|
[Haskell Data.Maybe](https://hackage.haskell.org/package/base-4.2.0.1/docs/Data-Maybe.html).
|
124
124
|
|
125
|
-
Structure classes derived from Ruby's [Struct](http://ruby-doc.org/core
|
125
|
+
Structure classes derived from Ruby's [Struct](http://ruby-doc.org/core/Struct.html):
|
126
126
|
|
127
127
|
* [ImmutableStruct](http://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ImmutableStruct.html)
|
128
128
|
Immutable struct where values are set at construction and cannot be changed later.
|
@@ -353,19 +353,43 @@ and load the appropriate C extensions.
|
|
353
353
|
No gems should depend on `concurrent-ruby-ext`. Doing so will force C extensions on your users. The
|
354
354
|
best practice is to depend on `concurrent-ruby` and let users to decide if they want C extensions.
|
355
355
|
|
356
|
+
## Building the gem
|
357
|
+
|
358
|
+
### Requirements
|
359
|
+
|
360
|
+
* Recent CRuby
|
361
|
+
* JRuby, `rbenv install jruby-9.2.17.0`
|
362
|
+
* Set env variable `CONCURRENT_JRUBY_HOME` to point to it, e.g. `/usr/local/opt/rbenv/versions/jruby-9.2.17.0`
|
363
|
+
* Install Docker, required for Windows builds
|
364
|
+
|
365
|
+
### Publishing the Gem
|
366
|
+
|
367
|
+
* Update`version.rb`
|
368
|
+
* Update the CHANGELOG
|
369
|
+
* Update the Yard documentation
|
370
|
+
- Add the new version to `docs-source/signpost.md`. Needs to be done only if there are visible changes in the
|
371
|
+
documentation.
|
372
|
+
- Run `bundle exec rake yard` to update the master documentation and signpost.
|
373
|
+
- Run `bundle exec rake yard:<new-version>` to add or update the documentation of the new version.
|
374
|
+
* Commit (and push) the changes.
|
375
|
+
* Use `be rake release` to release the gem. It consists
|
376
|
+
of `['release:checks', 'release:build', 'release:test', 'release:publish']` steps. It will ask at the end before
|
377
|
+
publishing anything. Steps can also be executed individually.
|
378
|
+
|
356
379
|
## Maintainers
|
357
380
|
|
358
|
-
* [Petr Chalupa](https://github.com/pitr-ch)
|
359
|
-
* [
|
360
|
-
|
381
|
+
* [Petr Chalupa](https://github.com/pitr-ch) — Lead maintainer, point-of-contact.
|
382
|
+
* [Chris Seaton](https://github.com/chrisseaton) —
|
383
|
+
If Petr is not available Chris can help or poke Petr to pay attention where it is needed.
|
361
384
|
|
362
385
|
### Special Thanks to
|
363
386
|
|
387
|
+
* [Jerry D'Antonio](https://github.com/jdantonio) for creating the gem
|
364
388
|
* [Brian Durand](https://github.com/bdurand) for the `ref` gem
|
365
389
|
* [Charles Oliver Nutter](https://github.com/headius) for the `atomic` and `thread_safe` gems
|
366
390
|
* [thedarkone](https://github.com/thedarkone) for the `thread_safe` gem
|
367
391
|
|
368
|
-
|
392
|
+
to the past maintainers
|
369
393
|
|
370
394
|
* [Michele Della Torre](https://github.com/mighe)
|
371
395
|
* [Paweł Obrok](https://github.com/obrok)
|
data/Rakefile
CHANGED
@@ -49,7 +49,9 @@ namespace :repackage do
|
|
49
49
|
Rake::Task['lib/concurrent-ruby/concurrent/concurrent_ruby.jar'].invoke
|
50
50
|
|
51
51
|
# build all gem files
|
52
|
-
|
52
|
+
%w[x86-mingw32 x64-mingw32].each do |plat|
|
53
|
+
RakeCompilerDock.sh "bundle install --local && bundle exec rake native:#{plat} gem --trace", platform: plat
|
54
|
+
end
|
53
55
|
end
|
54
56
|
end
|
55
57
|
end
|
@@ -133,7 +135,7 @@ begin
|
|
133
135
|
task :update_readme do
|
134
136
|
Dir.chdir __dir__ do
|
135
137
|
content = File.read(File.join('README.md')).
|
136
|
-
|
138
|
+
gsub(/\[([\w ]+)\]\(http:\/\/ruby-concurrency\.github\.io\/concurrent-ruby\/master\/.*\)/) do |_|
|
137
139
|
case $1
|
138
140
|
when 'LockFreeLinkedSet'
|
139
141
|
"{Concurrent::Edge::#{$1} #{$1}}"
|
@@ -163,9 +165,9 @@ begin
|
|
163
165
|
desc "* of #{name} into subdir #{name}"
|
164
166
|
YARD::Rake::YardocTask.new(name) do |yard|
|
165
167
|
yard.options.push(
|
166
|
-
|
167
|
-
|
168
|
-
|
168
|
+
'--output-dir', output_dir,
|
169
|
+
'--main', 'tmp/README.md',
|
170
|
+
*common_yard_options)
|
169
171
|
yard.files = ['./lib/concurrent-ruby/**/*.rb',
|
170
172
|
'./lib/concurrent-ruby-edge/**/*.rb',
|
171
173
|
'./ext/concurrent_ruby_ext/**/*.c',
|
@@ -173,7 +175,7 @@ begin
|
|
173
175
|
'docs-source/thread_pools.md',
|
174
176
|
'docs-source/promises.out.md',
|
175
177
|
'docs-source/medium-example.out.rb',
|
176
|
-
'LICENSE.
|
178
|
+
'LICENSE.txt',
|
177
179
|
'CHANGELOG.md']
|
178
180
|
end
|
179
181
|
Rake::Task[name].prerequisites.push removal_name,
|
@@ -187,9 +189,9 @@ begin
|
|
187
189
|
desc "* signpost for versions"
|
188
190
|
YARD::Rake::YardocTask.new(:signpost) do |yard|
|
189
191
|
yard.options.push(
|
190
|
-
|
191
|
-
|
192
|
-
|
192
|
+
'--output-dir', 'docs',
|
193
|
+
'--main', 'docs-source/signpost.md',
|
194
|
+
*common_yard_options)
|
193
195
|
yard.files = ['no-lib']
|
194
196
|
end
|
195
197
|
|
@@ -204,7 +206,7 @@ begin
|
|
204
206
|
sh 'diff -r docs/ docs-copy/' do |ok, res|
|
205
207
|
unless ok
|
206
208
|
begin
|
207
|
-
STDOUT.puts
|
209
|
+
STDOUT.puts "yard:#{name} is not properly generated and committed.", "Continue? (y/n)"
|
208
210
|
input = STDIN.gets.strip.downcase
|
209
211
|
end until %w(y n).include?(input)
|
210
212
|
exit 1 if input == 'n'
|
@@ -232,15 +234,13 @@ task :release => ['release:checks', 'release:build', 'release:test', 'release:pu
|
|
232
234
|
namespace :release do
|
233
235
|
# Depends on environment of @pitr-ch
|
234
236
|
|
235
|
-
mri_version = '2.6.5'
|
236
|
-
jruby_version = 'jruby-9.2.9.0'
|
237
|
-
|
238
237
|
task :checks => "yard:#{current_yard_version_name}:uptodate" do
|
239
238
|
Dir.chdir(__dir__) do
|
240
239
|
sh 'test -z "$(git status --porcelain)"' do |ok, res|
|
241
240
|
unless ok
|
242
241
|
begin
|
243
|
-
|
242
|
+
status = `git status --porcelain`
|
243
|
+
STDOUT.puts 'There are local changes that you might want to commit.', status, 'Continue? (y/n)'
|
244
244
|
input = STDIN.gets.strip.downcase
|
245
245
|
end until %w(y n).include?(input)
|
246
246
|
exit 1 if input == 'n'
|
@@ -248,10 +248,10 @@ namespace :release do
|
|
248
248
|
end
|
249
249
|
sh 'git fetch'
|
250
250
|
sh 'test $(git show-ref --verify --hash refs/heads/master) = ' +
|
251
|
-
|
251
|
+
'$(git show-ref --verify --hash refs/remotes/origin/master)' do |ok, res|
|
252
252
|
unless ok
|
253
253
|
begin
|
254
|
-
STDOUT.puts '
|
254
|
+
STDOUT.puts 'Local master branch is not pushed to origin.', 'Continue? (y/n)'
|
255
255
|
input = STDIN.gets.strip.downcase
|
256
256
|
end until %w(y n).include?(input)
|
257
257
|
exit 1 if input == 'n'
|
@@ -268,6 +268,12 @@ namespace :release do
|
|
268
268
|
Dir.chdir(__dir__) do
|
269
269
|
old = ENV['RBENV_VERSION']
|
270
270
|
|
271
|
+
mri_version = `ruby -e 'puts RUBY_VERSION'`.chomp
|
272
|
+
jruby_version = File.basename(ENV['CONCURRENT_JRUBY_HOME'])
|
273
|
+
|
274
|
+
puts "Using following version:"
|
275
|
+
pp mri_version: mri_version, jruby_version: jruby_version
|
276
|
+
|
271
277
|
ENV['RBENV_VERSION'] = mri_version
|
272
278
|
sh 'rbenv version'
|
273
279
|
sh 'bundle exec rake spec:installed'
|
@@ -290,19 +296,19 @@ namespace :release do
|
|
290
296
|
|
291
297
|
task :ask do
|
292
298
|
begin
|
293
|
-
STDOUT.puts 'Do you want to publish anything? (y/n)'
|
299
|
+
STDOUT.puts 'Do you want to publish anything now? (y/n)'
|
294
300
|
input = STDIN.gets.strip.downcase
|
295
301
|
end until %w(y n).include?(input)
|
296
302
|
exit 1 if input == 'n'
|
297
303
|
begin
|
298
|
-
STDOUT.puts 'Do you want to publish edge
|
304
|
+
STDOUT.puts 'It will publish `concurrent-ruby`. Do you want to publish `concurrent-ruby-edge`? (y/n)'
|
299
305
|
input = STDIN.gets.strip.downcase
|
300
306
|
end until %w(y n).include?(input)
|
301
307
|
publish_edge = input == 'y'
|
302
308
|
end
|
303
309
|
|
304
310
|
desc '** tag HEAD with current version and push to github'
|
305
|
-
task :tag do
|
311
|
+
task :tag => :ask do
|
306
312
|
Dir.chdir(__dir__) do
|
307
313
|
sh "git tag v#{Concurrent::VERSION}"
|
308
314
|
sh "git push origin v#{Concurrent::VERSION}"
|
@@ -312,7 +318,7 @@ namespace :release do
|
|
312
318
|
end
|
313
319
|
|
314
320
|
desc '** push all *.gem files to rubygems'
|
315
|
-
task :rubygems do
|
321
|
+
task :rubygems => :ask do
|
316
322
|
Dir.chdir(__dir__) do
|
317
323
|
sh "gem push pkg/concurrent-ruby-#{Concurrent::VERSION}.gem"
|
318
324
|
sh "gem push pkg/concurrent-ruby-edge-#{Concurrent::EDGE_VERSION}.gem" if publish_edge
|
@@ -324,9 +330,10 @@ namespace :release do
|
|
324
330
|
|
325
331
|
desc '** print post release steps'
|
326
332
|
task :post_steps do
|
327
|
-
|
328
|
-
puts 'Manually:
|
329
|
-
puts 'Manually:
|
333
|
+
# TODO: (petr 05-Jun-2021) automate and renew the process
|
334
|
+
# puts 'Manually: create a release on GitHub with relevant changelog part'
|
335
|
+
# puts 'Manually: send email same as release with relevant changelog part'
|
336
|
+
# puts 'Manually: tweet'
|
330
337
|
end
|
331
338
|
end
|
332
339
|
end
|
@@ -16,7 +16,7 @@ module Concurrent
|
|
16
16
|
# operation therefore when two `+=` operations are executed concurrently updates
|
17
17
|
# may be lost. Use `#concat` instead.
|
18
18
|
#
|
19
|
-
# @see http://ruby-doc.org/core
|
19
|
+
# @see http://ruby-doc.org/core/Array.html Ruby standard library `Array`
|
20
20
|
|
21
21
|
# @!macro internal_implementation_note
|
22
22
|
ArrayImplementation = case
|
@@ -58,26 +58,6 @@ module Concurrent
|
|
58
58
|
# end
|
59
59
|
# ```
|
60
60
|
#
|
61
|
-
# When defining a constructor it is critical that the first line be a call to
|
62
|
-
# `super` with no arguments. The `super` method initializes the background
|
63
|
-
# thread and other asynchronous components.
|
64
|
-
#
|
65
|
-
# ```
|
66
|
-
# class BackgroundLogger
|
67
|
-
# include Concurrent::Async
|
68
|
-
#
|
69
|
-
# def initialize(level)
|
70
|
-
# super()
|
71
|
-
# @logger = Logger.new(STDOUT)
|
72
|
-
# @logger.level = level
|
73
|
-
# end
|
74
|
-
#
|
75
|
-
# def info(msg)
|
76
|
-
# @logger.info(msg)
|
77
|
-
# end
|
78
|
-
# end
|
79
|
-
# ```
|
80
|
-
#
|
81
61
|
# Mixing this module into a class provides each object two proxy methods:
|
82
62
|
# `async` and `await`. These methods are thread safe with respect to the
|
83
63
|
# enclosing object. The former proxy allows methods to be called
|
@@ -309,6 +289,7 @@ module Concurrent
|
|
309
289
|
@delegate = delegate
|
310
290
|
@queue = []
|
311
291
|
@executor = Concurrent.global_io_executor
|
292
|
+
@ruby_pid = $$
|
312
293
|
end
|
313
294
|
|
314
295
|
# Delegates method calls to the wrapped object.
|
@@ -326,6 +307,7 @@ module Concurrent
|
|
326
307
|
|
327
308
|
ivar = Concurrent::IVar.new
|
328
309
|
synchronize do
|
310
|
+
reset_if_forked
|
329
311
|
@queue.push [ivar, method, args, block]
|
330
312
|
@executor.post { perform } if @queue.length == 1
|
331
313
|
end
|
@@ -361,6 +343,13 @@ module Concurrent
|
|
361
343
|
end
|
362
344
|
end
|
363
345
|
end
|
346
|
+
|
347
|
+
def reset_if_forked
|
348
|
+
if $$ != @ruby_pid
|
349
|
+
@queue.clear
|
350
|
+
@ruby_pid = $$
|
351
|
+
end
|
352
|
+
end
|
364
353
|
end
|
365
354
|
private_constant :AsyncDelegator
|
366
355
|
|
@@ -28,38 +28,27 @@ module Concurrent
|
|
28
28
|
# But when a Thread is GC'd, we need to drop the reference to its thread-local
|
29
29
|
# array, so we don't leak memory
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
#
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
ARRAYS.each_value do |array|
|
49
|
-
array[i] = nil
|
50
|
-
end
|
51
|
-
end
|
52
|
-
when :thread_finalizer
|
53
|
-
LOCK.synchronize do
|
54
|
-
# The thread which used this thread-local array is now gone
|
55
|
-
# So don't hold onto a reference to the array (thus blocking GC)
|
56
|
-
ARRAYS.delete(i)
|
57
|
-
end
|
58
|
-
end
|
31
|
+
FREE = []
|
32
|
+
LOCK = Mutex.new
|
33
|
+
THREAD_LOCAL_ARRAYS = {} # used as a hash set
|
34
|
+
|
35
|
+
# synchronize when not on MRI
|
36
|
+
# on MRI using lock in finalizer leads to "can't be called from trap context" error
|
37
|
+
# so the code is carefully written to be tread-safe on MRI relying on GIL
|
38
|
+
|
39
|
+
if Concurrent.on_cruby?
|
40
|
+
# @!visibility private
|
41
|
+
def self.semi_sync(&block)
|
42
|
+
block.call
|
43
|
+
end
|
44
|
+
else
|
45
|
+
# @!visibility private
|
46
|
+
def self.semi_sync(&block)
|
47
|
+
LOCK.synchronize(&block)
|
59
48
|
end
|
60
49
|
end
|
61
50
|
|
62
|
-
private_constant :FREE, :LOCK, :
|
51
|
+
private_constant :FREE, :LOCK, :THREAD_LOCAL_ARRAYS
|
63
52
|
|
64
53
|
# @!macro thread_local_var_method_get
|
65
54
|
def value
|
@@ -85,7 +74,7 @@ module Concurrent
|
|
85
74
|
# Using Ruby's built-in thread-local storage is faster
|
86
75
|
unless (array = get_threadlocal_array(me))
|
87
76
|
array = set_threadlocal_array([], me)
|
88
|
-
|
77
|
+
self.class.semi_sync { THREAD_LOCAL_ARRAYS[array.object_id] = array }
|
89
78
|
ObjectSpace.define_finalizer(me, self.class.thread_finalizer(array.object_id))
|
90
79
|
end
|
91
80
|
array[@index] = (value.nil? ? NULL : value)
|
@@ -95,32 +84,53 @@ module Concurrent
|
|
95
84
|
protected
|
96
85
|
|
97
86
|
# @!visibility private
|
98
|
-
# noinspection RubyClassVariableUsageInspection
|
99
87
|
def allocate_storage
|
100
|
-
@index =
|
101
|
-
|
102
|
-
result = @@next
|
103
|
-
@@next += 1
|
104
|
-
result
|
105
|
-
end
|
106
|
-
end
|
88
|
+
@index = FREE.pop || next_index
|
89
|
+
|
107
90
|
ObjectSpace.define_finalizer(self, self.class.thread_local_finalizer(@index))
|
108
91
|
end
|
109
92
|
|
110
93
|
# @!visibility private
|
111
94
|
def self.thread_local_finalizer(index)
|
112
|
-
|
113
|
-
|
95
|
+
proc do
|
96
|
+
semi_sync do
|
97
|
+
# The cost of GC'ing a TLV is linear in the number of threads using TLVs
|
98
|
+
# But that is natural! More threads means more storage is used per TLV
|
99
|
+
# So naturally more CPU time is required to free more storage
|
100
|
+
#
|
101
|
+
# DO NOT use each_value which might conflict with new pair assignment
|
102
|
+
# into the hash in #value= method
|
103
|
+
THREAD_LOCAL_ARRAYS.values.each { |array| array[index] = nil }
|
104
|
+
# free index has to be published after the arrays are cleared
|
105
|
+
FREE.push(index)
|
106
|
+
end
|
107
|
+
end
|
114
108
|
end
|
115
109
|
|
116
110
|
# @!visibility private
|
117
111
|
def self.thread_finalizer(id)
|
118
|
-
|
119
|
-
|
112
|
+
proc do
|
113
|
+
semi_sync do
|
114
|
+
# The thread which used this thread-local array is now gone
|
115
|
+
# So don't hold onto a reference to the array (thus blocking GC)
|
116
|
+
THREAD_LOCAL_ARRAYS.delete(id)
|
117
|
+
end
|
118
|
+
end
|
120
119
|
end
|
121
120
|
|
122
121
|
private
|
123
122
|
|
123
|
+
# noinspection RubyClassVariableUsageInspection
|
124
|
+
@@next = 0
|
125
|
+
# noinspection RubyClassVariableUsageInspection
|
126
|
+
def next_index
|
127
|
+
LOCK.synchronize do
|
128
|
+
result = @@next
|
129
|
+
@@next += 1
|
130
|
+
result
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
124
134
|
if Thread.instance_methods.include?(:thread_variable_get)
|
125
135
|
|
126
136
|
def get_threadlocal_array(thread = Thread.current)
|
@@ -19,7 +19,7 @@ module Concurrent
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def compute_if_absent(key)
|
22
|
-
if stored_value =
|
22
|
+
if NULL != (stored_value = @backend.fetch(key, NULL)) # fast non-blocking path for the most likely case
|
23
23
|
stored_value
|
24
24
|
else
|
25
25
|
@write_lock.synchronize { super }
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Concurrent
|
2
|
+
|
3
|
+
# @!visibility private
|
4
|
+
module Collection
|
5
|
+
|
6
|
+
# @!visibility private
|
7
|
+
class TruffleRubyMapBackend < TruffleRuby::ConcurrentMap
|
8
|
+
def initialize(options = nil)
|
9
|
+
options ||= {}
|
10
|
+
super(initial_capacity: options[:initial_capacity], load_factor: options[:load_factor])
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -30,7 +30,7 @@ module Concurrent
|
|
30
30
|
if @queue[k] == item
|
31
31
|
swap(k, @length)
|
32
32
|
@length -= 1
|
33
|
-
sink(k)
|
33
|
+
sink(k) || swim(k)
|
34
34
|
@queue.pop
|
35
35
|
else
|
36
36
|
k += 1
|
@@ -126,12 +126,17 @@ module Concurrent
|
|
126
126
|
#
|
127
127
|
# @!visibility private
|
128
128
|
def sink(k)
|
129
|
+
success = false
|
130
|
+
|
129
131
|
while (j = (2 * k)) <= @length do
|
130
132
|
j += 1 if j < @length && ! ordered?(j, j+1)
|
131
133
|
break if ordered?(k, j)
|
132
134
|
swap(k, j)
|
135
|
+
success = true
|
133
136
|
k = j
|
134
137
|
end
|
138
|
+
|
139
|
+
success
|
135
140
|
end
|
136
141
|
|
137
142
|
# Percolate up to maintain heap invariant.
|
@@ -140,10 +145,15 @@ module Concurrent
|
|
140
145
|
#
|
141
146
|
# @!visibility private
|
142
147
|
def swim(k)
|
148
|
+
success = false
|
149
|
+
|
143
150
|
while k > 1 && ! ordered?(k/2, k) do
|
144
151
|
swap(k, k/2)
|
145
152
|
k = k/2
|
153
|
+
success = true
|
146
154
|
end
|
155
|
+
|
156
|
+
success
|
147
157
|
end
|
148
158
|
end
|
149
159
|
end
|
Binary file
|
@@ -16,6 +16,9 @@ module Concurrent
|
|
16
16
|
# Default maximum number of seconds a thread in the pool may remain idle
|
17
17
|
# before being reclaimed.
|
18
18
|
|
19
|
+
# @!macro thread_pool_executor_constant_default_synchronous
|
20
|
+
# Default value of the :synchronous option.
|
21
|
+
|
19
22
|
# @!macro thread_pool_executor_attr_reader_max_length
|
20
23
|
# The maximum number of threads that may be created in the pool.
|
21
24
|
# @return [Integer] The maximum number of threads that may be created in the pool.
|
@@ -40,6 +43,10 @@ module Concurrent
|
|
40
43
|
# The number of seconds that a thread may be idle before being reclaimed.
|
41
44
|
# @return [Integer] The number of seconds that a thread may be idle before being reclaimed.
|
42
45
|
|
46
|
+
# @!macro thread_pool_executor_attr_reader_synchronous
|
47
|
+
# Whether or not a value of 0 for :max_queue option means the queue must perform direct hand-off or rather unbounded queue.
|
48
|
+
# @return [true, false]
|
49
|
+
|
43
50
|
# @!macro thread_pool_executor_attr_reader_max_queue
|
44
51
|
# The maximum number of tasks that may be waiting in the work queue at any one time.
|
45
52
|
# When the queue size reaches `max_queue` subsequent tasks will be rejected in
|
@@ -21,12 +21,18 @@ if Concurrent.on_jruby?
|
|
21
21
|
# @!macro thread_pool_executor_constant_default_thread_timeout
|
22
22
|
DEFAULT_THREAD_IDLETIMEOUT = 60
|
23
23
|
|
24
|
+
# @!macro thread_pool_executor_constant_default_synchronous
|
25
|
+
DEFAULT_SYNCHRONOUS = false
|
26
|
+
|
24
27
|
# @!macro thread_pool_executor_attr_reader_max_length
|
25
28
|
attr_reader :max_length
|
26
29
|
|
27
30
|
# @!macro thread_pool_executor_attr_reader_max_queue
|
28
31
|
attr_reader :max_queue
|
29
32
|
|
33
|
+
# @!macro thread_pool_executor_attr_reader_synchronous
|
34
|
+
attr_reader :synchronous
|
35
|
+
|
30
36
|
# @!macro thread_pool_executor_method_initialize
|
31
37
|
def initialize(opts = {})
|
32
38
|
super(opts)
|
@@ -94,8 +100,10 @@ if Concurrent.on_jruby?
|
|
94
100
|
max_length = opts.fetch(:max_threads, DEFAULT_MAX_POOL_SIZE).to_i
|
95
101
|
idletime = opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT).to_i
|
96
102
|
@max_queue = opts.fetch(:max_queue, DEFAULT_MAX_QUEUE_SIZE).to_i
|
103
|
+
@synchronous = opts.fetch(:synchronous, DEFAULT_SYNCHRONOUS)
|
97
104
|
@fallback_policy = opts.fetch(:fallback_policy, :abort)
|
98
105
|
|
106
|
+
raise ArgumentError.new("`synchronous` cannot be set unless `max_queue` is 0") if @synchronous && @max_queue > 0
|
99
107
|
raise ArgumentError.new("`max_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}") if max_length < DEFAULT_MIN_POOL_SIZE
|
100
108
|
raise ArgumentError.new("`max_threads` cannot be greater than #{DEFAULT_MAX_POOL_SIZE}") if max_length > DEFAULT_MAX_POOL_SIZE
|
101
109
|
raise ArgumentError.new("`min_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}") if min_length < DEFAULT_MIN_POOL_SIZE
|
@@ -103,7 +111,11 @@ if Concurrent.on_jruby?
|
|
103
111
|
raise ArgumentError.new("#{fallback_policy} is not a valid fallback policy") unless FALLBACK_POLICY_CLASSES.include?(@fallback_policy)
|
104
112
|
|
105
113
|
if @max_queue == 0
|
106
|
-
|
114
|
+
if @synchronous
|
115
|
+
queue = java.util.concurrent.SynchronousQueue.new
|
116
|
+
else
|
117
|
+
queue = java.util.concurrent.LinkedBlockingQueue.new
|
118
|
+
end
|
107
119
|
else
|
108
120
|
queue = java.util.concurrent.LinkedBlockingQueue.new(@max_queue)
|
109
121
|
end
|
@@ -23,6 +23,9 @@ module Concurrent
|
|
23
23
|
# @!macro thread_pool_executor_constant_default_thread_timeout
|
24
24
|
DEFAULT_THREAD_IDLETIMEOUT = 60
|
25
25
|
|
26
|
+
# @!macro thread_pool_executor_constant_default_synchronous
|
27
|
+
DEFAULT_SYNCHRONOUS = false
|
28
|
+
|
26
29
|
# @!macro thread_pool_executor_attr_reader_max_length
|
27
30
|
attr_reader :max_length
|
28
31
|
|
@@ -35,6 +38,9 @@ module Concurrent
|
|
35
38
|
# @!macro thread_pool_executor_attr_reader_max_queue
|
36
39
|
attr_reader :max_queue
|
37
40
|
|
41
|
+
# @!macro thread_pool_executor_attr_reader_synchronous
|
42
|
+
attr_reader :synchronous
|
43
|
+
|
38
44
|
# @!macro thread_pool_executor_method_initialize
|
39
45
|
def initialize(opts = {})
|
40
46
|
super(opts)
|
@@ -114,9 +120,11 @@ module Concurrent
|
|
114
120
|
@max_length = opts.fetch(:max_threads, DEFAULT_MAX_POOL_SIZE).to_i
|
115
121
|
@idletime = opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT).to_i
|
116
122
|
@max_queue = opts.fetch(:max_queue, DEFAULT_MAX_QUEUE_SIZE).to_i
|
123
|
+
@synchronous = opts.fetch(:synchronous, DEFAULT_SYNCHRONOUS)
|
117
124
|
@fallback_policy = opts.fetch(:fallback_policy, :abort)
|
118
|
-
raise ArgumentError.new("#{@fallback_policy} is not a valid fallback policy") unless FALLBACK_POLICIES.include?(@fallback_policy)
|
119
125
|
|
126
|
+
raise ArgumentError.new("`synchronous` cannot be set unless `max_queue` is 0") if @synchronous && @max_queue > 0
|
127
|
+
raise ArgumentError.new("#{@fallback_policy} is not a valid fallback policy") unless FALLBACK_POLICIES.include?(@fallback_policy)
|
120
128
|
raise ArgumentError.new("`max_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}") if @max_length < DEFAULT_MIN_POOL_SIZE
|
121
129
|
raise ArgumentError.new("`max_threads` cannot be greater than #{DEFAULT_MAX_POOL_SIZE}") if @max_length > DEFAULT_MAX_POOL_SIZE
|
122
130
|
raise ArgumentError.new("`min_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}") if @min_length < DEFAULT_MIN_POOL_SIZE
|
@@ -201,6 +209,8 @@ module Concurrent
|
|
201
209
|
#
|
202
210
|
# @!visibility private
|
203
211
|
def ns_enqueue(*args, &task)
|
212
|
+
return false if @synchronous
|
213
|
+
|
204
214
|
if !ns_limited_queue? || @queue.size < @max_queue
|
205
215
|
@queue << [task, args]
|
206
216
|
true
|
@@ -73,7 +73,8 @@ module Concurrent
|
|
73
73
|
# @option opts [Symbol] :fallback_policy (:abort) the policy for handling new
|
74
74
|
# tasks that are received when the queue size has reached
|
75
75
|
# `max_queue` or the executor has shut down
|
76
|
-
#
|
76
|
+
# @option opts [Boolean] :synchronous (DEFAULT_SYNCHRONOUS) whether or not a value of 0
|
77
|
+
# for :max_queue means the queue must perform direct hand-off rather than unbounded.
|
77
78
|
# @raise [ArgumentError] if `:max_threads` is less than one
|
78
79
|
# @raise [ArgumentError] if `:min_threads` is less than zero
|
79
80
|
# @raise [ArgumentError] if `:fallback_policy` is not one of the values specified
|
@@ -10,7 +10,7 @@ module Concurrent
|
|
10
10
|
# or writing at a time. This includes iteration methods like `#each`,
|
11
11
|
# which takes the lock repeatedly when reading an item.
|
12
12
|
#
|
13
|
-
# @see http://ruby-doc.org/core
|
13
|
+
# @see http://ruby-doc.org/core/Hash.html Ruby standard library `Hash`
|
14
14
|
|
15
15
|
# @!macro internal_implementation_note
|
16
16
|
HashImplementation = case
|
@@ -5,7 +5,7 @@ module Concurrent
|
|
5
5
|
|
6
6
|
# A thread-safe, immutable variation of Ruby's standard `Struct`.
|
7
7
|
#
|
8
|
-
# @see http://ruby-doc.org/core
|
8
|
+
# @see http://ruby-doc.org/core/Struct.html Ruby standard library `Struct`
|
9
9
|
module ImmutableStruct
|
10
10
|
include Synchronization::AbstractStruct
|
11
11
|
|
@@ -15,7 +15,10 @@ module Concurrent
|
|
15
15
|
when Concurrent.on_cruby?
|
16
16
|
require 'concurrent/collection/map/mri_map_backend'
|
17
17
|
MriMapBackend
|
18
|
-
when Concurrent.
|
18
|
+
when Concurrent.on_truffleruby? && defined?(::TruffleRuby::ConcurrentMap)
|
19
|
+
require 'concurrent/collection/map/truffleruby_map_backend'
|
20
|
+
TruffleRubyMapBackend
|
21
|
+
when Concurrent.on_truffleruby? || Concurrent.on_rbx?
|
19
22
|
require 'concurrent/collection/map/atomic_reference_map_backend'
|
20
23
|
AtomicReferenceMapBackend
|
21
24
|
else
|
@@ -114,7 +117,7 @@ module Concurrent
|
|
114
117
|
# @return [true, false] true if deleted
|
115
118
|
# @!macro map.atomic_method
|
116
119
|
|
117
|
-
|
120
|
+
#
|
118
121
|
def initialize(options = nil, &block)
|
119
122
|
if options.kind_of?(::Hash)
|
120
123
|
validate_options_hash!(options)
|
@@ -143,8 +146,15 @@ module Concurrent
|
|
143
146
|
end
|
144
147
|
end
|
145
148
|
|
149
|
+
# Set a value with key
|
150
|
+
# @param [Object] key
|
151
|
+
# @param [Object] value
|
152
|
+
# @return [Object] the new value
|
153
|
+
def []=(key, value)
|
154
|
+
super
|
155
|
+
end
|
156
|
+
|
146
157
|
alias_method :get, :[]
|
147
|
-
# TODO (pitr-ch 30-Oct-2018): doc
|
148
158
|
alias_method :put, :[]=
|
149
159
|
|
150
160
|
# Get a value with key, or default_value when key is absent,
|
@@ -6,7 +6,7 @@ module Concurrent
|
|
6
6
|
# An thread-safe variation of Ruby's standard `Struct`. Values can be set at
|
7
7
|
# construction or safely changed at any time during the object's lifecycle.
|
8
8
|
#
|
9
|
-
# @see http://ruby-doc.org/core
|
9
|
+
# @see http://ruby-doc.org/core/Struct.html Ruby standard library `Struct`
|
10
10
|
module MutableStruct
|
11
11
|
include Synchronization::AbstractStruct
|
12
12
|
|
@@ -40,7 +40,7 @@ module Concurrent
|
|
40
40
|
# struct. Unset parameters default to nil. Passing more parameters than number of attributes
|
41
41
|
# will raise an `ArgumentError`.
|
42
42
|
#
|
43
|
-
# @see http://ruby-doc.org/core
|
43
|
+
# @see http://ruby-doc.org/core/Struct.html#method-c-new Ruby standard library `Struct#new`
|
44
44
|
|
45
45
|
# @!macro struct_values
|
46
46
|
#
|
@@ -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
|
-
#
|
27
|
-
#
|
28
|
-
|
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,6 +39,7 @@ module Concurrent
|
|
33
39
|
class JRubySet < ::Set
|
34
40
|
include JRuby::Synchronized
|
35
41
|
end
|
42
|
+
|
36
43
|
JRubySet
|
37
44
|
|
38
45
|
when Concurrent.on_rbx?
|
@@ -41,7 +48,8 @@ module Concurrent
|
|
41
48
|
|
42
49
|
class RbxSet < ::Set
|
43
50
|
end
|
44
|
-
|
51
|
+
|
52
|
+
ThreadSafe::Util.make_synchronized_on_rbx RbxSet
|
45
53
|
RbxSet
|
46
54
|
|
47
55
|
when Concurrent.on_truffleruby?
|
@@ -50,7 +58,7 @@ module Concurrent
|
|
50
58
|
class TruffleRubySet < ::Set
|
51
59
|
end
|
52
60
|
|
53
|
-
ThreadSafe::Util.make_synchronized_on_truffleruby
|
61
|
+
ThreadSafe::Util.make_synchronized_on_truffleruby TruffleRubySet
|
54
62
|
TruffleRubySet
|
55
63
|
|
56
64
|
else
|
@@ -9,7 +9,7 @@ module Concurrent
|
|
9
9
|
# or any time thereafter. Attempting to assign a value to a member
|
10
10
|
# that has already been set will result in a `Concurrent::ImmutabilityError`.
|
11
11
|
#
|
12
|
-
# @see http://ruby-doc.org/core
|
12
|
+
# @see http://ruby-doc.org/core/Struct.html Ruby standard library `Struct`
|
13
13
|
# @see http://en.wikipedia.org/wiki/Final_(Java) Java `final` keyword
|
14
14
|
module SettableStruct
|
15
15
|
include Synchronization::AbstractStruct
|
@@ -26,8 +26,8 @@ module Concurrent
|
|
26
26
|
# the classes using it. Use {Synchronization::Object} not this abstract class.
|
27
27
|
#
|
28
28
|
# @note this object does not support usage together with
|
29
|
-
# [`Thread#wakeup`](http://ruby-doc.org/core
|
30
|
-
# and [`Thread#raise`](http://ruby-doc.org/core
|
29
|
+
# [`Thread#wakeup`](http://ruby-doc.org/core/Thread.html#method-i-wakeup)
|
30
|
+
# and [`Thread#raise`](http://ruby-doc.org/core/Thread.html#method-i-raise).
|
31
31
|
# `Thread#sleep` and `Thread#wakeup` will work as expected but mixing `Synchronization::Object#wait` and
|
32
32
|
# `Thread#wakeup` will not work on all platforms.
|
33
33
|
#
|
@@ -32,6 +32,12 @@ module Concurrent
|
|
32
32
|
@__Condition__ = ::ConditionVariable.new
|
33
33
|
end
|
34
34
|
|
35
|
+
def initialize_copy(other)
|
36
|
+
super
|
37
|
+
@__Lock__ = ::Mutex.new
|
38
|
+
@__Condition__ = ::ConditionVariable.new
|
39
|
+
end
|
40
|
+
|
35
41
|
protected
|
36
42
|
|
37
43
|
def synchronize
|
@@ -61,6 +67,12 @@ module Concurrent
|
|
61
67
|
@__Condition__ = @__Lock__.new_cond
|
62
68
|
end
|
63
69
|
|
70
|
+
def initialize_copy(other)
|
71
|
+
super
|
72
|
+
@__Lock__ = ::Monitor.new
|
73
|
+
@__Condition__ = @__Lock__.new_cond
|
74
|
+
end
|
75
|
+
|
64
76
|
protected
|
65
77
|
|
66
78
|
def synchronize # TODO may be a problem with lock.synchronize { lock.wait }
|
@@ -12,12 +12,37 @@ end
|
|
12
12
|
module Concurrent
|
13
13
|
module ThreadSafe
|
14
14
|
module Util
|
15
|
+
def self.make_synchronized_on_cruby(klass)
|
16
|
+
klass.class_eval do
|
17
|
+
def initialize(*args, &block)
|
18
|
+
@_monitor = Monitor.new
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize_copy(other)
|
23
|
+
# make sure a copy is not sharing a monitor with the original object!
|
24
|
+
@_monitor = Monitor.new
|
25
|
+
super
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
klass.superclass.instance_methods(false).each do |method|
|
30
|
+
klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
31
|
+
def #{method}(*args)
|
32
|
+
monitor = @_monitor
|
33
|
+
monitor or raise("BUG: Internal monitor was not properly initialized. Please report this to the concurrent-ruby developers.")
|
34
|
+
monitor.synchronize { super }
|
35
|
+
end
|
36
|
+
RUBY
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
15
40
|
def self.make_synchronized_on_rbx(klass)
|
16
41
|
klass.class_eval do
|
17
42
|
private
|
18
43
|
|
19
44
|
def _mon_initialize
|
20
|
-
@_monitor
|
45
|
+
@_monitor ||= Monitor.new # avoid double initialisation
|
21
46
|
end
|
22
47
|
|
23
48
|
def self.new(*args)
|
@@ -97,7 +97,7 @@ module Concurrent
|
|
97
97
|
# TODO: this only adds padding after the :value slot, need to find a way to add padding before the slot
|
98
98
|
# TODO (pitr-ch 28-Jul-2018): the padding instance vars may not be created
|
99
99
|
# hide from yardoc in a method
|
100
|
-
attr_reader
|
100
|
+
attr_reader :padding_0, :padding_1, :padding_2, :padding_3, :padding_4, :padding_5, :padding_6, :padding_7, :padding_8, :padding_9, :padding_10, :padding_11
|
101
101
|
end
|
102
102
|
padding
|
103
103
|
end
|
@@ -188,7 +188,10 @@ module Concurrent
|
|
188
188
|
def write(tvar, value)
|
189
189
|
# Have we already written to this TVar?
|
190
190
|
|
191
|
-
|
191
|
+
if @write_log.has_key? tvar
|
192
|
+
# Record the value written
|
193
|
+
@write_log[tvar] = value
|
194
|
+
else
|
192
195
|
# Try to lock the TVar
|
193
196
|
|
194
197
|
unless tvar.unsafe_lock.try_lock
|
@@ -196,7 +199,11 @@ module Concurrent
|
|
196
199
|
Concurrent::abort_transaction
|
197
200
|
end
|
198
201
|
|
199
|
-
#
|
202
|
+
# Record the value written
|
203
|
+
|
204
|
+
@write_log[tvar] = value
|
205
|
+
|
206
|
+
# If we previously read from it, check the version hasn't changed
|
200
207
|
|
201
208
|
@read_log.each do |log_entry|
|
202
209
|
if log_entry.tvar == tvar and tvar.unsafe_version > log_entry.version
|
@@ -204,10 +211,6 @@ module Concurrent
|
|
204
211
|
end
|
205
212
|
end
|
206
213
|
end
|
207
|
-
|
208
|
-
# Record the value written
|
209
|
-
|
210
|
-
@write_log[tvar] = value
|
211
214
|
end
|
212
215
|
|
213
216
|
def abort
|
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.9
|
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:
|
13
|
+
date: 2021-06-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.
|
@@ -20,12 +20,12 @@ executables: []
|
|
20
20
|
extensions: []
|
21
21
|
extra_rdoc_files:
|
22
22
|
- README.md
|
23
|
-
- LICENSE.
|
23
|
+
- LICENSE.txt
|
24
24
|
- CHANGELOG.md
|
25
25
|
files:
|
26
26
|
- CHANGELOG.md
|
27
27
|
- Gemfile
|
28
|
-
- LICENSE.
|
28
|
+
- LICENSE.txt
|
29
29
|
- README.md
|
30
30
|
- Rakefile
|
31
31
|
- ext/concurrent-ruby/ConcurrentRubyService.java
|
@@ -79,6 +79,7 @@ files:
|
|
79
79
|
- lib/concurrent-ruby/concurrent/collection/map/mri_map_backend.rb
|
80
80
|
- lib/concurrent-ruby/concurrent/collection/map/non_concurrent_map_backend.rb
|
81
81
|
- lib/concurrent-ruby/concurrent/collection/map/synchronized_map_backend.rb
|
82
|
+
- lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb
|
82
83
|
- lib/concurrent-ruby/concurrent/collection/non_concurrent_priority_queue.rb
|
83
84
|
- lib/concurrent-ruby/concurrent/collection/ruby_non_concurrent_priority_queue.rb
|
84
85
|
- lib/concurrent-ruby/concurrent/concern/deprecation.rb
|
@@ -184,8 +185,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
184
185
|
- !ruby/object:Gem::Version
|
185
186
|
version: '0'
|
186
187
|
requirements: []
|
187
|
-
|
188
|
-
rubygems_version: 2.7.9
|
188
|
+
rubygems_version: 3.2.3
|
189
189
|
signing_key:
|
190
190
|
specification_version: 4
|
191
191
|
summary: Modern concurrency tools for Ruby. Inspired by Erlang, Clojure, Scala, Haskell,
|