Install
$ agentstack add skill-muratmirgun-gophers-go-concurrency ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Go Concurrency
Goroutines are cheap, but every one you spawn is a resource you must own. The goal is structured concurrency: each goroutine has a clear owner, a predictable exit, and a way for the caller to wait and collect errors.
Core Rules
- Never start a goroutine without knowing how it will stop. A blocked goroutine is not garbage-collected — it leaks.
- The caller must be able to wait. Use
sync.WaitGroup,errgroup.Group, or an explicit done channel. - No goroutines in
init(). ExposeStart/Stop/Shutdownso callers control the lifecycle. - Share by communicating. Default to channels; reach for
sync.Mutexonly when the problem is genuinely "protect a shared field". - Only the sender closes a channel. Closing from the receiver side panics on the next send.
- Specify channel direction (
chan Read [references/sync-primitives.md](references/sync-primitives.md) when picking between mutex, atomic,sync.Map,sync.Pool, orsingleflight`, or when designing the field layout of a struct that protects shared state.
Goroutine Lifetimes
// Good: bounded WaitGroup, deterministic exit
var wg sync.WaitGroup
for item := range queue {
wg.Add(1)
go func(it Item) { defer wg.Done(); process(ctx, it) }(item)
}
wg.Wait()
// Bad: no stop signal, no wait — classic leak
go func() { for { flush(); time.Sleep(delay) } }()
Go 1.25+ exposes wg.Go(fn) which folds Add/Done into one call. Always call wg.Add before go — otherwise wg.Wait may return before the goroutine even starts.
errgroup: Errors and Cancellation
errgroup.WithContext is the right default when sibling goroutines should cancel each other on the first failure:
g, ctx := errgroup.WithContext(ctx)
g.SetLimit(8)
for _, url := range urls {
g.Go(func() error { return fetch(ctx, url) })
}
if err := g.Wait(); err != nil {
return fmt.Errorf("fetching urls: %w", err)
}
g.Wait returns the first non-nil error; ctx is cancelled as soon as any worker fails. See [references/errgroup-and-pools.md](references/errgroup-and-pools.md).
Channels
func produce(out chan Read [references/channels-and-select.md](references/channels-and-select.md) when implementing pipelines, fan-in/fan-out, broadcast via `close`, or non-blocking sends with `default`.
## Mutexes and Atomics
The zero value of `sync.Mutex`/`RWMutex` is valid — almost never use a pointer. Do not embed mutexes; keep them as an unexported `mu` field so `Lock`/`Unlock` aren't public API. Keep critical sections short; never hold a lock across I/O. Prefer typed atomics (`atomic.Bool`, `atomic.Int64`) over raw `sync/atomic` on `int32`/`int64` fields.
## Testing: goleak and synctest
Wire `go.uber.org/goleak` into every package that spawns goroutines (`goleak.VerifyTestMain(m)` or `defer goleak.VerifyNone(t)`). For timer-dependent tests, use `testing/synctest` (Go 1.25+) so synthetic time advances deterministically. Go 1.26 adds an experimental `goroutineleak` pprof profile for production diagnosis — it is not a substitute for `goleak` in tests. See [references/leaks-and-synctest.md](references/leaks-and-synctest.md).
## Anti-Patterns
| Anti-pattern | Why it hurts | Do this instead |
|---|---|---|
| Fire-and-forget `go func()` with no signal | Leaks on shutdown; can outlive its inputs | Pass `ctx`, use `errgroup`, or own a done channel |
| Closing a channel from the receiver | Panics on the next send | Only the sender closes |
| `time.After` in a hot loop | Allocates a timer per iteration | `time.NewTimer` + `Reset` |
| `select` without `ctx.Done()` | Cannot be cancelled | Always include the cancel case |
| `wg.Add(1)` inside the goroutine | `Wait` may return before `Add` runs | `Add` before `go`, or use `wg.Go` (Go 1.25+) |
| Buffered channel sized "to be safe" | Hides backpressure, masks bugs | Size 0 or 1; justify anything larger |
| Concurrent read+write on `map` | Hard runtime crash, not a race warning | `sync.Map` or `sync.RWMutex` + map |
| Mutex held across I/O / RPC | Serializes the whole service | Copy what you need under the lock; release before the call |
| Sending a pointer through a channel | Re-introduces shared memory | Send a copy or an immutable value |
| Forgetting `-race` in CI | Races ship to prod | `go test -race ./...` always |
## Verification Checklist
Before finishing a concurrency change:
- [ ] Every `go` has a documented exit (ctx, done channel, or bounded loop)
- [ ] Every long-running `select` has a `<-ctx.Done()` case
- [ ] `wg.Add` is called before `go`, or `wg.Go` is used (Go 1.25+)
- [ ] Channels are sized 0 or 1, or the size has a comment justifying it
- [ ] Only the sender closes channels; receivers use `for v := range ch` or `v, ok := <-ch`
- [ ] No mutex is held across network/disk I/O
- [ ] `go test -race ./...` is clean
- [ ] Packages that spawn goroutines wire `goleak.VerifyTestMain` or per-test `VerifyNone`
## References
- [references/sync-primitives.md](references/sync-primitives.md) — mutex vs atomic vs `sync.Map`/`Pool`/`Once`/`singleflight`
- [references/channels-and-select.md](references/channels-and-select.md) — channel ownership, direction, pipelines, non-blocking sends
- [references/errgroup-and-pools.md](references/errgroup-and-pools.md) — `errgroup`, `SetLimit`, worker pools, fan-out/fan-in
- [references/leaks-and-synctest.md](references/leaks-and-synctest.md) — `goleak`, `testing/synctest`, Go 1.26 experimental leak profile
## Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [muratmirgun](https://github.com/muratmirgun)
- **Source:** [muratmirgun/gophers](https://github.com/muratmirgun/gophers)
- **License:** MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.