Go

Implement a concurrent prime sieve with a daisy chain of goroutines. How many goroutines does it create, and how do you stop it without leaks?

Question 524HardGo 1.22 to 1.25

A generator emits 2, 3, 4, ... For every prime p it finds, main adds a filter goroutine to the chain that drops multiples of p. The first value that reaches the end of the chain is always the next prime.

func generate(ctx context.Context) <-chan int {
	ch := make(chan int)
	go func() {
		defer close(ch)
		for i := 2; ; i++ {
			select {
			case ch <- i:
			case <-ctx.Done():
				return
			}
		}
	}()
	return ch
}

func filter(ctx context.Context, in <-chan int, prime int) <-chan int {
	out := make(chan int)
	go func() {
		defer close(out)
		for v := range in { // ends when upstream closes
			if v%prime != 0 {
				select {
				case out <- v:
				case <-ctx.Done():
					return
				}
			}
		}
	}()
	return out
}

func Primes(n int) []int {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel() // tears the whole chain down
	ch := generate(ctx)
	primes := make([]int, 0, n)
	for range n {
		p := <-ch
		primes = append(primes, p)
		ch = filter(ctx, ch, p)
	}
	return primes
}

Goroutine count: one generator plus one filter per prime found, so n + 1 goroutines for n primes. The last filter is created but never read from.

Shutdown without leaks: the classic Go-talk version has no stop mechanism, so every goroutine stays blocked on a send forever, and that is a leak. With ctx, cancel() wakes every goroutine that is blocked on a send. A filter blocked on receive is released by the cascade: its upstream returns, the deferred close runs, range in ends, and the filter closes its own out. Verify this in tests with go.uber.org/goleak or runtime.NumGoroutine().

Interviewer bonus: this is not the real Sieve of Eratosthenes. Each number is trial-divided by every smaller prime, so the work is roughly O(n² / log n), and every number costs one channel hop per stage. It is elegant for showing off CSP pipelines but slow, and a plain slice-based sieve is orders of magnitude faster.

More on Classic Concurrency Coding Problems

All 16 Classic Concurrency Coding Problems questions