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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -0
  3. data/Gemfile +1 -1
  4. data/{LICENSE.md → LICENSE.txt} +18 -20
  5. data/README.md +31 -7
  6. data/Rakefile +30 -23
  7. data/lib/concurrent-ruby/concurrent/array.rb +1 -1
  8. data/lib/concurrent-ruby/concurrent/async.rb +9 -20
  9. data/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb +52 -42
  10. data/lib/concurrent-ruby/concurrent/collection/map/mri_map_backend.rb +1 -1
  11. data/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb +14 -0
  12. data/lib/concurrent-ruby/concurrent/collection/ruby_non_concurrent_priority_queue.rb +11 -1
  13. data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
  14. data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +7 -0
  15. data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +13 -1
  16. data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +11 -1
  17. data/lib/concurrent-ruby/concurrent/executor/thread_pool_executor.rb +2 -1
  18. data/lib/concurrent-ruby/concurrent/hash.rb +1 -1
  19. data/lib/concurrent-ruby/concurrent/immutable_struct.rb +1 -1
  20. data/lib/concurrent-ruby/concurrent/map.rb +13 -3
  21. data/lib/concurrent-ruby/concurrent/mutable_struct.rb +2 -2
  22. data/lib/concurrent-ruby/concurrent/promise.rb +1 -0
  23. data/lib/concurrent-ruby/concurrent/set.rb +14 -6
  24. data/lib/concurrent-ruby/concurrent/settable_struct.rb +1 -1
  25. data/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb +2 -2
  26. data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +12 -0
  27. data/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb +6 -0
  28. data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +26 -1
  29. data/lib/concurrent-ruby/concurrent/thread_safe/util/striped64.rb +1 -1
  30. data/lib/concurrent-ruby/concurrent/timer_task.rb +0 -1
  31. data/lib/concurrent-ruby/concurrent/tvar.rb +9 -6
  32. data/lib/concurrent-ruby/concurrent/version.rb +1 -1
  33. metadata +6 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 649de1a5e5c7c82652bd6ad21496f2c1061d7c75517271d8ca30c3ceb9c49038
4
- data.tar.gz: 6274d2cd2dcda660a1e34436547eca26d6c74d03e9a527d7ce5d1a9f3f72f4be
3
+ metadata.gz: 4e26dce842bfb7973c28f13017d50c3e729a9261f48427920db279055c4d089b
4
+ data.tar.gz: 501e9d485d4683657b49cd5820ef0462ce91133ad163ba9e790f254892d1e533
5
5
  SHA512:
6
- metadata.gz: 2adbf28bdf7034295709496964e8e70d22f59bf058bf18ca005c2ce536627160538de6c510ea1f39893de0ad90b2fb50d6f35c848c97f58bd8f71ff8d9e57fc2
7
- data.tar.gz: 1cbc384488e0f70d19d742e54c34dbd7abfaffe89f75230b2f5b80d2ae4d161b731666ce6b64e8f5adeb561b6d32191ce6bd679e05d6bd9eec91b869ee379b2f
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', '~> 0.7.0'
17
+ gem 'rake-compiler-dock', '~> 1.0'
18
18
  gem 'pry', '~> 0.11', platforms: :mri
19
19
  end
20
20
 
@@ -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
- all copies or substantial portions of the Software.
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
- THE SOFTWARE.
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-2.2.0/Array.html).
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-2.2.0/Hash.html).
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-2.2.0/Struct.html):
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) (lead maintainer, point-of-contact)
359
- * [Jerry D'Antonio](https://github.com/jdantonio) (creator)
360
- * [Chris Seaton](https://github.com/chrisseaton)
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
- and to the past maintainers
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
- RakeCompilerDock.sh 'bundle install --local && bundle exec rake cross native package --trace'
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
- gsub(/\[([\w ]+)\]\(http:\/\/ruby-concurrency\.github\.io\/concurrent-ruby\/master\/.*\)/) do |_|
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
- '--output-dir', output_dir,
167
- '--main', 'tmp/README.md',
168
- *common_yard_options)
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.md',
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
- '--output-dir', 'docs',
191
- '--main', 'docs-source/signpost.md',
192
- *common_yard_options)
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 'Command failed. Continue? (y/n)'
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
- STDOUT.puts 'Command failed. Continue? (y/n)'
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
- '$(git show-ref --verify --hash refs/remotes/origin/master)' do |ok, res|
251
+ '$(git show-ref --verify --hash refs/remotes/origin/master)' do |ok, res|
252
252
  unless ok
253
253
  begin
254
- STDOUT.puts 'Command failed. Continue? (y/n)'
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? (y/n)'
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
- puts 'Manually: create a release on GitHub with relevant changelog part'
328
- puts 'Manually: send email same as release with relevant changelog part'
329
- puts 'Manually: tweet'
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-2.2.0/Array.html Ruby standard library `Array`
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
- # @!visibility private
32
- FREE = []
33
- LOCK = Mutex.new
34
- ARRAYS = {} # used as a hash set
35
- # noinspection RubyClassVariableUsageInspection
36
- @@next = 0
37
- QUEUE = Queue.new
38
- THREAD = Thread.new do
39
- while true
40
- method, i = QUEUE.pop
41
- case method
42
- when :thread_local_finalizer
43
- LOCK.synchronize do
44
- FREE.push(i)
45
- # The cost of GC'ing a TLV is linear in the number of threads using TLVs
46
- # But that is natural! More threads means more storage is used per TLV
47
- # So naturally more CPU time is required to free more storage
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, :ARRAYS, :QUEUE, :THREAD
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
- LOCK.synchronize { ARRAYS[array.object_id] = array }
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 = LOCK.synchronize do
101
- FREE.pop || begin
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
- # avoid error: can't be called from trap context
113
- proc { QUEUE.push [:thread_local_finalizer, index] }
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
- # avoid error: can't be called from trap context
119
- proc { QUEUE.push [:thread_finalizer, id] }
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 = _get(key) # fast non-blocking path for the most likely case
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
@@ -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
- queue = java.util.concurrent.LinkedBlockingQueue.new
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-2.2.0/Hash.html Ruby standard library `Hash`
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-2.2.0/Struct.html Ruby standard library `Struct`
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.on_rbx? || Concurrent.on_truffleruby?
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-2.2.0/Struct.html Ruby standard library `Struct`
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-2.2.0/Struct.html#method-c-new Ruby standard library `Struct#new`
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
  #
@@ -250,6 +250,7 @@ module Concurrent
250
250
  realize(@promise_body)
251
251
  end
252
252
  else
253
+ compare_and_set_state(:pending, :unscheduled)
253
254
  @parent.execute
254
255
  end
255
256
  self
@@ -19,13 +19,19 @@ module Concurrent
19
19
  #
20
20
  # @see http://ruby-doc.org/stdlib-2.4.0/libdoc/set/rdoc/Set.html Ruby standard library `Set`
21
21
 
22
-
23
22
  # @!macro internal_implementation_note
24
23
  SetImplementation = case
25
24
  when Concurrent.on_cruby?
26
- # Because MRI never runs code in parallel, the existing
27
- # non-thread-safe structures should usually work fine.
28
- ::Set
25
+ # The CRuby implementation of Set is written in Ruby itself and is
26
+ # not thread safe for certain methods.
27
+ require 'monitor'
28
+ require 'concurrent/thread_safe/util/data_structures'
29
+
30
+ class CRubySet < ::Set
31
+ end
32
+
33
+ ThreadSafe::Util.make_synchronized_on_cruby CRubySet
34
+ CRubySet
29
35
 
30
36
  when Concurrent.on_jruby?
31
37
  require 'jruby/synchronized'
@@ -33,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
- ThreadSafe::Util.make_synchronized_on_rbx Concurrent::RbxSet
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 Concurrent::TruffleRubySet
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-2.2.0/Struct.html Ruby standard library `Struct`
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-2.2.0/Thread.html#method-i-wakeup)
30
- # and [`Thread#raise`](http://ruby-doc.org/core-2.2.0/Thread.html#method-i-raise).
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,6 +12,12 @@ module Concurrent
12
12
  @__owner__ = nil
13
13
  end
14
14
 
15
+ def initialize_copy(other)
16
+ super
17
+ @__Waiters__ = []
18
+ @__owner__ = nil
19
+ end
20
+
15
21
  protected
16
22
 
17
23
  def synchronize(&block)
@@ -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 = Monitor.new unless @_monitor # avoid double initialisation
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 *(12.times.collect{ |i| "padding_#{i}".to_sym })
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
@@ -325,7 +325,6 @@ module Concurrent
325
325
  def timeout_task(completion)
326
326
  return unless @running.true?
327
327
  if completion.try?
328
- self.value = value
329
328
  schedule_next_task
330
329
  observers.notify_observers(Time.now, nil, Concurrent::TimeoutError.new)
331
330
  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
- unless @write_log.has_key? tvar
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
- # If we previously wrote to it, check the version hasn't changed
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
@@ -1,3 +1,3 @@
1
1
  module Concurrent
2
- VERSION = '1.1.6'
2
+ VERSION = '1.1.9'
3
3
  end
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.6
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: 2020-02-10 00:00:00.000000000 Z
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.md
23
+ - LICENSE.txt
24
24
  - CHANGELOG.md
25
25
  files:
26
26
  - CHANGELOG.md
27
27
  - Gemfile
28
- - LICENSE.md
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
- rubyforge_project:
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,