1. Thread::
  2. ConditionVariable

class Thread::ConditionVariable

ConditionVariable objects augment classMutex. Using condition variables, it is possible to suspend while in the middle of a critical section until a condition is met, such as a resource becomes available.

Due to non-deterministic scheduling and spurious wake-ups, users of condition variables should always use a separate boolean predicate (such as reading from a boolean variable) to check if the condition is actually met before starting to wait, and should wait in a loop, re-checking the condition every time theConditionVariable is waken up. The idiomatic way of using condition variables is calling thewait method in anuntil loop with the predicate as the loop condition.

condvar.wait(mutex)untilcondition_is_met

In the example below, we use the boolean variableresource_available (which is protected bymutex) to indicate the availability of the resource, and usecondvar to wait for that variable to become true. Note that:

  1. Threadb may be scheduled before threada1 anda2, and may run so fast that it have already made the resource available before eithera1 ora2 starts. Therefore,a1 anda2 should check ifresource_available is already true before starting to wait.

  2. Thewait method may spuriously wake up without signalling. Therefore, threada1 anda2 should recheckresource_available after thewait method returns, and go back to wait if the condition is not actually met.

  3. It is possible that threada2 starts right after threada1 is waken up byb.Threada2 may have acquired themutex and consumed the resource before threada1 acquires themutex. This necessitates rechecking afterwait, too.

Example:

mutex =Thread::Mutex.newresource_available =falsecondvar =Thread::ConditionVariable.newa1 =Thread.new {# Thread 'a1' waits for the resource to become available and consumes# the resource.mutex.synchronize {condvar.wait(mutex)untilresource_available# After the loop, 'resource_available' is guaranteed to be true.resource_available =falseputs"a1 consumed the resource"  }}a2 =Thread.new {# Thread 'a2' behaves like 'a1'.mutex.synchronize {condvar.wait(mutex)untilresource_availableresource_available =falseputs"a2 consumed the resource"  }}b =Thread.new {# Thread 'b' periodically makes the resource available.loop {mutex.synchronize {resource_available =true# Notify one waiting thread if any.  It is possible that neither# 'a1' nor 'a2 is waiting on 'condvar' at this moment.  That's OK.condvar.signal    }sleep1  }}# Eventually both 'a1' and 'a2' will have their resources, albeit in an# unspecified order.[a1,a2].each {|th|th.join}

Public Class Methods

Source
static VALUErb_condvar_initialize(VALUE self){    struct rb_condvar *cv = condvar_ptr(self);    ccan_list_head_init(&cv->waitq);    return self;}

Creates a new condition variable instance.

Public Instance Methods

Source
static VALUErb_condvar_broadcast(VALUE self){    struct rb_condvar *cv = condvar_ptr(self);    wakeup_all(&cv->waitq);    return self;}

Wakes up all threads waiting for this lock.

Source
static VALUErb_condvar_signal(VALUE self){    struct rb_condvar *cv = condvar_ptr(self);    wakeup_one(&cv->waitq);    return self;}

Wakes up the first thread in line waiting for this lock.

Source
static VALUErb_condvar_wait(int argc, VALUE *argv, VALUE self){    rb_execution_context_t *ec = GET_EC();    struct rb_condvar *cv = condvar_ptr(self);    struct sleep_call args;    rb_scan_args(argc, argv, "11", &args.mutex, &args.timeout);    struct sync_waiter sync_waiter = {        .self = args.mutex,        .th = ec->thread_ptr,        .fiber = nonblocking_fiber(ec->fiber_ptr)    };    ccan_list_add_tail(&cv->waitq, &sync_waiter.node);    return rb_ensure(do_sleep, (VALUE)&args, delete_from_waitq, (VALUE)&sync_waiter);}

Releases the lock held inmutex and waits; reacquires the lock on wakeup.

Iftimeout is given, this method returns aftertimeout seconds passed, even if no other thread doesn’t signal.

This method may wake up spuriously due to underlying implementation details.

Returns the slept result onmutex.