Skip to content

runtime: allow cleanups to run concurrently #71825

Closed
@mknyszek

Description

@mknyszek

Proposal Details

Currently the docs for runtime.AddCleanup state:

A single goroutine runs all cleanup calls for a program, sequentially.

But this was copied from runtime.SetFinalizer because it was convenient. It really deserved more thought.

Since runtime.AddCleanup just debuted in Go 1.24, and thus there probably aren't too many programs actually depending on this behavior, I propose we remove this sentence from the docs and intentionally run cleanups concurrently (on two goroutines to start with) to prevent dependence on this behavior.

We can always change the behavior back to sequential in a backwards-compatible way. We can still recommend that people shunt long-running work to a separate goroutine.

This proposal is partially motivated by #71772, in which sub-optimal usage of unique.Make may result in a rate of insertions into unique's internal map that exceeds the pace of deletions, resulting in unbounded memory growth. The current deletion strategy involves a goroutine that wastefully walks over the whole map. Using runtime.AddCleanup mitigates this problem by directly targeting dead entries in the map. However, this is only a mitigation, and not a full solution. Today, to fully resolve the issue, we will have to shunt work to a global work queue that's specific to the unique package using runtime.AddCleanup, and introduce a new backpressure mechanism in unique.Make. Even so, the cleanup/finalizer goroutine can still theoretically become a bottleneck at high GOMAXPROCS (think 128+). Worse still, anyone else using runtime.AddCleanup to build a canonicalization map or cache has a chance of running into the same problem, and must roll their own solution. This issue has been demonstrated in a small reproducer, where GOMAXPROCS doesn't have to be very high at all, though it hasn't yet been demonstrated in production.

Even if we don't have a concrete plan yet on how exactly to support such uses of runtime.AddCleanup, I think we should at least open the door to alternative cleanup strategies.

Metadata

Metadata

Assignees

No one assigned

    Labels

    LibraryProposalIssues describing a requested change to the Go standard library or x/ libraries, but not to a toolProposalProposal-Accepted

    Type

    No type

    Projects

    Status

    Accepted

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions