Class: AtomicRuby::AtomicConditionVariable

Inherits:
Object
  • Object
show all
Defined in:
lib/atomic-ruby/atomic_condition_variable.rb,
ext/atomic_ruby/atomic_ruby.c

Overview

Note:

This class is NOT Ractor-safe as it parks Thread references, which cannot be shared across ractors.

Provides lock-free wait/signal coordination using atomic operations.

AtomicConditionVariable lets one or more threads park until another thread signals them, without the paired Mutex that Ruby's ConditionVariable requires.

Waiters supply a predicate that is checked again after registration, preventing signals sent after a relevant state change from being lost.

Examples:

Basic usage

condvar = AtomicConditionVariable.new
ready = AtomicBoolean.new(false)

waiter = Thread.new do
  condvar.wait { ready.true? }
  puts "ready"
end

ready.make_true
condvar.signal

Worker loop draining an atomic queue

condvar.wait do
  work = queue.pop
  work || shutdown.true?
end

Defined Under Namespace

Classes: Waiter

Instance Method Summary collapse

Constructor Details

#initializeAtomicConditionVariable

Creates a new condition variable with no parked threads.

Examples:

condvar = AtomicConditionVariable.new

RBS:

  • () -> void



43
44
45
# File 'lib/atomic-ruby/atomic_condition_variable.rb', line 43

def initialize
  _initialize
end

Instance Method Details

#broadcastInteger

Wakes every parked waiter.

Examples:

condvar = AtomicConditionVariable.new
condvar.broadcast #=> 0

RBS:

  • () -> Integer

Returns:

  • (Integer)

    The number of waiters signalled



90
91
92
93
94
# File 'lib/atomic-ruby/atomic_condition_variable.rb', line 90

def broadcast
  threads = _drain_threads
  threads.each { |thread| thread.wakeup rescue nil }
  threads.size
end

#signaltrue, false

Wakes one parked waiter, or no-ops if none are parked.

Examples:

condvar = AtomicConditionVariable.new
condvar.signal #=> false

RBS:

  • () -> bool

Returns:

  • (true, false)

    true if a waiter was signalled, false otherwise



73
74
75
76
77
78
79
# File 'lib/atomic-ruby/atomic_condition_variable.rb', line 73

def signal
  thread = _shift_thread
  return false unless thread

  thread.wakeup rescue nil
  true
end

#waituntyped

Blocks until the given block returns a truthy value, then returns that value.

The block may run more than once and may run concurrently with a signalling thread. Rechecking it after registration prevents lost wakeups. Interrupted waits are unregistered before the exception is propagated.

Examples:

Simple wait

condvar = AtomicConditionVariable.new
ready = AtomicBoolean.new(false)

Thread.new do
  sleep(1)
  ready.make_true
  condvar.signal
end

condvar.wait { ready.true? } #=> true

RBS:

  • () { () -> untyped } -> untyped

Yield Returns:

  • (untyped)

    Truthy to wake, falsy to keep waiting

Returns:

  • (untyped)

    The first truthy value returned by the block



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/atomic-ruby/atomic_condition_variable.rb', line 120

def wait
  result = yield
  return result if result

  self_thread = Thread.current
  loop do
    Thread.handle_interrupt(Exception => :never) do
      waiter = _add_waiter(self_thread)
      begin
        Thread.handle_interrupt(Exception => :immediate) do
          result = yield
          return result if result

          Thread.stop
        end
      ensure
        _remove_waiter(waiter)
      end
    end
  end
end

#waiter_countInteger

Returns the number of currently parked waiters.

This operation is atomic and thread-safe. The returned value reflects the state at the time of the call, but may change immediately after in concurrent environments.

Examples:

condvar = AtomicConditionVariable.new
puts condvar.waiter_count #=> 0

RBS:

  • () -> Integer

Returns:

  • (Integer)

    The number of currently parked waiters



60
61
62
# File 'lib/atomic-ruby/atomic_condition_variable.rb', line 60

def waiter_count
  _waiter_count
end