Go

Go needs an explicit function that blocks main forever

Such a weird title!

Applications do, however, require the ability to have one thread block forever, specifically the main thread. As long as it stays blocked, it can never exit, as long as it never exits, the application stays running.

In Go there’s no direct, explicit, way to achieve this, there are, however, a number of ways that use side effects that do. Rather than describe each of them, I recommend reading How to block forever, by Scott Mansfield, he lists eight possible approaches, there’s also runtime.GoExit (which notes

Calling Goexit from the main goroutine terminates that goroutine without func main returning. Since func main has not returned, the program continues execution of other goroutines
```).

The current cleanest and most obvious way, in my opinion, is to use an empty select statement:
```Go
select{}

Digging deeper into the Go source, it’s possible to see which of the methods listed so far to use, and which to avoid.

waitgroup.Wait uses a busy wait loop to block, as seen in the wait function.

select has a function called block that is called when it wants to, er, block. That function calls gopark as can be seen in the Select source code.

The double Lock and Channel Read approaches are using gopark as well.

There’s something going on here, gopark needs investigation!

The runtime Hacking ReadMe states the following

To interact directly with the goroutine scheduler, use gopark and goready. gopark parks the current goroutine—putting it in the "waiting" state and removing it from the scheduler's run queue—and schedules another goroutine on the current M/P. goready puts a parked goroutine back in the "runnable" state and adds it to the run queue.

That is, the goroutine that calls gopark is placed in a queue in the scheduler and is ignored forever. There is zero CPU cost, as the scheduler isn’t going to check the parked goroutine, the goroutine is only reactivated when a goready call is executed, telling the scheduler to move the waiting goroutine to a run queue.

It’s clear, then, that the ideal solution to making main block forever is to use one of the approaches that calls gopark.

The problems here, though, are that the approaches mentioned use gopark, but, that’s an implementation detail, therefore there’s no guarantee that the approaches mentioned use the gopark method under the hood, they can just as easily change to a busy wait. They can change, whenever they feel like it, and users (developers) are none the wiser. Further, it’s a side effect of the calls that developers are taking advantage of. A casual reader will see the select{} call, and have to ask, “why is there nothing being selected?”.

The best pathway is for an explicit method being made available that developers can call, only from the main thread, that parks the main thread, forever. It’s clear to reviewers, then, why the call is being made, and what it does. It’s not a hack.

Note: For the curious, this is all the gopark references in the Go source tree hosted on Github.

Published:
comments powered by Disqus