A variable becomes unavailable when the runtime detects that Go code cannot reach the point at which this link refers again.
In the above example, syscall.Open() is used to open the file. The returned file descriptor (which is only an int value) is wrapped in a struct . Then, a finalizer is attached to this struct value, which closes the file descriptor. Now that this structure value becomes unavailable, its finalizer can be started at any time, and closing / invalidating / reusing the file descriptor may lead to unexpected behavior or errors when executing syscall Read() .
The last use of this struct p value in Go code when syscall.Read() called (and the file descriptor pd is passed to it). The syscall implementation will use this file descriptor after initializing syscall.Read() , it can do this until syscall.Read() returns. But this use of the file descriptor is "independent" of the Go code.
Thus, the value of struct p not used at run time syscall, and syscall blocks the Go code until it returns. This means that Go’s runtime allows p to be marked as unreachable at the time of Read() (until Read() returns) or even before it actually runs (since p used only to provide arguments for calling Read() .
Therefore, the call to runtime.KeepAlive() : since this call is after syscall.Read() and refers to the variable p , Go runtime is not allowed to mark p inaccessible until Read() returns, because this happens after the call to Read() .
Note that you can use other constructs to “keep p alive,” for example. _ = p or return it. runtime.KeepAlive() does nothing magical in the background, its implementation is as follows:
func KeepAlive(interface{}) {}
runtime.KeepAlive() provides a much better alternative, because:
- The docs make it clear that we want to keep
p alive (to prevent Finalizers from running). - Using other constructs, such as
_ = p , may be "optimized" by future compilers, but does not call runtime.KeepAlive() .
source share