-
Notifications
You must be signed in to change notification settings - Fork 17.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
runtime: allow cleanups to run concurrently #71825
Comments
Related Issues (Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.) |
Because this affects the 1.24 release that just happened, we're planning to fast-track this proposal. It doesn't seem too controversial, and the impact should be minimal because this API was only introduced in 1.24. |
@mknyszek , can you write exactly what that paragraph of the |
Here's a draft diff: -// A single goroutine runs all cleanup calls for a program, sequentially. If a
-// cleanup function must run for a long time, it should create a new goroutine.
+// Cleanups may run concurrently with one another, unlike finalizers. However, if
+// a cleanup function must run for a long time, it should create a new goroutine
+// to avoid blocking the execution of other cleanups. |
Change https://go.dev/cl/650697 mentions this issue: |
Change https://go.dev/cl/650696 mentions this issue: |
@cherrymui notes that this text allows cleanups to run on a regular user goroutine (e.g., the one calling I think other than that detail, the proposal committee is good with this change. |
Based on the discussion above, this proposal seems like a likely accept. The proposal is to remove the following sentences from the
and replace them with
|
Should there be a backport, at least for the change in the doc comment? |
That's a very good point. Requiring it to be totally concurrent SGTM. |
Yes, definitely. I'll backport the doc change at the very least. Actually running cleanups on more than 2 goroutines concurrently might be too complicated to backport, but just for the sake of preventing a dependency on the current behavior, it might be fairly simple to just occasionally shunt cleanup work off to another goroutine. |
Change https://go.dev/cl/651659 mentions this issue: |
@gopherbot Please open a backport issue for Go 1.24. This API is new to Go 1.24. At present, we only plan to backport the documentation change, since https://go.dev/cl/651659 is a bit hacky and thus risky. I haven't been able yet to come up with a simpler way to do this. There may be something we can do with the race detector. (This is all assuming there's isn't a last-minute reason why we shouldn't approve this proposal.) |
I'm going to work on a patch in Go 1.25 to start enforcing this in race detector by treating cleanup calls as executing in different race contexts, even if they're executed from the same goroutine, which will help identify any latent data races. Hopefully this will be easy to backport as well. |
Backport issue(s) opened: #71955 (for 1.24). Remember to create the cherry-pick CL(s) as soon as the patch is submitted to master, according to https://go.dev/wiki/MinorReleases. |
Change https://go.dev/cl/652636 mentions this issue: |
Change https://go.dev/cl/652637 mentions this issue: |
No change in consensus, so accepted. 🎉 The proposal is to remove the following sentences from the
and replace them with
|
…rrently with each other For #71825. Fixes #71955. Change-Id: I25af19eb72d75f13cf661fc47ee5717782785326 Reviewed-on: https://go-review.googlesource.com/c/go/+/652637 Reviewed-by: Keith Randall <khr@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Michael Knyszek <mknyszek@google.com> Reviewed-by: Keith Randall <khr@google.com>
Lets say that at some point we would like to optimize Any thoughts? |
This implementation strategy is attractive but does fall into the same issue as:
This could be a surprising source of deadlocks, though I suppose in this case it would trigger deterministically. I personally would like it to be the case that we can run cleanups on user goroutines for other reasons (backpressure), I'm not sure how we do this without adding another footgun to (As cleanups approach "can run anywhere, any time on user goroutines," they start to look more like Unix signals, which are notoriously hard to reason about. This is of course an extreme comparison though. There might be a way to leave this particular door open.) |
In limited cases we may be able to statically prove that there's no way to distinguish whether the cleanup is running on the same goroutine or not, and transform it into a defer. This would be quite limited, but could probably capture simple cleanups. |
Proposal Details
Currently the docs for
runtime.AddCleanup
state: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 intounique
'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. Usingruntime.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 theunique
package usingruntime.AddCleanup
, and introduce a new backpressure mechanism inunique.Make
. Even so, the cleanup/finalizer goroutine can still theoretically become a bottleneck at highGOMAXPROCS
(think 128+). Worse still, anyone else usingruntime.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, whereGOMAXPROCS
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.The text was updated successfully, but these errors were encountered: