Skip to content

Weekly Review — Month 3 · Week 2 (Days 064–070)

Journal index · Roadmap › this week

📅 The Week in One Line

The other half of concurrency: guarding shared memory with the sync toolbox (Mutex/RWMutex, WaitGroup, Once, Pool, atomics), proven correct by the memory model and caught by -race.

✅ What I Completed

  • Day 064 — sync.Mutex & RWMutex; critical sections; the no-copy rule
  • Day 065 — sync.WaitGroup in depth; lock-free collection; errors.Join aggregation
  • Day 066 — sync.Once (and OnceValue) & sync.Pool; once-per-key memoisation
  • Day 067 — sync/atomic: typed atomics, CompareAndSwap, the CAS retry loop
  • Day 068 — the race detector (-race): reading reports, go test -race ./...
  • Day 069 — the Go memory model: happens-before edges, data race = UB
  • Day 070 — review + closed-book recall
  • Examples: mutex, waitgroup, once-pool, atomic (stdlib-only, -race-clean)
  • Exercises solved: 3 (safecounter, oncecache, atomicgate)

💡 Lessons Learned

  • Channels pass ownership; mutexes/atomics guard shared state in place — pick by whether you're handing data off or sharing it.
  • Keep the lock next to the data it protects, and defer Unlock immediately after Lock so panics/early-returns still release.
  • Atomics protect exactly one word; multi-field invariants still need a Mutex. Mixing atomic and plain access to one variable is itself a race.
  • sync.Once never retries (even on panic) and never resets; for once-per-key, pair sync.Map.LoadOrStore with a per-key Once.
  • sync.Pool is an allocation optimisation, not a cache — GC may empty it; always Reset before reuse and copy out before Put.
  • A data race is undefined behaviour, not a stale read; the only fix is a happens-before edge (channel, mutex, Once, atomic, go).

💪 Strengths (what clicked)

  • The Mutex Lock/defer Unlock rhythm and the RWMutex read/write split.
  • The CAS retry loop as the lock-free update template.
  • Running go test -race ./... as a reflex and reading the two-stack report.

🧩 Weaknesses (what's still fuzzy)

  • When RWMutex truly beats Mutex, and when a CAS spin beats a mutex — both need benchmarks.
  • The formal acquire/release wording of the 2022 memory model (intuition is fine; formalism isn't).
  • Quantifying sync.Pool's allocation win (-benchmem).

🔁 Spaced-Repetition Re-quiz (topics from earlier weeks)

  1. Q: (M1) What does string(65) produce and why?
    A"A" — int→rune to code point U+0041, not "65". Use strconv.Itoa.
  2. Q: (M2) What does %w in fmt.Errorf do?
    AWraps an error so errors.Is/errors.As can unwrap and match it.
  3. Q: (M3 W1) The four close panic/zero cases?
    ASend on closed, close a closed, close a nil → panic; receive on closed → zero value, ok==false.
  4. Q: (M3 W1) Two ready select cases — which runs?
    AOne chosen uniformly at random; no priority.
  5. Q: (M3 W2) Why must wg.Add precede go?
    AAdd inside the goroutine races with Wait, which could return early on a zero counter.

🎯 Action Items

  • Benchmark Mutex vs RWMutex vs atomic counter at several read/write ratios (-bench, -benchmem).
  • Rewrite the once-per-key cache using sync.OnceValues (Go 1.21) and compare ergonomics.
  • Deliberately delete a lock and practise reading the -race report end to end.

🚀 Next Week Goals

  • context.Context: WithCancel/WithTimeout/WithDeadline, propagation, the context-as-first-param convention.
  • context.Value and why it's an anti-pattern for required dependencies.
  • Wiring cancellation into the Week 1 pipeline/worker-pool code.

📊 Metrics

Hours Days hit Exercises Commits Avg confidence
9 7/7 3 7 3.⅘

Suggested commit: docs(journal): month 3 week 2 review