Golang 1.24: runtime.AddCleanup

分享
Golang 1.24: runtime.AddCleanup

Previously, I wrote an article about the runtime.SetFinalizer that is used to be called when an object is cleaned up, but some issues with this function cause it to be used less frequently.

https://github.com/golang/go/issues/67535

  • SetFinalizer must always refer to the first word of an allocation. This means programmers must know what an 'allocation' is, whereas that distinction isn't generally exposed in the language.
  • There cannot be more than one finalizer on any object.
  • Objects with finalizers involved in any reference cycle will silently fail to be freed, and the finalizer will never run.
  • Objects with finalizers require at least two GC cycles to be freed.

For the above reasons, a new function runtime.AddCleanUp has been added in Golang 1.24 to replace runtime.SetFinalizer.

Neither runtime.AddCleanup nor runtime.SetFinalizer is guaranteed to execute.

The design goal of AddCleanup is to address many of the problems with runtime.SetFinalizer, in particular avoids object resurrection, thus allowing for the timely cleanup of objects, and supporting cyclic cleanup of objects.
API

func AddCleanup[T, S any](ptr *T, cleanup func(S), arg S) Cleanup

Based on this, a Demo for RAII (Resource Acquisition Is Initialization) can be written. In the go weak article, we implemented a fixed-length cache, changed the code a bit, add a newElemWeak method that automatically calls delete to remove the key from the cache when the oldest elem is evicted. We don't need to manage it manually.

func (c *WeakCache) newElemWeak(elem *list.Element) weak.Pointer[list.Element] {
	elemWeak := weak.Make(elem)
	runtime.AddCleanup(elem, func(name string) {
		delete(c.cache, name)
	}, elem.Value.(*CacheItem).key)
	return elemWeak
}

A Few Things to Keep in Mind

AddCleanup places a few constraints on ptr and supports attaching multiple cleanup functions to the same pointer. However, if ptr is reachable from cleanup or arg, it will never be reclaimed (memory leak), and the cleanup function will never run. For the moment, this scenario doesn't pan out. However, a pattern like GODEBUG=gccheckmark=1 may detect this in the future.
For example
https://gist.github.com/hxzhouh/5bb1d55259dcb4dab87b37beaef9bea2
View code snippet on GitHub Gist →
fmt.Println("close f") will not be printed, and the file will not be closed.

When binding multiple cleanups to a single ptr, its running order may be variable since cleanup runs in a separate goroutine.

In particular, if several objects point to each other and become unreachable simultaneously, their cleanup functions can all be run and in any order. This is true even if the objects form a loop (runtime.SetFinalizer creates a memory leak in this case).
for example
https://gist.github.com/hxzhouh/ca402c723faa78726baba7e56bff573a
View code snippet on GitHub Gist →

References

  1. https://github.com/golang/go/issues/67535

阅读更多

那些意外变成特性的 Bug:当代码漏洞改写计算史

那些意外变成特性的 Bug:当代码漏洞改写计算史

那些意外变成特性的 Bug:当代码漏洞改写计算史. 比起草台班子将错就错的神话,面对异常数据时的观测定力才是真的 社交网络上常年流传着一类爽文:某家公司的程序员把代码写崩了,结果不仅没被开除,这个 Bug 反而成了拳头产品,妥妥的爽文主角剧本。 最常被搬出来的有三个:Audio Hijack 写错试用期拯救公。

By huizhou92