diff --git a/src/Libraries/Microsoft.AspNetCore.AsyncState/AsyncStateHttpContextExtensions.cs b/src/Libraries/Microsoft.AspNetCore.AsyncState/AsyncStateHttpContextExtensions.cs
index 6c289c96f77..d9141dbf83e 100644
--- a/src/Libraries/Microsoft.AspNetCore.AsyncState/AsyncStateHttpContextExtensions.cs
+++ b/src/Libraries/Microsoft.AspNetCore.AsyncState/AsyncStateHttpContextExtensions.cs
@@ -16,6 +16,7 @@ public static class AsyncStateHttpContextExtensions
///
/// Adds default implementations for , , and services,
/// scoped to the lifetime of instances.
+ /// Please note that implementations of these interfaces are not thread safe.
///
/// The to add the service to.
/// The value of .
diff --git a/src/Libraries/Microsoft.AspNetCore.AsyncState/README.md b/src/Libraries/Microsoft.AspNetCore.AsyncState/README.md
index d5bc4f0a353..5bce6aa3a99 100644
--- a/src/Libraries/Microsoft.AspNetCore.AsyncState/README.md
+++ b/src/Libraries/Microsoft.AspNetCore.AsyncState/README.md
@@ -4,6 +4,9 @@ This provides the ability to store and retrieve state objects that flow with the
The lifetime of the shared data is controlled automatically and will be the same as of `HttpContext`.
+> [!NOTE]
+> Please note, the implementation of `IAsyncContext` provided by this library is not thread-safe.
+
## Install the package
From the command-line:
diff --git a/src/Libraries/Microsoft.Extensions.AsyncState/AsyncState.cs b/src/Libraries/Microsoft.Extensions.AsyncState/AsyncState.cs
index 9fc79930954..4d782409238 100644
--- a/src/Libraries/Microsoft.Extensions.AsyncState/AsyncState.cs
+++ b/src/Libraries/Microsoft.Extensions.AsyncState/AsyncState.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
+using System.Collections.Generic;
using System.Threading;
using Microsoft.Extensions.ObjectPool;
using Microsoft.Shared.Diagnostics;
@@ -12,7 +13,7 @@ namespace Microsoft.Extensions.AsyncState;
internal sealed class AsyncState : IAsyncState
{
private static readonly AsyncLocal _asyncContextCurrent = new();
- private static readonly ObjectPool _featuresPool = PoolFactory.CreatePool(new FeaturesPooledPolicy());
+ private static readonly ObjectPool> _featuresPool = PoolFactory.CreatePool(new FeaturesPooledPolicy());
private int _contextCount;
public void Initialize()
@@ -21,12 +22,12 @@ public void Initialize()
// Use an object indirection to hold the AsyncContext in the AsyncLocal,
// so it can be cleared in all ExecutionContexts when its cleared.
- var asyncStateHolder = new AsyncStateHolder
+ var features = new AsyncStateHolder
{
Features = _featuresPool.Get()
};
- _asyncContextCurrent.Value = asyncStateHolder;
+ _asyncContextCurrent.Value = features;
}
public void Reset()
@@ -59,7 +60,9 @@ public bool TryGet(AsyncStateToken token, out object? value)
return false;
}
- value = _asyncContextCurrent.Value.Features.Get(token.Index);
+ EnsureCount(_asyncContextCurrent.Value.Features, token.Index + 1);
+
+ value = _asyncContextCurrent.Value.Features[token.Index];
return true;
}
@@ -83,14 +86,28 @@ public void Set(AsyncStateToken token, object? value)
Throw.InvalidOperationException("Context is not initialized");
}
- _asyncContextCurrent.Value.Features.Set(token.Index, value);
+ EnsureCount(_asyncContextCurrent.Value.Features, token.Index + 1);
+
+ _asyncContextCurrent.Value.Features[token.Index] = value;
+ }
+
+ internal static void EnsureCount(List