1+ # rbs_inline: enabled
12# frozen_string_literal: true
23
34require_relative "atom"
45
56module AtomicRuby
7+ # Provides atomic boolean semantics with thread-safe toggle operations.
8+ #
9+ # AtomicBoolean wraps a boolean value in an atomic reference, providing
10+ # lock-free operations for common boolean manipulations like toggling,
11+ # setting to true/false, and checking the current state.
12+ #
13+ # @example Basic usage
14+ # boolean = AtomicBoolean.new(false)
15+ # puts boolean.false? #=> true
16+ # boolean.toggle
17+ # puts boolean.true? #=> true
18+ #
19+ # @example Thread-safe toggle
20+ # boolean = AtomicBoolean.new(false)
21+ # threads = 10.times.map do
22+ # Thread.new { 100.times { boolean.toggle } }
23+ # end
24+ # threads.each(&:join)
25+ # # Final state depends on whether total toggles is even or odd
26+ #
27+ # @example Atomic flag setting
28+ # flag = AtomicBoolean.new(false)
29+ # flag.make_true
30+ # puts flag.value #=> true
31+ #
32+ # @note This class is Ractor-safe in Ruby 4.0+ when compiled with ractor support.
633 class AtomicBoolean
34+ # Creates a new atomic boolean with the given initial value.
35+ #
36+ # @param boolean [true, false] The initial boolean value
37+ # @raise [ArgumentError] if the value is not a boolean (TrueClass or FalseClass)
38+ #
39+ # @example
40+ # boolean = AtomicBoolean.new(true)
41+ # boolean = AtomicBoolean.new(false)
42+ #
43+ # @example Invalid usage
44+ # AtomicBoolean.new(nil) #=> raises ArgumentError
45+ # AtomicBoolean.new("true") #=> raises ArgumentError
46+ #
47+ # @rbs (bool boolean) -> void
748 def initialize ( boolean )
849 unless boolean . is_a? ( TrueClass ) || boolean . is_a? ( FalseClass )
950 raise ArgumentError , "boolean must be a TrueClass or FalseClass"
@@ -14,26 +55,102 @@ def initialize(boolean)
1455 Ractor . make_shareable ( self ) if RACTOR_SAFE
1556 end
1657
58+ # Returns the current boolean value stored in the atom.
59+ #
60+ # This operation is atomic and thread-safe. The returned value reflects
61+ # the state at the time of the call, but may change immediately after
62+ # in concurrent environments.
63+ #
64+ # @return [true, false] The current atomic boolean value
65+ #
66+ # @example
67+ # boolean = AtomicBoolean.new(true)
68+ # puts boolean.value #=> true
69+ #
70+ # @rbs () -> bool
1771 def value
1872 @boolean . value
1973 end
2074
75+ # Tests if the current value is true.
76+ #
77+ # @return [true, false] true if the atomic value is true, false otherwise
78+ #
79+ # @example
80+ # boolean = AtomicBoolean.new(true)
81+ # puts boolean.true? #=> true
82+ # puts boolean.false? #=> false
83+ #
84+ # @rbs () -> bool
2185 def true?
2286 value == true
2387 end
2488
89+ # Tests if the current value is false.
90+ #
91+ # @return [true, false] true if the atomic value is false, false otherwise
92+ #
93+ # @example
94+ # boolean = AtomicBoolean.new(false)
95+ # puts boolean.false? #=> true
96+ # puts boolean.true? #=> false
97+ #
98+ # @rbs () -> bool
2599 def false?
26100 value == false
27101 end
28102
103+ # Atomically sets the value to true.
104+ #
105+ # This operation uses compare-and-swap to ensure atomicity,
106+ # making it safe for concurrent access.
107+ #
108+ # @return [true] Always returns true (the new value)
109+ #
110+ # @example
111+ # boolean = AtomicBoolean.new(false)
112+ # boolean.make_true
113+ # puts boolean.value #=> true
114+ #
115+ # @rbs () -> true
29116 def make_true
30117 @boolean . swap { true }
31118 end
32119
120+ # Atomically sets the value to false.
121+ #
122+ # This operation uses compare-and-swap to ensure atomicity,
123+ # making it safe for concurrent access.
124+ #
125+ # @return [false] Always returns false (the new value)
126+ #
127+ # @example
128+ # boolean = AtomicBoolean.new(true)
129+ # boolean.make_false
130+ # puts boolean.value #=> false
131+ #
132+ # @rbs () -> false
33133 def make_false
34134 @boolean . swap { false }
35135 end
36136
137+ # Atomically toggles the boolean value.
138+ #
139+ # Changes true to false and false to true using a compare-and-swap
140+ # operation, making it safe for concurrent access from multiple threads.
141+ #
142+ # @return [true, false] The new boolean value after toggling
143+ #
144+ # @example
145+ # boolean = AtomicBoolean.new(false)
146+ # boolean.toggle #=> true
147+ # boolean.toggle #=> false
148+ #
149+ # @example Thread-safe toggling
150+ # boolean = AtomicBoolean.new(false)
151+ # 10.times.map { Thread.new { boolean.toggle } }.each(&:join)
152+ #
153+ # @rbs () -> bool
37154 def toggle
38155 @boolean . swap { |current_value | !current_value }
39156 end
0 commit comments