concurrent-ruby 1.1.6 → 1.1.9

Sign up to get free protection for your applications and to get access to all the features.
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,