GC-preserve a Task's scope on start, to prevent freeing unrooted scopes via ScopedThunk#60620
GC-preserve a Task's scope on start, to prevent freeing unrooted scopes via ScopedThunk#60620
Conversation
|
EDIT: @KristofferC I have adjusted the test so it should pass on 1.12 as well. Therefore this should be a clean backport. |
f052809 to
b52ae00
Compare
|
Does it make more sense to instead preserve the previous scope when we install a new one? It feels a bit odd to double-preserve the current scope. |
|
Looking at #52309 the interpreter version does have a GC preserve, so maybe we just need to emit a GC preserve in the codegen version? |
Both interpreter and codegen preserve the new scope. However, if a scope was inherited this can leave the initial scope of a task dangling, which is what this PR fixes. My proposal was to switch both interpreter and codegen to preserving the old scope. |
|
Yeah that seems like it would be a reasonable approach as well, Keno, and it might be easier to understand. (It's what the LLMs wanted to do to fix this 😛) |
|
I agree it is more intuitive to preserve the old scope. Maybe we didn't do it that way because many times that would just be preserving nothing though? |
I think I just wasn't thinking carefully - let's try preserving the old scope - makes more sense to me and lets the optimizer delete it if there are no safepoints. |
|
Ah, yeah I missed that detail. old_scope = current_task.scope
try
current_task.scope = new_scope # Rooted through task object
finally
current_task.scope = old_scope
endIs the behavior that we implement, and indeed we should only root the I guess the only thing that confuses me is that I would have expected GC lowering to put the |
|
@Keno / @JeffBezanson / @vchuravy: My instinct would be to merge this PR as-is, now, since it is a minimal change, we know it fixes the issue, and it can be easily backported to 1.12 as a bug-fix. Then, maybe in a separate PR we can make the changes you're suggesting above, which would change the "calling convention" for GC-preserving the scope? I feel like that is a slightly bigger change, and i'd rather make that only on master, rather than backporting it to 1.12 before we've had a chance to identify any unintended consequences of that change / or any cases we've missed. Does that sound okay to you all? Any objections to merging this one now, and then following up with the proposal above? EDIT: I'll start a branch now to make the proposed change. |
E.g. I'm confused about this part: Are you sure we don't also need to root the new task? From what i've read, we don't scan all of the Tasks' scope fields. EDIT: I think as long as we also do #60617, then it should be okay to only root the old scope. Is that right? |
|
No, I don't think we should merge this PR as is. The change on its own is quite weird. We must preserve the old scope across a restore point, and here we a preserving the new scope. The new scope shold be rooted through the task object itself, so it's very counter intuitive to have it put onto a GC frame again. If we don't preserve the old scope correctly there might be other opportunities for bugs, What about nested with scopes? |
|
No, we shouldn't ship disparate fixes to different versions - that leaves the potential for bugs in the backports that are not present on master, which is always annoying. |
A write barrier should be inserted, it's technically not needed, since the task is young and thus the wb is a no-op, but it's cleaner to have it here. |
|
Write barrier fix is in #60617, which is also needed. |
|
@vchuravy I think the WB fix is needed, because if the Task is promoted to old (and then it's switched away) then nothing is rooting the Scope, so it could be freed if the scope is young but the task is old? |
|
@Keno: I'm not sure i agree with that principle. Different versions already are different since they have different features and refactorings merged. I feel like backporting the most minimal fix to the code that's present on that version is always preferable to backporting larger changes, since larger changes introduce the possibility of introducing new bugs in the patch releases (which TBH we see happen all the time). I do agree with you guys that the proposed change would make the code more sensible overall, but it will be a bigger change, and it requires #60617 as well. (I'm working on that now, in another branch, but not sure i've gotten it right yet.) @KristofferC: Since you are the backport-king, do you have a preference? |
Both changes require that PR |
|
Also, just to make sure: Do we actually scan the Task's |
Fixes #60620 (comment) previously we were preserving the new scope across the exception handler, instead of the scope that was actually stored in the eh context. The additionaly write_barriers are probably not necessary (the task should always be young. Alternative to #60620
We scan all pointer fields: Lines 2360 to 2370 in 64d4518 |
There are two unrelated bugs, but both can result in the scope (or for #60617 any other object referenced by the task, including on the stack) to not be marked under specific circumstances. This fix is likely more frequent, but we suspect the other is an issue as well. |
Fixes JuliaLang#60620 (comment) previously we were preserving the new scope across the exception handler, instead of the scope that was actually stored in the eh context. The additionaly write_barriers are probably not necessary (the task should always be young. Alternative to JuliaLang#60620
This comment was marked as outdated.
This comment was marked as outdated.
|
Replaced by #60647 |
Fixes #60620 (comment) previously we were preserving the new scope across the exception handler, instead of the scope that was actually stored in the eh context. The additionaly write_barriers are probably not necessary (the task should always be young. Alternative to #60620
Explanation of the issue in 59483:
This is now fixed by rooting the Scope when starting a new Task.
Fixes #59483.