From 31cdd3c1bb9987b165b0ab6a5d3ccdb5954c5112 Mon Sep 17 00:00:00 2001 From: Ian Ker-Seymer Date: Mon, 2 Nov 2015 23:53:19 -0500 Subject: [PATCH 1/4] Move AtomicMarkableReference out of Edge --- README.md | 2 +- lib/concurrent-edge.rb | 1 - lib/concurrent.rb | 1 + .../{edge => atomic}/atomic_markable_reference.rb | 9 ++++----- lib/concurrent/edge/lock_free_linked_set/node.rb | 5 +++-- spec/concurrent/atomic/atomic_markable_reference_spec.rb | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) rename lib/concurrent/{edge => atomic}/atomic_markable_reference.rb (96%) diff --git a/README.md b/README.md index 03a1fda1b..1f74e225a 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,7 @@ Thread-safe variables: * [ReadWriteLock](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/ReadWriteLock.html) A lock that supports multiple readers but only one writer. * [ReentrantReadWriteLock](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/ReentrantReadWriteLock.html) A read/write lock with reentrant and upgrade features. * [Semaphore](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Semaphore.html) A counting-based locking mechanism that uses permits. +* [AtomicMarkableReference](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Atomic/AtomicMarkableReference.html) ### Edge Features @@ -129,7 +130,6 @@ be obeyed though. Features developed in `concurrent-ruby-edge` are expected to m Functionally equivalent to Go [channels](https://tour.golang.org/concurrency/2) with additional inspiration from Clojure [core.async](https://clojure.github.io/core.async/). * [LazyRegister](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/LazyRegister.html) -* [AtomicMarkableReference](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Edge/AtomicMarkableReference.html) * [LockFreeLinkedSet](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Edge/LockFreeLinkedSet.html) * [LockFreeStack](http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/LockFreeStack.html) diff --git a/lib/concurrent-edge.rb b/lib/concurrent-edge.rb index d641a36e2..fffe5dc6e 100644 --- a/lib/concurrent-edge.rb +++ b/lib/concurrent-edge.rb @@ -6,7 +6,6 @@ require 'concurrent/exchanger' require 'concurrent/lazy_register' -require 'concurrent/edge/atomic_markable_reference' require 'concurrent/edge/lock_free_linked_set' require 'concurrent/edge/lock_free_queue' require 'concurrent/edge/lock_free_stack' diff --git a/lib/concurrent.rb b/lib/concurrent.rb index d7377b54d..ab6ef65f6 100644 --- a/lib/concurrent.rb +++ b/lib/concurrent.rb @@ -7,6 +7,7 @@ require 'concurrent/executors' require 'concurrent/synchronization' +require 'concurrent/atomic/atomic_markable_reference' require 'concurrent/atomic/atomic_reference' require 'concurrent/agent' require 'concurrent/atom' diff --git a/lib/concurrent/edge/atomic_markable_reference.rb b/lib/concurrent/atomic/atomic_markable_reference.rb similarity index 96% rename from lib/concurrent/edge/atomic_markable_reference.rb rename to lib/concurrent/atomic/atomic_markable_reference.rb index 5ebf045b1..9db354991 100644 --- a/lib/concurrent/edge/atomic_markable_reference.rb +++ b/lib/concurrent/atomic/atomic_markable_reference.rb @@ -1,6 +1,5 @@ module Concurrent - module Edge - + module Atomic # @!macro [attach] atomic_markable_reference # # An atomic reference which maintains an object reference along with a mark bit @@ -144,9 +143,9 @@ def try_update! unless compare_and_set old_val, new_val, old_mark, new_mark fail ::Concurrent::ConcurrentUpdateError, - 'AtomicMarkableReference: Update failed due to race condition.', - 'Note: If you would like to guarantee an update, please use ' \ - 'the `AtomicMarkableReference#update` method.' + 'AtomicMarkableReference: Update failed due to race condition.', + 'Note: If you would like to guarantee an update, please use ' \ + 'the `AtomicMarkableReference#update` method.' end ImmutableArray[new_val, new_mark] diff --git a/lib/concurrent/edge/lock_free_linked_set/node.rb b/lib/concurrent/edge/lock_free_linked_set/node.rb index 1a7c31a7c..20506e1b2 100644 --- a/lib/concurrent/edge/lock_free_linked_set/node.rb +++ b/lib/concurrent/edge/lock_free_linked_set/node.rb @@ -1,16 +1,17 @@ -require 'concurrent/edge/atomic_markable_reference' +require 'concurrent/atomic/atomic_markable_reference' module Concurrent module Edge class LockFreeLinkedSet class Node < Synchronization::Object include Comparable + include Concurrent::Atomic safe_initialization! def initialize(data = nil, successor = nil) super() - @SuccessorReference = AtomicMarkableReference.new(successor || Tail.new) + @SuccessorReference = AtomicMarkableReference.new(successor || Tail.new) @Data = data @Key = key_for data end diff --git a/spec/concurrent/atomic/atomic_markable_reference_spec.rb b/spec/concurrent/atomic/atomic_markable_reference_spec.rb index 632846ffa..1bfdd6897 100644 --- a/spec/concurrent/atomic/atomic_markable_reference_spec.rb +++ b/spec/concurrent/atomic/atomic_markable_reference_spec.rb @@ -1,4 +1,4 @@ -RSpec.describe Concurrent::Edge::AtomicMarkableReference do +RSpec.describe Concurrent::Atomic::AtomicMarkableReference do subject { described_class.new 1000, true } describe '.initialize' do From da6a4f0305a4542100bdde7aea6be1244a2bd4d3 Mon Sep 17 00:00:00 2001 From: Petr Chalupa Date: Fri, 6 Jul 2018 16:29:56 +0200 Subject: [PATCH 2/4] Do not leak internal class --- .../atomic/atomic_markable_reference.rb | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/lib/concurrent/atomic/atomic_markable_reference.rb b/lib/concurrent/atomic/atomic_markable_reference.rb index 9db354991..444756f16 100644 --- a/lib/concurrent/atomic/atomic_markable_reference.rb +++ b/lib/concurrent/atomic/atomic_markable_reference.rb @@ -14,7 +14,7 @@ class AtomicMarkableReference < ::Concurrent::Synchronization::Object # @!macro [attach] atomic_markable_reference_method_initialize def initialize(value = nil, mark = false) super() - self.reference = ImmutableArray[value, mark] + self.reference = immutable_array(value, mark) end # @!macro [attach] atomic_markable_reference_method_compare_and_set @@ -53,7 +53,7 @@ def compare_and_set(expected_val, new_val, expected_mark, new_mark) return false unless expected_val.equal? curr_val end - prospect = ImmutableArray[new_val, new_mark] + prospect = immutable_array(new_val, new_mark) compare_and_set_reference current, prospect end @@ -63,7 +63,7 @@ def compare_and_set(expected_val, new_val, expected_mark, new_mark) # # Gets the current reference and marked values. # - # @return [ImmutableArray] the current reference and marked values + # @return [Array] the current reference and marked values def get reference end @@ -95,9 +95,9 @@ def mark # @param [Object] new_val the new value # @param [Boolean] new_mark the new mark # - # @return [ImmutableArray] both the new value and the new mark + # @return [Array] both the new value and the new mark def set(new_val, new_mark) - self.reference = ImmutableArray[new_val, new_mark] + self.reference = immutable_array(new_val, new_mark) end # @!macro [attach] atomic_markable_reference_method_update @@ -111,14 +111,14 @@ def set(new_val, new_mark) # @yieldparam [Object] old_val the starting value of the atomic reference # @yieldparam [Boolean] old_mark the starting state of marked # - # @return [ImmutableArray] the new value and new mark + # @return [Array] the new value and new mark def update loop do old_val, old_mark = reference new_val, new_mark = yield old_val, old_mark if compare_and_set old_val, new_val, old_mark, new_mark - return ImmutableArray[new_val, new_mark] + return immutable_array(new_val, new_mark) end end end @@ -134,7 +134,7 @@ def update # @yieldparam [Object] old_val the starting value of the atomic reference # @yieldparam [Boolean] old_mark the starting state of marked # - # @return [ImmutableArray] the new value and marked state + # @return [Array] the new value and marked state # # @raise [Concurrent::ConcurrentUpdateError] if the update fails def try_update! @@ -148,7 +148,7 @@ def try_update! 'the `AtomicMarkableReference#update` method.' end - ImmutableArray[new_val, new_mark] + immutable_array(new_val, new_mark) end # @!macro [attach] atomic_markable_reference_method_try_update @@ -161,7 +161,7 @@ def try_update! # @yieldparam [Object] old_val the starting value of the atomic reference # @yieldparam [Boolean] old_mark the starting state of marked # - # @return [ImmutableArray] the new value and marked state, or nil if + # @return [Array] the new value and marked state, or nil if # the update failed def try_update old_val, old_mark = reference @@ -169,14 +169,13 @@ def try_update return unless compare_and_set old_val, new_val, old_mark, new_mark - ImmutableArray[new_val, new_mark] + immutable_array(new_val, new_mark) end - # Internal/private ImmutableArray for representing pairs - class ImmutableArray < ::Array - def self.new(*args) - super(*args).freeze - end + private + + def immutable_array(*args) + args.freeze end end end From c6ae9940ebd9f7cf4d69d06138d43bcbbe8fd8a0 Mon Sep 17 00:00:00 2001 From: Petr Chalupa Date: Fri, 6 Jul 2018 16:33:03 +0200 Subject: [PATCH 3/4] Removed unused macros --- .../atomic/atomic_markable_reference.rb | 71 +++++++------------ 1 file changed, 26 insertions(+), 45 deletions(-) diff --git a/lib/concurrent/atomic/atomic_markable_reference.rb b/lib/concurrent/atomic/atomic_markable_reference.rb index 444756f16..776ab43fc 100644 --- a/lib/concurrent/atomic/atomic_markable_reference.rb +++ b/lib/concurrent/atomic/atomic_markable_reference.rb @@ -1,37 +1,32 @@ module Concurrent module Atomic - # @!macro [attach] atomic_markable_reference + # An atomic reference which maintains an object reference along with a mark bit + # that can be updated atomically. # - # An atomic reference which maintains an object reference along with a mark bit - # that can be updated atomically. - # - # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicMarkableReference.html - # java.util.concurrent.atomic.AtomicMarkableReference + # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicMarkableReference.html + # java.util.concurrent.atomic.AtomicMarkableReference class AtomicMarkableReference < ::Concurrent::Synchronization::Object private(*attr_atomic(:reference)) - # @!macro [attach] atomic_markable_reference_method_initialize def initialize(value = nil, mark = false) super() self.reference = immutable_array(value, mark) end - # @!macro [attach] atomic_markable_reference_method_compare_and_set - # - # Atomically sets the value and mark to the given updated value and - # mark given both: - # - the current value == the expected value && - # - the current mark == the expected mark + # Atomically sets the value and mark to the given updated value and + # mark given both: + # - the current value == the expected value && + # - the current mark == the expected mark # - # @param [Object] expected_val the expected value - # @param [Object] new_val the new value - # @param [Boolean] expected_mark the expected mark - # @param [Boolean] new_mark the new mark + # @param [Object] expected_val the expected value + # @param [Object] new_val the new value + # @param [Boolean] expected_mark the expected mark + # @param [Boolean] new_mark the new mark # - # @return [Boolean] `true` if successful. A `false` return indicates - # that the actual value was not equal to the expected value or the - # actual mark was not equal to the expected mark + # @return [Boolean] `true` if successful. A `false` return indicates + # that the actual value was not equal to the expected value or the + # actual mark was not equal to the expected mark def compare_and_set(expected_val, new_val, expected_mark, new_mark) # Memoize a valid reference to the current AtomicReference for # later comparison. @@ -59,49 +54,39 @@ def compare_and_set(expected_val, new_val, expected_mark, new_mark) end alias_method :compare_and_swap, :compare_and_set - # @!macro [attach] atomic_markable_reference_method_get - # - # Gets the current reference and marked values. + # Gets the current reference and marked values. # - # @return [Array] the current reference and marked values + # @return [Array] the current reference and marked values def get reference end - # @!macro [attach] atomic_markable_reference_method_value + # Gets the current value of the reference # - # Gets the current value of the reference - # - # @return [Object] the current value of the reference + # @return [Object] the current value of the reference def value reference[0] end - # @!macro [attach] atomic_markable_reference_method_mark - # - # Gets the current marked value + # Gets the current marked value # - # @return [Boolean] the current marked value + # @return [Boolean] the current marked value def mark reference[1] end alias_method :marked?, :mark - # @!macro [attach] atomic_markable_reference_method_set + # _Unconditionally_ sets to the given value of both the reference and + # the mark. # - # _Unconditionally_ sets to the given value of both the reference and - # the mark. + # @param [Object] new_val the new value + # @param [Boolean] new_mark the new mark # - # @param [Object] new_val the new value - # @param [Boolean] new_mark the new mark - # - # @return [Array] both the new value and the new mark + # @return [Array] both the new value and the new mark def set(new_val, new_mark) self.reference = immutable_array(new_val, new_mark) end - # @!macro [attach] atomic_markable_reference_method_update - # # Pass the current value and marked state to the given block, replacing it # with the block's results. May retry if the value changes during the # block's execution. @@ -123,8 +108,6 @@ def update end end - # @!macro [attach] atomic_markable_reference_method_try_update! - # # Pass the current value to the given block, replacing it # with the block's result. Raise an exception if the update # fails. @@ -151,8 +134,6 @@ def try_update! immutable_array(new_val, new_mark) end - # @!macro [attach] atomic_markable_reference_method_try_update - # # Pass the current value to the given block, replacing it with the # block's result. Simply return nil if update fails. # From 5e340b48a30d26b490b642851e850f61e1cdc19b Mon Sep 17 00:00:00 2001 From: Petr Chalupa Date: Fri, 6 Jul 2018 16:42:58 +0200 Subject: [PATCH 4/4] Move out of Atomic namespace --- .../atomic/atomic_markable_reference.rb | 276 +++++++++--------- .../edge/lock_free_linked_set/node.rb | 1 - .../atomic/atomic_markable_reference_spec.rb | 2 +- 3 files changed, 139 insertions(+), 140 deletions(-) diff --git a/lib/concurrent/atomic/atomic_markable_reference.rb b/lib/concurrent/atomic/atomic_markable_reference.rb index 776ab43fc..ed616609f 100644 --- a/lib/concurrent/atomic/atomic_markable_reference.rb +++ b/lib/concurrent/atomic/atomic_markable_reference.rb @@ -1,163 +1,163 @@ module Concurrent - module Atomic - # An atomic reference which maintains an object reference along with a mark bit - # that can be updated atomically. - # - # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicMarkableReference.html - # java.util.concurrent.atomic.AtomicMarkableReference - class AtomicMarkableReference < ::Concurrent::Synchronization::Object - - private(*attr_atomic(:reference)) + # An atomic reference which maintains an object reference along with a mark bit + # that can be updated atomically. + # + # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicMarkableReference.html + # java.util.concurrent.atomic.AtomicMarkableReference + class AtomicMarkableReference < ::Concurrent::Synchronization::Object + + private(*attr_atomic(:reference)) + + def initialize(value = nil, mark = false) + super() + self.reference = immutable_array(value, mark) + end - def initialize(value = nil, mark = false) - super() - self.reference = immutable_array(value, mark) + # Atomically sets the value and mark to the given updated value and + # mark given both: + # - the current value == the expected value && + # - the current mark == the expected mark + # + # @param [Object] expected_val the expected value + # @param [Object] new_val the new value + # @param [Boolean] expected_mark the expected mark + # @param [Boolean] new_mark the new mark + # + # @return [Boolean] `true` if successful. A `false` return indicates + # that the actual value was not equal to the expected value or the + # actual mark was not equal to the expected mark + def compare_and_set(expected_val, new_val, expected_mark, new_mark) + # Memoize a valid reference to the current AtomicReference for + # later comparison. + current = reference + curr_val, curr_mark = current + + # Ensure that that the expected marks match. + return false unless expected_mark == curr_mark + + if expected_val.is_a? Numeric + # If the object is a numeric, we need to ensure we are comparing + # the numerical values + return false unless expected_val == curr_val + else + # Otherwise, we need to ensure we are comparing the object identity. + # Theoretically, this could be incorrect if a user monkey-patched + # `Object#equal?`, but they should know that they are playing with + # fire at that point. + return false unless expected_val.equal? curr_val end - # Atomically sets the value and mark to the given updated value and - # mark given both: - # - the current value == the expected value && - # - the current mark == the expected mark - # - # @param [Object] expected_val the expected value - # @param [Object] new_val the new value - # @param [Boolean] expected_mark the expected mark - # @param [Boolean] new_mark the new mark - # - # @return [Boolean] `true` if successful. A `false` return indicates - # that the actual value was not equal to the expected value or the - # actual mark was not equal to the expected mark - def compare_and_set(expected_val, new_val, expected_mark, new_mark) - # Memoize a valid reference to the current AtomicReference for - # later comparison. - current = reference - curr_val, curr_mark = current - - # Ensure that that the expected marks match. - return false unless expected_mark == curr_mark - - if expected_val.is_a? Numeric - # If the object is a numeric, we need to ensure we are comparing - # the numerical values - return false unless expected_val == curr_val - else - # Otherwise, we need to ensure we are comparing the object identity. - # Theoretically, this could be incorrect if a user monkey-patched - # `Object#equal?`, but they should know that they are playing with - # fire at that point. - return false unless expected_val.equal? curr_val - end + prospect = immutable_array(new_val, new_mark) - prospect = immutable_array(new_val, new_mark) + compare_and_set_reference current, prospect + end - compare_and_set_reference current, prospect - end - alias_method :compare_and_swap, :compare_and_set + alias_method :compare_and_swap, :compare_and_set - # Gets the current reference and marked values. - # - # @return [Array] the current reference and marked values - def get - reference - end + # Gets the current reference and marked values. + # + # @return [Array] the current reference and marked values + def get + reference + end - # Gets the current value of the reference - # - # @return [Object] the current value of the reference - def value - reference[0] - end + # Gets the current value of the reference + # + # @return [Object] the current value of the reference + def value + reference[0] + end - # Gets the current marked value - # - # @return [Boolean] the current marked value - def mark - reference[1] - end - alias_method :marked?, :mark - - # _Unconditionally_ sets to the given value of both the reference and - # the mark. - # - # @param [Object] new_val the new value - # @param [Boolean] new_mark the new mark - # - # @return [Array] both the new value and the new mark - def set(new_val, new_mark) - self.reference = immutable_array(new_val, new_mark) - end + # Gets the current marked value + # + # @return [Boolean] the current marked value + def mark + reference[1] + end - # Pass the current value and marked state to the given block, replacing it - # with the block's results. May retry if the value changes during the - # block's execution. - # - # @yield [Object] Calculate a new value and marked state for the atomic - # reference using given (old) value and (old) marked - # @yieldparam [Object] old_val the starting value of the atomic reference - # @yieldparam [Boolean] old_mark the starting state of marked - # - # @return [Array] the new value and new mark - def update - loop do - old_val, old_mark = reference - new_val, new_mark = yield old_val, old_mark - - if compare_and_set old_val, new_val, old_mark, new_mark - return immutable_array(new_val, new_mark) - end - end - end + alias_method :marked?, :mark - # Pass the current value to the given block, replacing it - # with the block's result. Raise an exception if the update - # fails. - # - # @yield [Object] Calculate a new value and marked state for the atomic - # reference using given (old) value and (old) marked - # @yieldparam [Object] old_val the starting value of the atomic reference - # @yieldparam [Boolean] old_mark the starting state of marked - # - # @return [Array] the new value and marked state - # - # @raise [Concurrent::ConcurrentUpdateError] if the update fails - def try_update! + # _Unconditionally_ sets to the given value of both the reference and + # the mark. + # + # @param [Object] new_val the new value + # @param [Boolean] new_mark the new mark + # + # @return [Array] both the new value and the new mark + def set(new_val, new_mark) + self.reference = immutable_array(new_val, new_mark) + end + + # Pass the current value and marked state to the given block, replacing it + # with the block's results. May retry if the value changes during the + # block's execution. + # + # @yield [Object] Calculate a new value and marked state for the atomic + # reference using given (old) value and (old) marked + # @yieldparam [Object] old_val the starting value of the atomic reference + # @yieldparam [Boolean] old_mark the starting state of marked + # + # @return [Array] the new value and new mark + def update + loop do old_val, old_mark = reference new_val, new_mark = yield old_val, old_mark - unless compare_and_set old_val, new_val, old_mark, new_mark - fail ::Concurrent::ConcurrentUpdateError, - 'AtomicMarkableReference: Update failed due to race condition.', - 'Note: If you would like to guarantee an update, please use ' \ - 'the `AtomicMarkableReference#update` method.' + if compare_and_set old_val, new_val, old_mark, new_mark + return immutable_array(new_val, new_mark) end + end + end - immutable_array(new_val, new_mark) + # Pass the current value to the given block, replacing it + # with the block's result. Raise an exception if the update + # fails. + # + # @yield [Object] Calculate a new value and marked state for the atomic + # reference using given (old) value and (old) marked + # @yieldparam [Object] old_val the starting value of the atomic reference + # @yieldparam [Boolean] old_mark the starting state of marked + # + # @return [Array] the new value and marked state + # + # @raise [Concurrent::ConcurrentUpdateError] if the update fails + def try_update! + old_val, old_mark = reference + new_val, new_mark = yield old_val, old_mark + + unless compare_and_set old_val, new_val, old_mark, new_mark + fail ::Concurrent::ConcurrentUpdateError, + 'AtomicMarkableReference: Update failed due to race condition.', + 'Note: If you would like to guarantee an update, please use ' + + 'the `AtomicMarkableReference#update` method.' end - # Pass the current value to the given block, replacing it with the - # block's result. Simply return nil if update fails. - # - # @yield [Object] Calculate a new value and marked state for the atomic - # reference using given (old) value and (old) marked - # @yieldparam [Object] old_val the starting value of the atomic reference - # @yieldparam [Boolean] old_mark the starting state of marked - # - # @return [Array] the new value and marked state, or nil if - # the update failed - def try_update - old_val, old_mark = reference - new_val, new_mark = yield old_val, old_mark + immutable_array(new_val, new_mark) + end + + # Pass the current value to the given block, replacing it with the + # block's result. Simply return nil if update fails. + # + # @yield [Object] Calculate a new value and marked state for the atomic + # reference using given (old) value and (old) marked + # @yieldparam [Object] old_val the starting value of the atomic reference + # @yieldparam [Boolean] old_mark the starting state of marked + # + # @return [Array] the new value and marked state, or nil if + # the update failed + def try_update + old_val, old_mark = reference + new_val, new_mark = yield old_val, old_mark - return unless compare_and_set old_val, new_val, old_mark, new_mark + return unless compare_and_set old_val, new_val, old_mark, new_mark - immutable_array(new_val, new_mark) - end + immutable_array(new_val, new_mark) + end - private + private - def immutable_array(*args) - args.freeze - end + def immutable_array(*args) + args.freeze end end end diff --git a/lib/concurrent/edge/lock_free_linked_set/node.rb b/lib/concurrent/edge/lock_free_linked_set/node.rb index 20506e1b2..d7fafa370 100644 --- a/lib/concurrent/edge/lock_free_linked_set/node.rb +++ b/lib/concurrent/edge/lock_free_linked_set/node.rb @@ -5,7 +5,6 @@ module Edge class LockFreeLinkedSet class Node < Synchronization::Object include Comparable - include Concurrent::Atomic safe_initialization! diff --git a/spec/concurrent/atomic/atomic_markable_reference_spec.rb b/spec/concurrent/atomic/atomic_markable_reference_spec.rb index 1bfdd6897..6ffab9d13 100644 --- a/spec/concurrent/atomic/atomic_markable_reference_spec.rb +++ b/spec/concurrent/atomic/atomic_markable_reference_spec.rb @@ -1,4 +1,4 @@ -RSpec.describe Concurrent::Atomic::AtomicMarkableReference do +RSpec.describe Concurrent::AtomicMarkableReference do subject { described_class.new 1000, true } describe '.initialize' do