-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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: non-preemptible zeroing in clear, append, bytes.growSlice, etc #69327
Comments
|
@prattmic that seems plausible to me. |
The simplest way to fix this would be to have I think the only concern would be if this function is called in places where we have to be very carefully non-preemptible (no explicit |
From
First, @mknyszek , you said
Are you referring to the "ensures ... cleared atomically" part of that documentation? (I see that Second, given that the current function definition includes a promise of atomicity — is that part of the API that members of the linkname hall of shame might require? (I would guess that it would be hard to use that property effectively outside of the runtime.) Though the comment of "any pointer-aligned, pointer-sized portion is cleared atomically" might only guarantee that individual portions — the size of a single pointer — are each cleared atomically? |
I think the distinguishing use case for I don't think any user code linknaming into memclrNoHeapPointers would run into this problem. Only the runtime can see Go-heap-allocated, not-yet-initialized memory. I think there are a few ways to solve this special issue. One is to ensure that an object mid-initialization is never scanned by the GC. Another is to ensure that an object mid-initialization has no ptr bits set yet. A third is to ensure that it is scanned conservatively. With any of those, I think interrupting the memclr itself should be fine. |
Correct. |
Yeah, that's right. I think a lot of places this is used don't actually care about true atomicity, but we need to be very careful. I was looking a bit more deeply into this and this is going to be harder than I thought. Take for example (1) Now whether or not this is a real problem is subtle. I think (but am not 100% certain) that you could make the argument that this isn't actually a problem in this case. The purpose of the deletion barrier is to catch pointers which are hidden in already-scanned stacks. If this is just a regular clear, then there's no chance of that. But if we read pointers from what we're about to clear and stored them on the stack before the mark phase, we can be certain they will be visible during the mark phase, if they're still relevant. Unfortunately there may be more bad or possibly-bad-but-actually-fine scenarios, but reasoning about all the situations will be subtle. I don't know why these functions do not explicitly disable preemption, but I suspect it's just performance. However, I think if we explicitly disabled preemption, that would probably not meaningfully impact performance very much either.
I agree with @randall77 that it's unlikely linknamed Go code will run into this. ... With maybe one exception. IIRC |
The
clear
andappend
built-ins can result in the need to zero an arbitrary amount of memory. For byte slices, the compiler appears to use a call toruntime.memclrNoHeapPointers
. That function cannot be preempted, which can lead to arbitrary delays when another goroutine wants to stop the world (such as to start or end a GC cycle).Applications that use
bytes.Buffer
can experience this when a call tobytes.(*Buffer).Write
leads to a call tobytes.growSlice
which usesappend
, as seen in one of the execution traces from #68399.The runtime and compiler should collaborate to allow opportunities for preemption when zeroing large amounts of memory.
CC @golang/runtime @mknyszek
Reproducer, using `clear` built-in plus `runtime.ReadMemStats` to provide STWs
Reproducer results, showing average time to stop the world is more than 1 ms (instead of less than 10 µs) when another part of the app is clearing a 100 MB byte slice
`bytes.growSlice` calling `runtime.memclrNoHeapPointers`
The text was updated successfully, but these errors were encountered: