diff --git a/src/Umbraco.Examine.Lucene/UmbracoExamineIndex.cs b/src/Umbraco.Examine.Lucene/UmbracoExamineIndex.cs index 6440c7bd6a..9e6c414344 100644 --- a/src/Umbraco.Examine.Lucene/UmbracoExamineIndex.cs +++ b/src/Umbraco.Examine.Lucene/UmbracoExamineIndex.cs @@ -107,12 +107,26 @@ namespace Umbraco.Cms.Infrastructure.Examine { if (CanInitialize()) { - // Use SafeCallContext to prevent the current Execution Context (AsyncLocal) flow to child + // Use ExecutionContext.SuppressFlow to prevent the current Execution Context (AsyncLocal) flow to child // tasks executed in the base class so we don't leak Scopes. // TODO: See notes at the top of this class using (ExecutionContext.SuppressFlow()) { base.PerformDeleteFromIndex(itemIds, onComplete); + } + } + } + + protected override void PerformIndexItems(IEnumerable values, Action onComplete) + { + if (CanInitialize()) + { + // Use ExecutionContext.SuppressFlow to prevent the current Execution Context (AsyncLocal) flow to child + // tasks executed in the base class so we don't leak Scopes. + // TODO: See notes at the top of this class + using (ExecutionContext.SuppressFlow()) + { + base.PerformIndexItems(values, onComplete); } } } diff --git a/src/Umbraco.Infrastructure/Scoping/Scope.cs b/src/Umbraco.Infrastructure/Scoping/Scope.cs index e7ec3df4f7..603a83c197 100644 --- a/src/Umbraco.Infrastructure/Scoping/Scope.cs +++ b/src/Umbraco.Infrastructure/Scoping/Scope.cs @@ -433,7 +433,7 @@ namespace Umbraco.Cms.Core.Scoping if (this != _scopeProvider.AmbientScope) { - var failedMessage = $"The {nameof(Scope)} {this.GetDebugInfo()} being disposed is not the Ambient {nameof(Scope)} {_scopeProvider.AmbientScope.GetDebugInfo()}. This typically indicates that a child {nameof(Scope)} was not disposed or flowed to a child thread that was not re-joined to the thread that the parent originated (i.e. Task.Run used as a fire and forget task without ExecutionContext.SuppressFlow())."; + var failedMessage = $"The {nameof(Scope)} {this.GetDebugInfo()} being disposed is not the Ambient {nameof(Scope)} {_scopeProvider.AmbientScope.GetDebugInfo()}. This typically indicates that a child {nameof(Scope)} was not disposed, or flowed to a child thread that was not awaited, or concurrent threads are accessing the same {nameof(Scope)} (Ambient context) which is not supported. If using Task.Run (or similar) as a fire and forget tasks or to run threads in parallel you must suppress execution context flow with ExecutionContext.SuppressFlow() and ExecutionContext.RestoreFlow()."; #if DEBUG_SCOPES Scope ambient = _scopeProvider.AmbientScope; diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/ScopeTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/ScopeTests.cs index 5a08d0c4b5..7b3075eeb9 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/ScopeTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/ScopeTests.cs @@ -129,12 +129,12 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping Console.WriteLine("Child Task after disposed: " + scopeProvider.AmbientScope.InstanceId); }); - Thread.Sleep(2000); // block for a bit to ensure the child task is disposed first + Console.WriteLine("Parent Task waiting: " + scopeProvider.AmbientScope?.InstanceId); + Task.WaitAll(t); Console.WriteLine("Parent Task disposing: " + scopeProvider.AmbientScope.InstanceId); mainScope.Dispose(); Console.WriteLine("Parent Task disposed: " + scopeProvider.AmbientScope?.InstanceId); - Task.WaitAll(t); Assert.Pass(); }