The current design of Go’s runtime assumes that the programmer is responsible for detecting when to terminate a goroutine and when to terminate the program. A program can be terminated in a normal way by calling os.Exit or by returning from the main() function. There are a lot of ways of blocking runtime forever, I will show all of them for better understanding of blocking in Go.

1. Using sync.WaitGroup

Wait blocks until the WaitGroup counter is zero.

package main

import "sync"

func main() {
    var wg sync.WaitGroup
    wg.Add(1)
    wg.Wait()
}

2. Empty select

An empty select{} statement blocks indefinitely i.e. forever. It is similar and in practice equivalent to an empty for{} statement.

package main

func main() {
    select{}
}

3. Infinite loop

The easiest way which will use 100% of CPU.

package main

func main() {
    for {}
}

4. Using sync.Mutex

If the lock is already in use, the calling goroutine blocks until the mutex is available.

package main

import "sync"

func main() {
    var m sync.Mutex
	m.Lock()
    m.Lock()
}

5. Empty Channel

Empty channels will block until there is something to receive.

package main

func main() {
	c := make(chan struct{})
    <-c
}

6. Nil Channel

Works for channels created without make.

package main

func main() {
	var c chan struct
    <-c
}

Conclusion

I have found 6 ways to block a Go program. It can be useful when you start multiple goroutines in a main() function and don’t want to terminate a whole program after that. But some of these examples are just for fun.

If you know another way - please share it in comments, I will add it here.