Repository navigation
Ensure a delegate built for the old tracing mode doesn't replace the new one - #2118
Conversation
slozier
left a comment
There was a problem hiding this comment.
Looks good to me! Thanks!
| SetTarget(target); | ||
| Thread.MemoryBarrier(); | ||
| while (context.EnableTracing != tracing) { | ||
| tracing = context.EnableTracing; |
There was a problem hiding this comment.
Lots of reading context.EnableTracing (and another one at the top of UpdateDelegate). Any chance of things getting out of sync (seems unlikely but doesn't hurt to ask).
There was a problem hiding this comment.
No. The whole reason for this loop is to prevent things getting out of sync. It settles when things become in sync. In practice it will not be lots of reading, usually no loop (typical case) or one loop (if sys.settrace happens in the middle between reading EnableTracing (volatile) and setting Target. It is a loop rather than a simple if because in theory there could be multiple threads calling sys.settrace with conflicting values all at once, or a quick succession by one thread doing it. But the end result is the same: the loop ends when things settle down in a consistent state.
The bug: switching tracing on or off (
sys.settrace) updates the target of every code object, but other writers of the target are not synchronized with that switch: the adaptive compiler's completion callbacks, the background precompile of class bodies, compilation on first use inMakeClassandFunctionCode.Call, and -X:Debugfunction definitions.A delegate built for the previous mode could thus become the target after the switch and stay until the next one, e.g. a class body that runs untraced while tracing is on.
These writers now set the target through
FunctionCode.PublishTarget, which skips a delegate built for a mode that is no longer current (it stays in its cache for when the mode switches back) and checks the mode again after setting the target, repeating the switch's update if the mode changed meanwhile.SetTracechanges the mode with an interlocked operation, so the new mode is visible before any code is updated for it.Under
-X:Debugthis also fixes functions defined while tracing is on, which always got their normal delegate and were not traced.