@@ -682,15 +682,22 @@ internal void LazyCompileFirstTarget(PythonFunction function) {
682682 /// <summary>
683683 /// Updates the delegate based upon current Python context settings for recursion enforcement and for tracing.
684684 /// </summary>
685+ /// <remarks>
686+ /// With <paramref name="forceCreation"/> false, this is the update done by switching tracing on or off (<see cref="UpdateAllCode"/>)
687+ /// and it always sets the target. With <paramref name="forceCreation"/> true, the update is not synchronized with such a switch,
688+ /// so the delegate is set as the target only if the tracing mode it was built for is still current (see <see cref="PublishTarget"/>).
689+ /// </remarks>
685690 /// <returns>
686- /// The delegate set as the target , or null if only a lazy target was set (only when <paramref name="forceCreation"/> is false).
687- /// A caller that needs the delegate should use this result rather than read <see cref="Target"/> again: another thread may change
688- /// it at any time, e.g. by switching tracing on or off.
691+ /// The delegate for the tracing mode that was current when the update started , or null if only a lazy target was set (only when
692+ /// <paramref name="forceCreation"/> is false). A caller that needs the delegate should use this result rather than read
693+ /// <see cref="Target"/> again: another thread may change it at any time, e.g. by switching tracing on or off.
689694 /// </returns>
690695 internal Delegate UpdateDelegate ( PythonContext context , bool forceCreation ) {
696+ // Read only once: the delegate is built for this mode, even if another thread switches tracing on or off meanwhile.
697+ bool tracing = context . EnableTracing ;
691698 Delegate finalTarget ;
692699
693- if ( context . EnableTracing && _lambda != null ) {
700+ if ( tracing && _lambda != null ) {
694701 if ( _tracingLambda == null ) {
695702 if ( ! forceCreation ) {
696703 // the user just called sys.settrace(), don't force re-compilation of every method in the system. Instead
@@ -723,10 +730,44 @@ internal Delegate UpdateDelegate(PythonContext context, bool forceCreation) {
723730
724731 finalTarget = AddRecursionCheck ( context , finalTarget ) ;
725732
726- SetTarget ( finalTarget ) ;
733+ if ( forceCreation ) {
734+ PublishTarget ( context , tracing , finalTarget ) ;
735+ } else {
736+ SetTarget ( finalTarget ) ;
737+ }
727738 return finalTarget ;
728739 }
729740
741+
742+ /// <summary>
743+ /// Sets the target to a delegate built for the given tracing mode, unless that mode is no longer current.
744+ /// </summary>
745+ /// <remarks>
746+ /// Used for updates that are not synchronized with switching tracing on or off (<see cref="UpdateAllCode"/>), so that a delegate
747+ /// built for the previous mode cannot replace the target set for the new one. Such a delegate stays in its cache
748+ /// (<see cref="_normalDelegate"/> or <see cref="_tracingDelegate"/>) and is used when the mode switches back.
749+ /// </remarks>
750+ /// <param name="tracing">
751+ /// The tracing mode that <paramref name="target"/> was built for: true for the tracing delegate, false for the normal one.
752+ /// </param>
753+ /// <param name="target">
754+ /// The delegate for the target, with the recursion check already added (see <see cref="AddRecursionCheck"/>).
755+ /// </param>
756+ internal void PublishTarget ( PythonContext context , bool tracing , Delegate target ) {
757+ if ( context . EnableTracing != tracing ) {
758+ return ;
759+ }
760+
761+ SetTarget ( target ) ;
762+ Thread . MemoryBarrier ( ) ;
763+ while ( context . EnableTracing != tracing ) {
764+ tracing = context . EnableTracing ;
765+ UpdateDelegate ( context , forceCreation : false ) ;
766+ Thread . MemoryBarrier ( ) ;
767+ }
768+ }
769+
770+
730771 /// <summary>
731772 /// Sets a target that creates the delegate for the current mode (tracing or not) when it is first needed.
732773 /// </summary>
@@ -751,7 +792,7 @@ private void SetLazyTarget() {
751792 internal void SetDebugTarget ( PythonContext context , Delegate target ) {
752793 _normalDelegate = target ;
753794
754- SetTarget ( AddRecursionCheck ( context , target ) ) ;
795+ PublishTarget ( context , tracing : false , AddRecursionCheck ( context , target ) ) ;
755796 }
756797
757798 /// <summary>
@@ -963,11 +1004,11 @@ public TargetUpdaterForCompilation(PythonContext context, FunctionCode code) {
9631004 }
9641005
9651006 public void SetCompiledTarget ( object sender , LightLambdaCompileEventArgs e ) {
966- _code . SetTarget ( _code . AddRecursionCheck ( _context , _code . _normalDelegate = e . Compiled ) ) ;
1007+ _code . PublishTarget ( _context , tracing : false , _code . AddRecursionCheck ( _context , _code . _normalDelegate = e . Compiled ) ) ;
9671008 }
9681009
9691010 public void SetCompiledTargetTracing ( object sender , LightLambdaCompileEventArgs e ) {
970- _code . SetTarget ( _code . AddRecursionCheck ( _context , _code . _tracingDelegate = e . Compiled ) ) ;
1011+ _code . PublishTarget ( _context , tracing : true , _code . AddRecursionCheck ( _context , _code . _tracingDelegate = e . Compiled ) ) ;
9711012 }
9721013 }
9731014
0 commit comments