Weekly Review — Month 3 · Week 2 (Days 064–070)¶
📅 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.WaitGroupin depth; lock-free collection;errors.Joinaggregation - Day 066 —
sync.Once(andOnceValue) &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 Unlockimmediately afterLockso 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.Oncenever retries (even on panic) and never resets; for once-per-key, pairsync.Map.LoadOrStorewith a per-keyOnce.sync.Poolis an allocation optimisation, not a cache — GC may empty it; alwaysResetbefore reuse and copy out beforePut.- 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 Unlockrhythm and theRWMutexread/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
RWMutextruly beatsMutex, 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)¶
- Q: (M1) What does
string(65)produce and why?A
"A"— int→rune to code point U+0041, not"65". Usestrconv.Itoa. - Q: (M2) What does
%winfmt.Errorfdo?A
Wraps an error soerrors.Is/errors.Ascan unwrap and match it. - Q: (M3 W1) The four
closepanic/zero cases?A
Send on closed, close a closed, close a nil → panic; receive on closed → zero value,ok==false. - Q: (M3 W1) Two ready
selectcases — which runs?A
One chosen uniformly at random; no priority. - Q: (M3 W2) Why must
wg.Addprecedego?A
Addinside the goroutine races withWait, which could return early on a zero counter.
🎯 Action Items¶
- Benchmark
MutexvsRWMutexvsatomiccounter 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
-racereport end to end.
🚀 Next Week Goals¶
context.Context:WithCancel/WithTimeout/WithDeadline, propagation, the context-as-first-param convention.context.Valueand 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