Skip to content

Commit 8c3e391

Browse files
aclementsgopherbot
authored andcommitted
runtime: improve AddCleanup documentation
Steer people from SetFinalizer to AddCleanup. Address some of the *non*-constraints on AddCleanup. Add some of the subtlety from the SetFinalizer documentation to the AddCleanup documentation. Updates #67535. Updates #70425. Change-Id: I8d13b756ca866051b8a5c19327fd5a76f5e0f3d7 Reviewed-on: https://go-review.googlesource.com/c/go/+/634318 Reviewed-by: Michael Knyszek <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Auto-Submit: Austin Clements <[email protected]>
1 parent 04cdaa9 commit 8c3e391

File tree

2 files changed

+33
-7
lines changed

2 files changed

+33
-7
lines changed

src/runtime/mcleanup.go

+30-7
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,37 @@ import (
1212
// AddCleanup attaches a cleanup function to ptr. Some time after ptr is no longer
1313
// reachable, the runtime will call cleanup(arg) in a separate goroutine.
1414
//
15-
// If ptr is reachable from cleanup or arg, ptr will never be collected
16-
// and the cleanup will never run. AddCleanup panics if arg is equal to ptr.
15+
// A typical use is that ptr is an object wrapping an underlying resource (e.g.,
16+
// a File object wrapping an OS file descriptor), arg is the underlying resource
17+
// (e.g., the OS file descriptor), and the cleanup function releases the underlying
18+
// resource (e.g., by calling the close system call).
1719
//
18-
// The cleanup(arg) call is not always guaranteed to run; in particular it is not
19-
// guaranteed to run before program exit.
20+
// There are few constraints on ptr. In particular, multiple cleanups may be
21+
// attached to the same pointer, or to different pointers within the same
22+
// allocation.
2023
//
21-
// Cleanups are not guaranteed to run if the size of T is zero bytes, because
22-
// it may share same address with other zero-size objects in memory. See
23-
// https://go.dev/ref/spec#Size_and_alignment_guarantees.
24+
// If ptr is reachable from cleanup or arg, ptr will never be collected
25+
// and the cleanup will never run. As a protection against simple cases of this,
26+
// AddCleanup panics if arg is equal to ptr.
2427
//
2528
// There is no specified order in which cleanups will run.
29+
// In particular, if several objects point to each other and all become
30+
// unreachable at the same time, their cleanups all become eligible to run
31+
// and can run in any order. This is true even if the objects form a cycle.
2632
//
2733
// A single goroutine runs all cleanup calls for a program, sequentially. If a
2834
// cleanup function must run for a long time, it should create a new goroutine.
2935
//
3036
// If ptr has both a cleanup and a finalizer, the cleanup will only run once
3137
// it has been finalized and becomes unreachable without an associated finalizer.
3238
//
39+
// The cleanup(arg) call is not always guaranteed to run; in particular it is not
40+
// guaranteed to run before program exit.
41+
//
42+
// Cleanups are not guaranteed to run if the size of T is zero bytes, because
43+
// it may share same address with other zero-size objects in memory. See
44+
// https://go.dev/ref/spec#Size_and_alignment_guarantees.
45+
//
3346
// It is not guaranteed that a cleanup will run for objects allocated
3447
// in initializers for package-level variables. Such objects may be
3548
// linker-allocated, not heap-allocated.
@@ -41,6 +54,16 @@ import (
4154
// allocation may never run if it always exists in the same batch as a
4255
// referenced object. Typically, this batching only happens for tiny
4356
// (on the order of 16 bytes or less) and pointer-free objects.
57+
//
58+
// A cleanup may run as soon as an object becomes unreachable.
59+
// In order to use cleanups correctly, the program must ensure that
60+
// the object is reachable until it is safe to run its cleanup.
61+
// Objects stored in global variables, or that can be found by tracing
62+
// pointers from a global variable, are reachable. A function argument or
63+
// receiver may become unreachable at the last point where the function
64+
// mentions it. To ensure a cleanup does not get called prematurely,
65+
// pass the object to the [KeepAlive] function after the last point
66+
// where the object must remain reachable.
4467
func AddCleanup[T, S any](ptr *T, cleanup func(S), arg S) Cleanup {
4568
// Explicitly force ptr to escape to the heap.
4669
ptr = abi.Escape(ptr)

src/runtime/mfinal.go

+3
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,9 @@ func blockUntilEmptyFinalizerQueue(timeout int64) bool {
350350
//
351351
// SetFinalizer(obj, nil) clears any finalizer associated with obj.
352352
//
353+
// New Go code should consider using [AddCleanup] instead, which is much
354+
// less error-prone than SetFinalizer.
355+
//
353356
// The argument obj must be a pointer to an object allocated by calling
354357
// new, by taking the address of a composite literal, or by taking the
355358
// address of a local variable.

0 commit comments

Comments
 (0)