Record how many awaited tasks arrived at a wait-group barrier and report status.

completion barrier A `sync.WaitGroup` tracks how many tasks a barrier still waits for. `Add` sets the expected count and each `Done` lowers it. Calling `Done` a selected number of times in one goroutine keeps the report deterministic: the barrier is released only when every expected task has arrived, so the status moves from waiting through partial to released as the pending count reaches zero.

Wait Group Barrier Coordination Report

arrivals
waitgroup_barrier_coordination_report.go
Replay: real traced execution (multi-file project)
package main

import (
	"fmt"
	"sync"
)

func main() {
	var arrivals = 2
	expected := 3
	var wg sync.WaitGroup
	wg.Add(expected)
	done := 0
	for i := 0; i < arrivals; i++ {
		wg.Done()
		done++
	}
	pending := expected - done
	var status string
	if done == 0 {
		status = "waiting"
	} else if pending > 0 {
		status = "partial"
	} else {
		wg.Wait()
		status = "released"
	}
	fmt.Printf("expected=%d done=%d pending=%d %s\n", expected, done, pending, status)
}
package main

import (
	"fmt"
	"sync"
)

func main() {
	var arrivals = 0
	expected := 3
	var wg sync.WaitGroup
	wg.Add(expected)
	done := 0
	for i := 0; i < arrivals; i++ {
		wg.Done()
		done++
	}
	pending := expected - done
	var status string
	if done == 0 {
		status = "waiting"
	} else if pending > 0 {
		status = "partial"
	} else {
		wg.Wait()
		status = "released"
	}
	fmt.Printf("expected=%d done=%d pending=%d %s\n", expected, done, pending, status)
}
package main

import (
	"fmt"
	"sync"
)

func main() {
	var arrivals = 3
	expected := 3
	var wg sync.WaitGroup
	wg.Add(expected)
	done := 0
	for i := 0; i < arrivals; i++ {
		wg.Done()
		done++
	}
	pending := expected - done
	var status string
	if done == 0 {
		status = "waiting"
	} else if pending > 0 {
		status = "partial"
	} else {
		wg.Wait()
		status = "released"
	}
	fmt.Printf("expected=%d done=%d pending=%d %s\n", expected, done, pending, status)
}
  1. arrivals ← 2, expected ← 3, wg ← sync.WaitGroup{noCopy:sync.noCopy{}, state:atomic.Uint64{_:atomic.noCopy{}, _:atomic.align64{}, v:0x0}, sema:0x0}

    8func main() {9  var arrivals→ 2 = 2 //@arrivals=0, 310  expected→ 3 := 311  var wg→ sync.WaitGroup{noCopy:sync.noCopy{}, state:atomic.Uint64{_:atomic.noCopy{}, _:atomic.align64{}, v:0x0}, sema:0x0} sync.WaitGroup12  wg→ sync.WaitGroup{noCopy:sync.noCopy{}, state:atomic.Uint64{_:atomic.noCopy{}, _:atomic.align64{}, v:⟨addr A⟩}, sema:0x0}.Add(expected3)13  done→ 0 := 014  for i := 0; i < arrivals; i++ {
  2. wg ← sync.WaitGroup{noCopy:sync.noCopy{}, state:atomic.Uint64{_:atomic.noCopy{}, _:atomic.align64{}, v:⟨addr B⟩}, sema:0x0}

    pass 1 of 2
    13done := 014for i0 := 0; i < arrivals2; i++ {15  wg→ sync.WaitGroup{noCopy:sync.noCopy{}, state:atomic.Uint64{_:atomic.noCopy{}, _:atomic.align64{}, v:⟨addr B⟩}, sema:0x0}.Done()16  done→ 1++17}
  3. wg ← sync.WaitGroup{noCopy:sync.noCopy{}, state:atomic.Uint64{_:atomic.noCopy{}, _:atomic.align64{}, v:⟨addr C⟩}, sema:0x0}

    pass 2 of 2
    13done := 014for i1 := 0; i < arrivals2; i++ {15  wg→ sync.WaitGroup{noCopy:sync.noCopy{}, state:atomic.Uint64{_:atomic.noCopy{}, _:atomic.align64{}, v:⟨addr C⟩}, sema:0x0}.Done()16  done→ 2++17}
  4. pending ← 1, status ← ""

    17}18pending→ 1 := expected3 - done219var status→ "" string20if done == 0 {
  5. status ← "partial"

    21  status = "waiting"22} else if pending1 > 0 {23  status→ "partial" = "partial"24} else {
  6. fmt.Printf("expected=%d done=%d pending=%d %s ", expected, done, pendi…

    27  }28  fmt.Printf("expected=%d done=%d pending=%d %s\n", expected3, done2, pending1, status"partial")29}
    outputexpected=3 done=2 pending=1 partial
  1. arrivals ← 0, expected ← 3, wg ← sync.WaitGroup{noCopy:sync.noCopy{}, state:atomic.Uint64{_:atomic.noCopy{}, _:atomic.align64{}, v:0x0}, sema:0x0}

    8func main() {9  var arrivals→ 0 = 010  expected→ 3 := 311  var wg→ sync.WaitGroup{noCopy:sync.noCopy{}, state:atomic.Uint64{_:atomic.noCopy{}, _:atomic.align64{}, v:0x0}, sema:0x0} sync.WaitGroup12  wg→ sync.WaitGroup{noCopy:sync.noCopy{}, state:atomic.Uint64{_:atomic.noCopy{}, _:atomic.align64{}, v:⟨addr A⟩}, sema:0x0}.Add(expected3)13  done→ 0 := 014  for i := 0; i < arrivals; i++ {15    wg.Done()16    done++17  }18  pending→ 3 := expected3 - done019  var status→ "" string20  if done == 0 {
  2. status ← "waiting"

    19var status string20if done0 == 0 {21  status→ "waiting" = "waiting"22} else if pending > 0 {
  3. fmt.Printf("expected=%d done=%d pending=%d %s ", expected, done, pendi…

    27  }28  fmt.Printf("expected=%d done=%d pending=%d %s\n", expected3, done0, pending3, status"waiting")29}
    outputexpected=3 done=0 pending=3 waiting
  1. arrivals ← 3, expected ← 3, wg ← sync.WaitGroup{noCopy:sync.noCopy{}, state:atomic.Uint64{_:atomic.noCopy{}, _:atomic.align64{}, v:0x0}, sema:0x0}

    8func main() {9  var arrivals→ 3 = 310  expected→ 3 := 311  var wg→ sync.WaitGroup{noCopy:sync.noCopy{}, state:atomic.Uint64{_:atomic.noCopy{}, _:atomic.align64{}, v:0x0}, sema:0x0} sync.WaitGroup12  wg→ sync.WaitGroup{noCopy:sync.noCopy{}, state:atomic.Uint64{_:atomic.noCopy{}, _:atomic.align64{}, v:⟨addr A⟩}, sema:0x0}.Add(expected3)13  done→ 0 := 014  for i := 0; i < arrivals; i++ {
  2. wg ← sync.WaitGroup{noCopy:sync.noCopy{}, state:atomic.Uint64{_:atomic.noCopy{}, _:atomic.align64{}, v:⟨addr B⟩}, sema:0x0}

    pass 1 of 3
    13done := 014for i0 := 0; i < arrivals3; i++ {15  wg→ sync.WaitGroup{noCopy:sync.noCopy{}, state:atomic.Uint64{_:atomic.noCopy{}, _:atomic.align64{}, v:⟨addr B⟩}, sema:0x0}.Done()16  done→ 1++17}
    All 3 passes — pass 1 is the card above
    passiwgdone
    10sync.WaitGroup{noCopy:sync.noCopy{}, state:atomic.Uint64{_:atomic.noCopy{}, _:atomic.align64{}, v:⟨addr A⟩}, sema:0x0} sync.WaitGroup{noCopy:sync.noCopy{}, state:atomic.Uint64{_:atomic.noCopy{}, _:atomic.align64{}, v:⟨addr B⟩}, sema:0x0}0 1
    21sync.WaitGroup{noCopy:sync.noCopy{}, state:atomic.Uint64{_:atomic.noCopy{}, _:atomic.align64{}, v:⟨addr B⟩}, sema:0x0} sync.WaitGroup{noCopy:sync.noCopy{}, state:atomic.Uint64{_:atomic.noCopy{}, _:atomic.align64{}, v:⟨addr C⟩}, sema:0x0}1 2
    32sync.WaitGroup{noCopy:sync.noCopy{}, state:atomic.Uint64{_:atomic.noCopy{}, _:atomic.align64{}, v:⟨addr C⟩}, sema:0x0} sync.WaitGroup{noCopy:sync.noCopy{}, state:atomic.Uint64{_:atomic.noCopy{}, _:atomic.align64{}, v:0x0}, sema:0x0}2 3
  3. pending ← 0, status ← ""

    17}18pending→ 0 := expected3 - done319var status→ "" string20if done == 0 {
  4. status ← "released"

    23  status = "partial"24} else {25  wgsync.WaitGroup{noCopy:sync.noCopy{}, state:atomic.Uint64{_:atomic.noCopy{}, _:atomic.align64{}, v:0x0}, sema:0x0}.Wait()26  status→ "released" = "released"27}
  5. fmt.Printf("expected=%d done=%d pending=%d %s ", expected, done, pendi…

    27  }28  fmt.Printf("expected=%d done=%d pending=%d %s\n", expected3, done3, pending0, status"released")29}
    outputexpected=3 done=3 pending=0 released