Fundamentals 6 min read

Mastering Go’s sync.Cond: Coordinating Goroutine Execution with Condition Variables

This article explains how Go's sync.Cond works, demonstrates its usage with practical code examples, analyzes its internal implementation, and shows a real‑world application in Kubernetes FIFO queues, helping developers control goroutine synchronization effectively.

Radish, Keep Going!
Radish, Keep Going!
Radish, Keep Going!
Mastering Go’s sync.Cond: Coordinating Goroutine Execution with Condition Variables
Cond’s main purpose is to acquire a lock, then wait for a notification via wait(), allowing controlled release and frequency of the lock in concurrent environments.

Example

Here is a basic usage of Cond:

package main

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

var locker = new(sync.Mutex)
var cond = sync.NewCond(locker)

func main() {
    for i := 0; i < 40; i++ {
        go func(x int) {
            cond.L.Lock()
            defer cond.L.Unlock()
            cond.Wait()
            fmt.Println(x)
            time.Sleep(time.Second * 1)
        }(i)
    }

    time.Sleep(time.Second * 1)
    fmt.Println("Signal...")
    cond.Signal()
    time.Sleep(time.Second * 1)
    cond.Signal()
    time.Sleep(time.Second * 3)
    cond.Broadcast()
    fmt.Println("Broadcast...")
    time.Sleep(time.Second * 60)
}

Cond Structure

type Cond struct {
    noCopy noCopy
    L      Locker
    notify notifyList
    checker copyChecker
}

notifyList Structure

type notifyList struct {
    wait   uint32
    notify uint32
    lock   uintptr
    head   unsafe.Pointer
    tail   unsafe.Pointer
}

NewCond

func NewCond(l Locker) *Cond {
    return &Cond{L: l}
}

Wait

func (c *Cond) Wait() {
    c.checker.check()
    t := runtime_notifyListAdd(&c.notify)
    c.L.Unlock()
    runtime_notifyListWait(&c.notify, t)
    c.L.Lock()
}

Signal

func (c *Cond) Signal() {
    c.checker.check()
    runtime_notifyListNotifyOne(&c.notify)
}

Broadcast

func (c *Cond) Broadcast() {
    c.checker.check()
    runtime_notifyListNotifyAll(&c.notify)
}

In Kubernetes, Cond is used to implement a FIFO queue that coordinates condition consumption.

Cond diagram
Cond diagram

Real‑world FIFO Example

func (f *FIFO) Pop(process PopProcessFunc) (interface{}, error) {
    f.lock.Lock()
    defer f.lock.Unlock()
    for {
        if len(f.queue) == 0 {
            // block until new item is enqueued or closed
            f.cond.Wait()
        }
        if f.IsClosed() {
            return nil, FIFOClosedError
        }
        // retrieve and remove the first element
        id := f.queue[0]
        f.queue = f.queue[1:]
        // process the item
        // ...
    }
}

func NewFIFO(keyFunc KeyFunc) *FIFO {
    f := &FIFO{
        items:   map[string]interface{}{},
        queue:   []string{},
        keyFunc: keyFunc,
    }
    f.cond = sync.NewCond(&f.lock)
    return f
}

The FIFO shares the same lock with Cond; before calling f.cond.Wait() it checks whether the queue length is zero to avoid waiting when the condition is already satisfied or to prevent race conditions that could cause out‑of‑bounds access.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendconcurrencyGocondition variableGoroutinesync.Cond
Radish, Keep Going!
Written by

Radish, Keep Going!

Personal sharing

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.