diff --git a/src/System.Private.CoreLib/Resources/Strings.resx b/src/System.Private.CoreLib/Resources/Strings.resx
index 26f5b66a2f2d..de7eb5ba00f3 100644
--- a/src/System.Private.CoreLib/Resources/Strings.resx
+++ b/src/System.Private.CoreLib/Resources/Strings.resx
@@ -2620,6 +2620,9 @@
Timeouts are not supported on this stream.
+
+ The Timer was already closed using an incompatible Dispose method.
+
The given type cannot be boxed.
diff --git a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems
index e8520f213ee9..cdc6c7d62ece 100644
--- a/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems
+++ b/src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems
@@ -72,6 +72,8 @@
+
+
@@ -208,6 +210,7 @@
+
@@ -421,6 +424,7 @@
+
@@ -632,6 +636,7 @@
+
diff --git a/src/System.Private.CoreLib/shared/System/Collections/Generic/IAsyncEnumerable.cs b/src/System.Private.CoreLib/shared/System/Collections/Generic/IAsyncEnumerable.cs
new file mode 100644
index 000000000000..b8cd4f26c9a1
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Collections/Generic/IAsyncEnumerable.cs
@@ -0,0 +1,11 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Collections.Generic
+{
+ public interface IAsyncEnumerable
+ {
+ IAsyncEnumerator GetAsyncEnumerator();
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/Collections/Generic/IAsyncEnumerator.cs b/src/System.Private.CoreLib/shared/System/Collections/Generic/IAsyncEnumerator.cs
new file mode 100644
index 000000000000..67b5670a3140
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Collections/Generic/IAsyncEnumerator.cs
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Threading.Tasks;
+
+namespace System.Collections.Generic
+{
+ public interface IAsyncEnumerator : IAsyncDisposable
+ {
+ ValueTask MoveNextAsync();
+ T Current { get; }
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/IAsyncDisposable.cs b/src/System.Private.CoreLib/shared/System/IAsyncDisposable.cs
new file mode 100644
index 000000000000..6a1ac3f7b1e2
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/IAsyncDisposable.cs
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Threading.Tasks;
+
+namespace System
+{
+ public interface IAsyncDisposable
+ {
+ ValueTask DisposeAsync();
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/IO/BinaryWriter.cs b/src/System.Private.CoreLib/shared/System/IO/BinaryWriter.cs
index d1a333f419f8..9b4523333e7f 100644
--- a/src/System.Private.CoreLib/shared/System/IO/BinaryWriter.cs
+++ b/src/System.Private.CoreLib/shared/System/IO/BinaryWriter.cs
@@ -5,6 +5,8 @@
using System.Text;
using System.Diagnostics;
using System.Buffers;
+using System.Threading.Tasks;
+using System.Threading;
namespace System.IO
{
@@ -12,7 +14,7 @@ namespace System.IO
// primitives to an arbitrary stream. A subclass can override methods to
// give unique encodings.
//
- public class BinaryWriter : IDisposable
+ public class BinaryWriter : IDisposable, IAsyncDisposable
{
public static readonly BinaryWriter Null = new BinaryWriter();
@@ -87,6 +89,30 @@ public void Dispose()
Dispose(true);
}
+ public virtual ValueTask DisposeAsync()
+ {
+ if (GetType() == typeof(BinaryWriter))
+ {
+ if (_leaveOpen)
+ {
+ return new ValueTask(OutStream.FlushAsync());
+ }
+ else
+ {
+ OutStream.Close();
+ return default;
+ }
+ }
+ else
+ {
+ // Since this is a derived BinaryWriter, delegate to whatever logic
+ // the derived implementation already has in Dispose.
+ return new ValueTask(Task.Factory.StartNew(s => ((BinaryWriter)s).Dispose(), this,
+ CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
+ }
+ }
+
+
// Returns the stream associated with the writer. It flushes all pending
// writes before returning. All subclasses should override Flush to
// ensure that all buffered data is sent to the stream.
diff --git a/src/System.Private.CoreLib/shared/System/IO/FileStream.Unix.cs b/src/System.Private.CoreLib/shared/System/IO/FileStream.Unix.cs
index ae4b709ea110..c77d348edfca 100644
--- a/src/System.Private.CoreLib/shared/System/IO/FileStream.Unix.cs
+++ b/src/System.Private.CoreLib/shared/System/IO/FileStream.Unix.cs
@@ -269,6 +269,12 @@ protected override void Dispose(bool disposing)
}
}
+ public override ValueTask DisposeAsync() =>
+ // On Unix, we'll always end up doing what's in Dispose anyway,
+ // so just delegate to the base to queue it. We maintain an explicit override for
+ // consistency with Windows, which has a more complicated implementation.
+ base.DisposeAsync();
+
/// Flushes the OS buffer. This does not flush the internal read/write buffer.
private void FlushOSBuffer()
{
diff --git a/src/System.Private.CoreLib/shared/System/IO/FileStream.Windows.cs b/src/System.Private.CoreLib/shared/System/IO/FileStream.Windows.cs
index 4f8292bcab17..7dcd6adf3d15 100644
--- a/src/System.Private.CoreLib/shared/System/IO/FileStream.Windows.cs
+++ b/src/System.Private.CoreLib/shared/System/IO/FileStream.Windows.cs
@@ -251,14 +251,11 @@ protected override void Dispose(bool disposing)
{
if (_fileHandle != null && !_fileHandle.IsClosed)
{
- if (_fileHandle.ThreadPoolBinding != null)
- _fileHandle.ThreadPoolBinding.Dispose();
-
+ _fileHandle.ThreadPoolBinding?.Dispose();
_fileHandle.Dispose();
}
- if (_preallocatedOverlapped != null)
- _preallocatedOverlapped.Dispose();
+ _preallocatedOverlapped?.Dispose();
_canSeek = false;
@@ -270,6 +267,35 @@ protected override void Dispose(bool disposing)
}
}
+ public override ValueTask DisposeAsync() =>
+ GetType() == typeof(FileStream) ?
+ DisposeAsyncCore() :
+ base.DisposeAsync();
+
+ private async ValueTask DisposeAsyncCore()
+ {
+ // Same logic as in Dispose(disposing:true), except with async counterparts.
+ // TODO: https://github.com/dotnet/corefx/issues/32837: FlushAsync does synchronous work.
+ try
+ {
+ if (_fileHandle != null && !_fileHandle.IsClosed && _writePos > 0)
+ {
+ await FlushAsyncInternal(default).ConfigureAwait(false);
+ }
+ }
+ finally
+ {
+ if (_fileHandle != null && !_fileHandle.IsClosed)
+ {
+ _fileHandle.ThreadPoolBinding?.Dispose();
+ _fileHandle.Dispose();
+ }
+
+ _preallocatedOverlapped?.Dispose();
+ _canSeek = false;
+ }
+ }
+
private void FlushOSBuffer()
{
if (!Interop.Kernel32.FlushFileBuffers(_fileHandle))
@@ -1544,6 +1570,7 @@ private Task FlushAsyncInternal(CancellationToken cancellationToken)
if (_fileHandle.IsClosed)
throw Error.GetFileNotOpen();
+ // TODO: https://github.com/dotnet/corefx/issues/32837 (stop doing this synchronous work).
// The always synchronous data transfer between the OS and the internal buffer is intentional
// because this is needed to allow concurrent async IO requests. Concurrent data transfer
// between the OS and the internal buffer will result in race conditions. Since FlushWrite and
diff --git a/src/System.Private.CoreLib/shared/System/IO/MemoryStream.cs b/src/System.Private.CoreLib/shared/System/IO/MemoryStream.cs
index 9bac0d818b12..538448f6383d 100644
--- a/src/System.Private.CoreLib/shared/System/IO/MemoryStream.cs
+++ b/src/System.Private.CoreLib/shared/System/IO/MemoryStream.cs
@@ -141,6 +141,17 @@ protected override void Dispose(bool disposing)
}
}
+ public override ValueTask DisposeAsync()
+ {
+ if (GetType() != typeof(MemoryStream))
+ {
+ return base.DisposeAsync();
+ }
+
+ Dispose(disposing: true);
+ return default;
+ }
+
// returns a bool saying whether we allocated a new array.
private bool EnsureCapacity(int value)
{
diff --git a/src/System.Private.CoreLib/shared/System/IO/PinnedBufferMemoryStream.cs b/src/System.Private.CoreLib/shared/System/IO/PinnedBufferMemoryStream.cs
index 94331a2ef826..28385a6b740e 100644
--- a/src/System.Private.CoreLib/shared/System/IO/PinnedBufferMemoryStream.cs
+++ b/src/System.Private.CoreLib/shared/System/IO/PinnedBufferMemoryStream.cs
@@ -15,8 +15,9 @@
===========================================================*/
using System;
-using System.Runtime.InteropServices;
using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
namespace System.IO
{
@@ -56,5 +57,11 @@ protected override void Dispose(bool disposing)
base.Dispose(disposing);
}
+
+ public override ValueTask DisposeAsync()
+ {
+ Dispose(disposing: true);
+ return default;
+ }
}
}
diff --git a/src/System.Private.CoreLib/shared/System/IO/Stream.cs b/src/System.Private.CoreLib/shared/System/IO/Stream.cs
index faeb69fb5418..f947b3f12471 100644
--- a/src/System.Private.CoreLib/shared/System/IO/Stream.cs
+++ b/src/System.Private.CoreLib/shared/System/IO/Stream.cs
@@ -25,7 +25,7 @@
namespace System.IO
{
- public abstract partial class Stream : MarshalByRefObject, IDisposable
+ public abstract partial class Stream : MarshalByRefObject, IDisposable, IAsyncDisposable
{
public static readonly Stream Null = new NullStream();
@@ -234,6 +234,12 @@ protected virtual void Dispose(bool disposing)
// torn down. This is the last code to run on cleanup for a stream.
}
+ public virtual ValueTask DisposeAsync()
+ {
+ return new ValueTask(Task.Factory.StartNew(s => ((Stream)s).Close(), this,
+ CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
+ }
+
public abstract void Flush();
public Task FlushAsync()
@@ -899,6 +905,8 @@ protected override void Dispose(bool disposing)
// Do nothing - we don't want NullStream singleton (static) to be closable
}
+ public override ValueTask DisposeAsync() => default;
+
public override void Flush()
{
}
@@ -1202,6 +1210,12 @@ protected override void Dispose(bool disposing)
}
}
+ public override ValueTask DisposeAsync()
+ {
+ lock (_stream)
+ return _stream.DisposeAsync();
+ }
+
public override void Flush()
{
lock (_stream)
diff --git a/src/System.Private.CoreLib/shared/System/IO/StreamWriter.cs b/src/System.Private.CoreLib/shared/System/IO/StreamWriter.cs
index 8d94ac60b989..705cd12639d3 100644
--- a/src/System.Private.CoreLib/shared/System/IO/StreamWriter.cs
+++ b/src/System.Private.CoreLib/shared/System/IO/StreamWriter.cs
@@ -195,32 +195,67 @@ protected override void Dispose(bool disposing)
}
finally
{
- // Dispose of our resources if this StreamWriter is closable.
- // Note: Console.Out and other such non closable streamwriters should be left alone
- if (!LeaveOpen && _stream != null)
+ CloseStreamFromDispose(disposing);
+ }
+ }
+
+ private void CloseStreamFromDispose(bool disposing)
+ {
+ // Dispose of our resources if this StreamWriter is closable.
+ if (!LeaveOpen && _stream != null)
+ {
+ try
{
- try
- {
- // Attempt to close the stream even if there was an IO error from Flushing.
- // Note that Stream.Close() can potentially throw here (may or may not be
- // due to the same Flush error). In this case, we still need to ensure
- // cleaning up internal resources, hence the finally block.
- if (disposing)
- {
- _stream.Close();
- }
- }
- finally
+ // Attempt to close the stream even if there was an IO error from Flushing.
+ // Note that Stream.Close() can potentially throw here (may or may not be
+ // due to the same Flush error). In this case, we still need to ensure
+ // cleaning up internal resources, hence the finally block.
+ if (disposing)
{
- _stream = null;
- _byteBuffer = null;
- _charBuffer = null;
- _encoding = null;
- _encoder = null;
- _charLen = 0;
- base.Dispose(disposing);
+ _stream.Close();
}
}
+ finally
+ {
+ _stream = null;
+ _byteBuffer = null;
+ _charBuffer = null;
+ _encoding = null;
+ _encoder = null;
+ _charLen = 0;
+ base.Dispose(disposing);
+ }
+ }
+ }
+
+ public override ValueTask DisposeAsync()
+ {
+ if (GetType() != typeof(StreamWriter))
+ {
+ // Since this is a derived StreamWriter, delegate to whatever logic
+ // the derived implementation already has in Dispose.
+ return new ValueTask(Task.Factory.StartNew(s => ((StreamWriter)s).Dispose(), this,
+ CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
+ }
+
+ return DisposeAsyncCore();
+ }
+
+ private async ValueTask DisposeAsyncCore()
+ {
+ Debug.Assert(GetType() == typeof(StreamWriter));
+
+ // Same logic as in Dispose(true), but async.
+ try
+ {
+ if (_stream != null)
+ {
+ await FlushAsync().ConfigureAwait(false);
+ }
+ }
+ finally
+ {
+ CloseStreamFromDispose(disposing: true);
}
}
diff --git a/src/System.Private.CoreLib/shared/System/IO/TextWriter.cs b/src/System.Private.CoreLib/shared/System/IO/TextWriter.cs
index 99f99b665c99..34cd74957c0a 100644
--- a/src/System.Private.CoreLib/shared/System/IO/TextWriter.cs
+++ b/src/System.Private.CoreLib/shared/System/IO/TextWriter.cs
@@ -18,7 +18,7 @@ namespace System.IO
//
// This class is intended for character output, not bytes.
// There are methods on the Stream class for writing bytes.
- public abstract partial class TextWriter : MarshalByRefObject, IDisposable
+ public abstract partial class TextWriter : MarshalByRefObject, IDisposable, IAsyncDisposable
{
public static readonly TextWriter Null = new NullTextWriter();
@@ -79,6 +79,15 @@ public void Dispose()
GC.SuppressFinalize(this);
}
+ public virtual ValueTask DisposeAsync()
+ {
+ // Since TextWriter is abstract, delegate to whatever logic a derived
+ // type put in place already in Dispose. The derived type can then
+ // optionally choose to override this to do better.
+ return new ValueTask(Task.Factory.StartNew(s => ((TextWriter)s).Dispose(), this,
+ CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
+ }
+
// Clears all buffers for this TextWriter and causes any buffered data to be
// written to the underlying device. This default method is empty, but
// descendant classes can override the method to provide the appropriate
@@ -732,6 +741,32 @@ public override void WriteLine(object value)
public override void Write(char value)
{
}
+
+ public override Task FlushAsync() => Task.CompletedTask;
+
+ public override Task WriteAsync(char value) => Task.CompletedTask;
+
+ public override Task WriteAsync(char[] buffer, int index, int count) => Task.CompletedTask;
+
+ public override Task WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) => Task.CompletedTask;
+
+ public override Task WriteAsync(string value) => Task.CompletedTask;
+
+ public override Task WriteAsync(StringBuilder value, CancellationToken cancellationToken = default) => Task.CompletedTask;
+
+ public override Task WriteLineAsync() => Task.CompletedTask;
+
+ public override Task WriteLineAsync(char value) => Task.CompletedTask;
+
+ public override Task WriteLineAsync(char[] buffer, int index, int count) => Task.CompletedTask;
+
+ public override Task WriteLineAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) => Task.CompletedTask;
+
+ public override Task WriteLineAsync(string value) => Task.CompletedTask;
+
+ public override Task WriteLineAsync(StringBuilder value, CancellationToken cancellationToken = default) => Task.CompletedTask;
+
+ public override ValueTask DisposeAsync() => default;
}
public static TextWriter Synchronized(TextWriter writer)
@@ -774,6 +809,14 @@ protected override void Dispose(bool disposing)
((IDisposable)_out).Dispose();
}
+ // [MethodImpl(MethodImplOptions.Synchronized)]
+ public override ValueTask DisposeAsync()
+ {
+ // TODO: https://github.com/dotnet/coreclr/issues/20499
+ // Manual synchronization should be replaced by Synchronized.
+ lock (this) return _out.DisposeAsync();
+ }
+
[MethodImpl(MethodImplOptions.Synchronized)]
public override void Flush() => _out.Flush();
diff --git a/src/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStream.cs b/src/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStream.cs
index d4af4cfee370..73e92ad30927 100644
--- a/src/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStream.cs
+++ b/src/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStream.cs
@@ -231,6 +231,17 @@ protected override void Dispose(bool disposing)
base.Dispose(disposing);
}
+ public override ValueTask DisposeAsync()
+ {
+ if (GetType() != typeof(UnmanagedMemoryStream))
+ {
+ return base.DisposeAsync();
+ }
+
+ Dispose(disposing: true);
+ return default;
+ }
+
private void EnsureNotClosed()
{
if (!_isOpen)
diff --git a/src/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStreamWrapper.cs b/src/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStreamWrapper.cs
index 9a598951ee80..dbc88488b551 100644
--- a/src/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStreamWrapper.cs
+++ b/src/System.Private.CoreLib/shared/System/IO/UnmanagedMemoryStreamWrapper.cs
@@ -59,6 +59,11 @@ protected override void Dispose(bool disposing)
}
}
+ public override ValueTask DisposeAsync()
+ {
+ return _unmanagedStream.DisposeAsync();
+ }
+
public override void Flush()
{
_unmanagedStream.Flush();
diff --git a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/AsyncIteratorMethodBuilder.cs b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/AsyncIteratorMethodBuilder.cs
new file mode 100644
index 000000000000..7605bdd55e16
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/AsyncIteratorMethodBuilder.cs
@@ -0,0 +1,77 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace System.Runtime.CompilerServices
+{
+ /// Represents a builder for asynchronous iterators.
+ [StructLayout(LayoutKind.Auto)]
+ public struct AsyncIteratorMethodBuilder
+ {
+ // AsyncIteratorMethodBuilder is used by the language compiler as part of generating
+ // async iterators. For now, the implementation just wraps AsyncTaskMethodBuilder, as
+ // most of the logic is shared. However, in the future this could be changed and
+ // optimized. For example, we do need to allocate an object (once) to flow state like
+ // ExecutionContext, which AsyncTaskMethodBuilder handles, but it handles it by
+ // allocating a Task-derived object. We could optimize this further by removing
+ // the Task from the hierarchy, but in doing so we'd also lose a variety of optimizations
+ // related to it, so we'd need to replicate all of those optimizations (e.g. storing
+ // that box object directly into a Task's continuation field).
+
+ private AsyncTaskMethodBuilder _methodBuilder; // mutable struct; do not make it readonly
+
+ /// Creates an instance of the struct.
+ /// The initialized instance.
+ public static AsyncIteratorMethodBuilder Create() =>
+#if CORERT
+ // corert's AsyncTaskMethodBuilder.Create() currently does additional debugger-related
+ // work, so we need to delegate to it.
+ new AsyncIteratorMethodBuilder() { _methodBuilder = AsyncTaskMethodBuilder.Create() };
+#else
+ default; // coreclr's AsyncTaskMethodBuilder.Create just returns default as well
+#endif
+
+ /// Invokes on the state machine while guarding the
+ /// The type of the state machine.
+ /// The state machine instance, passed by reference.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void MoveNext(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine =>
+#if CORERT
+ _methodBuilder.Start(ref stateMachine);
+#else
+ AsyncMethodBuilderCore.Start(ref stateMachine);
+#endif
+
+ /// Schedules the state machine to proceed to the next action when the specified awaiter completes.
+ /// The type of the awaiter.
+ /// The type of the state machine.
+ /// The awaiter.
+ /// The state machine.
+ public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine)
+ where TAwaiter : INotifyCompletion
+ where TStateMachine : IAsyncStateMachine =>
+ _methodBuilder.AwaitOnCompleted(ref awaiter, ref stateMachine);
+
+ /// Schedules the state machine to proceed to the next action when the specified awaiter completes.
+ /// The type of the awaiter.
+ /// The type of the state machine.
+ /// The awaiter.
+ /// The state machine.
+ public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine)
+ where TAwaiter : ICriticalNotifyCompletion
+ where TStateMachine : IAsyncStateMachine =>
+ _methodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
+
+ /// Marks iteration as being completed, whether successfully or otherwise.
+ public void Complete() =>
+ _methodBuilder.SetResult();
+
+ /// Gets an object that may be used to uniquely identify this builder to the debugger.
+ internal object ObjectIdForDebugger =>
+ _methodBuilder.ObjectIdForDebugger;
+ }
+}
diff --git a/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs b/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs
index 694514ef07e8..983281491778 100644
--- a/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs
+++ b/src/System.Private.CoreLib/shared/System/Threading/ExecutionContext.cs
@@ -21,6 +21,8 @@ namespace System.Threading
{
public delegate void ContextCallback(object state);
+ internal delegate void ContextCallback(ref TState state);
+
public sealed class ExecutionContext : IDisposable, ISerializable
{
internal static readonly ExecutionContext Default = new ExecutionContext(isDefault: true);
@@ -201,6 +203,85 @@ internal static void RunInternal(ExecutionContext executionContext, ContextCallb
edi?.Throw();
}
+ // Direct copy of the above RunInternal overload, except that it passes the state into the callback strongly-typed and by ref.
+ internal static void RunInternal(ExecutionContext executionContext, ContextCallback callback, ref TState state)
+ {
+ // Note: ExecutionContext.RunInternal is an extremely hot function and used by every await, ThreadPool execution, etc.
+ // Note: Manual enregistering may be addressed by "Exception Handling Write Through Optimization"
+ // https://github.com/dotnet/coreclr/blob/master/Documentation/design-docs/eh-writethru.md
+
+ // Enregister variables with 0 post-fix so they can be used in registers without EH forcing them to stack
+ // Capture references to Thread Contexts
+ Thread currentThread0 = Thread.CurrentThread;
+ Thread currentThread = currentThread0;
+ ExecutionContext previousExecutionCtx0 = currentThread0.ExecutionContext;
+
+ // Store current ExecutionContext and SynchronizationContext as "previousXxx".
+ // This allows us to restore them and undo any Context changes made in callback.Invoke
+ // so that they won't "leak" back into caller.
+ // These variables will cross EH so be forced to stack
+ ExecutionContext previousExecutionCtx = previousExecutionCtx0;
+ SynchronizationContext previousSyncCtx = currentThread0.SynchronizationContext;
+
+ if (executionContext != null && executionContext.m_isDefault)
+ {
+ // Default is a null ExecutionContext internally
+ executionContext = null;
+ }
+
+ if (previousExecutionCtx0 != executionContext)
+ {
+ // Restore changed ExecutionContext
+ currentThread0.ExecutionContext = executionContext;
+ if ((executionContext != null && executionContext.HasChangeNotifications) ||
+ (previousExecutionCtx0 != null && previousExecutionCtx0.HasChangeNotifications))
+ {
+ // There are change notifications; trigger any affected
+ OnValuesChanged(previousExecutionCtx0, executionContext);
+ }
+ }
+
+ ExceptionDispatchInfo edi = null;
+ try
+ {
+ callback.Invoke(ref state);
+ }
+ catch (Exception ex)
+ {
+ // Note: we have a "catch" rather than a "finally" because we want
+ // to stop the first pass of EH here. That way we can restore the previous
+ // context before any of our callers' EH filters run.
+ edi = ExceptionDispatchInfo.Capture(ex);
+ }
+
+ // Re-enregistrer variables post EH with 1 post-fix so they can be used in registers rather than from stack
+ SynchronizationContext previousSyncCtx1 = previousSyncCtx;
+ Thread currentThread1 = currentThread;
+ // The common case is that these have not changed, so avoid the cost of a write barrier if not needed.
+ if (currentThread1.SynchronizationContext != previousSyncCtx1)
+ {
+ // Restore changed SynchronizationContext back to previous
+ currentThread1.SynchronizationContext = previousSyncCtx1;
+ }
+
+ ExecutionContext previousExecutionCtx1 = previousExecutionCtx;
+ ExecutionContext currentExecutionCtx1 = currentThread1.ExecutionContext;
+ if (currentExecutionCtx1 != previousExecutionCtx1)
+ {
+ // Restore changed ExecutionContext back to previous
+ currentThread1.ExecutionContext = previousExecutionCtx1;
+ if ((currentExecutionCtx1 != null && currentExecutionCtx1.HasChangeNotifications) ||
+ (previousExecutionCtx1 != null && previousExecutionCtx1.HasChangeNotifications))
+ {
+ // There are change notifications; trigger any affected
+ OnValuesChanged(currentExecutionCtx1, previousExecutionCtx1);
+ }
+ }
+
+ // If exception was thrown by callback, rethrow it now original contexts are restored
+ edi?.Throw();
+ }
+
internal static void OnValuesChanged(ExecutionContext previousExecutionCtx, ExecutionContext nextExecutionCtx)
{
Debug.Assert(previousExecutionCtx != nextExecutionCtx);
diff --git a/src/System.Private.CoreLib/shared/System/Threading/Tasks/Sources/ManualResetValueTaskSourceLogic.cs b/src/System.Private.CoreLib/shared/System/Threading/Tasks/Sources/ManualResetValueTaskSourceLogic.cs
new file mode 100644
index 000000000000..008cab167e06
--- /dev/null
+++ b/src/System.Private.CoreLib/shared/System/Threading/Tasks/Sources/ManualResetValueTaskSourceLogic.cs
@@ -0,0 +1,273 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.ExceptionServices;
+using System.Runtime.InteropServices;
+
+namespace System.Threading.Tasks.Sources
+{
+ /// Provides the core logic for implementing a manual-reset or .
+ ///
+ [StructLayout(LayoutKind.Auto)]
+ public struct ManualResetValueTaskSourceLogic
+ {
+ ///
+ /// The callback to invoke when the operation completes if was called before the operation completed,
+ /// or if the operation completed before a callback was supplied,
+ /// or null if a callback hasn't yet been provided and the operation hasn't yet completed.
+ ///
+ private Action