Skip to content

burrows99/goroutine-lab

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Goroutine Lab

Go React License: MIT standard-readme compliant PRs Welcome

Learn Go concurrency: 27 real patterns run in slow motion, and you step through the source like a debugger.

Goroutine Lab is a hands-on visualizer for Go concurrency. Every pattern is genuine Go — real goroutines, channels, select, context, and sync primitives — instrumented so your browser can animate what the runtime is actually doing: goroutines starting and blocking, values flowing through channels, mutexes being held, contexts cancelling.

Then it goes one step further. You can step through the Go source like a debugger — set breakpoints on real statements, single-step, and watch the highlighted line move in lock-step with the animation. The instrumentation is hidden, so what you read is the clean, interview-defensible concept code.

The Go module is named goroutinelab; the repository is goroutine-lab.

┌────────────┐   goroutines run for real    ┌──────────────────────┐
│ Go backend │ ─────  events over SSE  ────▶ │ React (SVG) live viz │
│ 27 demos   │  go_start / chan_send / …     │  + source debugger   │
└────────────┘                               └──────────────────────┘
default.demo-upload.mp4

One pattern from each of the six categories — goroutines/WaitGroup, a buffered channel, select, a worker pool, a mutex, and context cancellation — each run end-to-end with the Go source stepping alongside.

Table of Contents

Background

Concurrency is the part of Go that's easy to write and hard to reason about. Textbooks show the code; they can't show the interleaving — which goroutine is parked, what's sitting in a channel buffer, who holds the lock, when a context cancellation actually propagates.

Goroutine Lab makes that interleaving visible. Each demo narrates itself through a small Tracer, and the backend paces the run with real, cancellable sleeps so it unfolds over a few seconds instead of microseconds. Because the demos are real Go (not a simulation), what you watch is the code that runs — and the source debugger maps every animation step back to the exact line that produced it, using Go's own go/ast to keep the mapping honest.

It's built as a companion for learning Go concurrency deeply enough to defend every line in an interview.

  • Backend: Go standard library only — no third-party modules.
  • Transport: Server-Sent Events (one dependency-free, long-lived HTTP response) rather than WebSocket.
  • Frontend: React + Vite, dependency-free SVG (no charting/animation libs).

Install

Prerequisites: Go 1.23+ and Node 20+ for local development, or Docker for the one-command path.

git clone https://github.com/burrows99/goroutine-lab.git
cd goroutine-lab

Docker (one command)

docker compose up --build
# frontend → http://localhost:5173
# backend  → http://localhost:8080

Local (two terminals)

# terminal 1 — backend (Go 1.23+)
cd backend
go run ./cmd/server            # or: go run -race ./cmd/server

# terminal 2 — frontend (Node 20+)
cd frontend
npm install
npm run dev                    # opens http://localhost:5173

A Makefile wraps the common tasks: make up (Docker), make backend, make race, make frontend, make vet, make build.

The frontend proxies /api and /healthz to the backend, so you only open :5173 in the browser.

Usage

  1. Open http://localhost:5173 and pick a pattern from the sidebar.
  2. Press Run. Watch the goroutines, channels, and messages come alive: green = running, amber = blocked, grey = done; tokens fly along channels; sync-primitive nodes light up as they're held.
  3. Open the Go source tab to read the pattern — and drive it like a debugger.

The source debugger

  • Live highlighting — as the run plays, the currently-executing Go statement is highlighted and auto-scrolled into view.
  • Step controls — a transport bar with restart · back · play · step · step-over. Stepping advances one concept line at a time (several runtime events on one line collapse into a single step), so the highlight always moves in step with the picture.
  • Breakpoints — click the gutter next to any line that actually runs a step (a send, a receive, a spawn, a lock, the critical section, a cancel()…) to set a breakpoint; playback halts exactly on that line, and Play doubles as continue to next breakpoint. The dot never moves and never lands on a brace, a declaration, or a dead line — the breakpoint stops precisely where it sits.
  • Clean concept view — the tracer hooks (t.Spawn, t.Send, …) live on their own lines and are stripped from the displayed source, along with the t *trace.Tracer parameter and the pacing calls, so what you read is pure, idiomatic Go — no instrumentation leaks in. Every event is mapped onto the exact statement it narrates (the hook sits directly on that statement), so highlight and breakpoints are precise, not heuristic. The same pure source is also materialized into backend/internal/concepts/ — one compilable package per pattern — so you can read (and go build) the concept on its own.

Each pattern also ships How it works and Interview notes tabs.

The patterns

27 runnable demos across six categories:

  • Goroutines & WaitGroups — goroutines + sync.WaitGroup
  • Channels — unbuffered (rendezvous) · buffered · close + range + comma-ok
  • Select — multiplex · non-blocking (default) · timeout (time.After) · ticker
  • Core Patterns — generator · pipeline · fan-out/fan-in · worker pool · semaphore (bounded concurrency) · rate limiting (token bucket) · or-done · tee · pub/sub
  • Sync Primitives — mutex & the data race · RWMutex · sync.Once · atomics · sync.Cond
  • Context & LifecycleWithCancel · WithTimeout · errgroup (bounded, first-error-cancels) · graceful shutdown · goroutine leak (and the fix)

Architecture

backend/
  cmd/server/main.go            entrypoint (graceful shutdown via signal.NotifyContext)
  cmd/genconcepts/main.go       generates internal/concepts/ from the instrumented demos
  internal/trace/trace.go       the dedicated event library (Tracer, Event, source-line capture)
  internal/patterns/*.go        one file per demo (+ registry.go: catalog + go/ast strip/map)
  internal/patterns/helpers.go  pace(): the pure, speed-scaled sleep (no tracer)
  internal/concepts/<name>/     generated pure-Go concept, one compilable package per pattern
  internal/server/server.go     catalog + SSE routes
frontend/
  src/App.tsx                   layout, run orchestration, the debugger play-head
  src/api.ts                    SSE stream client (fetch + AbortController)
  src/components/Visualizer.tsx animated SVG topology engine
  src/components/CodeBlock.tsx   source view: line highlight + breakpoint gutter
  src/components/*.tsx           sidebar, controls, transport, event log, info panel
docker-compose.yml              backend + frontend, one command
  • trace — the dedicated event library. A Tracer that pattern code calls to narrate itself (Spawn, Send, Recv, Block, Lock, Cancel, …); each call becomes a JSON Event stamped with the source line it came from. Safe for concurrent use. Pacing is deliberately not here — the watchable, cancellable delay is patterns.pace(ctx, d), a tracer-free helper that scales by the run speed carried on the context — so the concept code stays instrumentation-free.
  • patterns — each demo registers itself and exposes Run(ctx, *Tracer). Narration hooks are written as their own statements sitting directly on the concept operation they describe. The source is //go:embed-ed; at catalog time registry.go drops the hook lines and the residual tracer parameter and maps each event to the exact adjacent statement — an exact, non-heuristic map. The same strip feeds cmd/genconcepts, which writes the pure concept out to internal/concepts/ (go generate ./...).
  • serverGET /api/patterns returns the catalog; GET /api/stream?pattern=…&speed=… runs one pattern and streams its events as SSE. The run is bound to the request context, so aborting the fetch (the Cancel button, or closing the tab) cancels the context and the pattern unwinds — a live cancellation demo in itself.

Maintainers

@burrows99 — Raunak Burrows.

Contributing

Issues and PRs are welcome — see CONTRIBUTING.md. Adding a new concurrency pattern is a great first contribution; the guide walks through it. Ask questions via GitHub issues.

License

MIT © Raunak Burrows.

About

Learn Go concurrency: 27 real patterns run in slow motion, and you step through the source like a debugger.

Topics

Resources

License

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Sponsor this project

 

Packages

 
 
 

Contributors