condition_variable / shared variable / Signalling

What is cond variable?
Thread1 notifies/signals Thread2 for condition. Then thread2 starts executes, until then thread2 is blocked
Advantages?
1. Avoids busy waiting, ie done by spinlock
2. Similar to [Semaphores]

How condition_variable is implemented internally

Implemented using
pthread_cond_wait pthread_cond_t
notify_one() sys_futex(&cond_sequence, FUTEX_WAKE_PRIVATE, 1)
kernel moves one thread from the futex wait-queue to the OS runnable queue.
notify_all() sys_futex(&cond_sequence, FUTEX_WAKE_PRIVATE, INT_MAX)
kernel wakes all threads waiting on that futex address

Code: Ping Pong

POSIX(pthread_cond_wait) C++11 (2 producers 2 consumers)
Code
pthread_cond_t cond_ping = PTHREAD_COND_INITIALIZER;
pthread_cond_t cond_pong = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int j=0;

void *pong(void* arg) {
    while (j < 10) {
        pthread_mutex_lock(&mutex);
        pthread_cond_wait(&cond_pong, &mutex);
        ++j;
        cout << "Pong" << ",j:"<< j << "\n";
        pthread_mutex_unlock(&mutex);
        pthread_cond_signal(&cond_ping);
    }
}
void *ping(void* arg) {
    while( j < 10) {
        sleep(1);
        pthread_cond_signal(&cond_pong);
        pthread_mutex_lock(&mutex);
        ++j;
        cout << "Ping" << ",j:" << j << "\n";
        pthread_cond_wait(&cond_ping, &mutex);
        pthread_mutex_unlock(&mutex);
    }
}
int main(){
    pthread_t tid1,tid2;
    pthread_create(&tid1, 0, ping, 0);
    pthread_create(&tid2, 0, pong, 0);

    pthread_join(tid1, 0);
    pthread_join(tid2, 0);
}
$ ./a.out
Ping,j:1
Pong,j:2
Ping,j:3
Pong,j:4
Ping,j:5
Pong,j:6
Ping,j:7
Pong,j:8
Ping,j:9
Pong,j:10
thread1,2 prints ping and then signal on condition variable.
thread3,4 waits for signal and then only prints pong
notify_all() notify all of threads waiting on
condition variable (test).
mutex m;
condition_variable cv;

void ping() {
    unique_lock <mutex> ul(m);

    // Any thread entering is blocked on condition_variable cv
    // !test means test should be false
    cv.wait(ul, []()
            { return !test; });
    cout << "ping\n";
    test = true;
    cv.notify_all();
}
void pong() {
    unique_lock <mutex< ul(m);

    // Any thread entering is blocked on condition_variable cv
    // For thread to enter Critical Section, test should be true
    // test is condition predicate
    cv.wait(ul, []
            { return test; });
    test = false;
    cout << "pong\n";
}

int main() {
  int num_of_threads = 4;
  thread t[num_of_threads];
  t[0] = thread(pong);
  t[1] = thread(pong);
  t[2] = thread(ping);
  t[3] = thread(ping);

  for (auto i = 0; i < num_of_threads; i++)
      t[i].join();
  return 0;
}
$ ./a.out
ping
pong
ping
pong

Condition Variable in Rust


bStop: bool = false;
mtx: Mutex<bool> = Mutex::new(false);
cv: Condvar = Condvar::new();
fn foo()
    {
        // After acquiring lock, data inside mutex can be changed
        let mut m = mtx.lock().unwrap();

        // Wait until b_enter_f becomes true
        while !bStop {
            state = cv.wait(m).unwrap();
        }
        // Do Some work

        cv.notify_all();
    }
      

Condition Variable in Golang

sync.Cond. It allows goroutines to wait for a specific condition or signal other goroutines when that condition changes.
Simple Producer/Consumer Example

package main

import (
	"fmt"
	"sync"
	"time"
)

func main() {
	var mu sync.Mutex           //mutex
	cond := sync.NewCond(&mu)   //condition variable
	ready := false

	go func() {     //goroutine that waits for the condition
		mu.Lock()
		defer mu.Unlock()

		for !ready {
			fmt.Println("[Consumer] Not ready yet. Waiting...")
			cond.Wait()       // Suspends execution and releases the lock
		}

		fmt.Println("[Consumer] Ready signal received! Processing data...")
	}()

	// Simulate some work before updating the condition
	time.Sleep(2 * time.Second)

	// Update the condition and signal the waiting goroutine
	mu.Lock()
	ready = true
	fmt.Println("[Producer] Data is ready. Signaling consumer...")
	cond.Signal() // Wakes up one waiting goroutine
	mu.Unlock()

}