diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Common/System/Threading/Tasks/TaskToApm.cs b/src/Microsoft.Data.SqlClient/netcore/src/Common/System/Threading/Tasks/TaskToApm.cs deleted file mode 100644 index add41f588e..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Common/System/Threading/Tasks/TaskToApm.cs +++ /dev/null @@ -1,189 +0,0 @@ -// 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. - -// Helper methods for using Tasks to implement the APM pattern. -// -// Example usage, wrapping a Task-returning FooAsync method with Begin/EndFoo methods: -// -// public IAsyncResult BeginFoo(..., AsyncCallback callback, object state) -// { -// Task t = FooAsync(...); -// return TaskToApm.Begin(t, callback, state); -// } -// public int EndFoo(IAsyncResult asyncResult) -// { -// return TaskToApm.End(asyncResult); -// } - -using System.Diagnostics; - -namespace System.Threading.Tasks -{ - /// - /// Provides support for efficiently using Tasks to implement the APM (Begin/End) pattern. - /// - internal static class TaskToApm - { - /// - /// Marshals the Task as an IAsyncResult, using the supplied callback and state - /// to implement the APM pattern. - /// - /// The Task to be marshaled. - /// The callback to be invoked upon completion. - /// The state to be stored in the IAsyncResult. - /// An IAsyncResult to represent the task's asynchronous operation. - public static IAsyncResult Begin(Task task, AsyncCallback callback, object state) - { - Debug.Assert(task != null); - - // If the task has already completed, then since the Task's CompletedSynchronously==false - // and we want it to be true, we need to create a new IAsyncResult. (We also need the AsyncState to match.) - IAsyncResult asyncResult; - if (task.IsCompleted) - { - // Synchronous completion. - asyncResult = new TaskWrapperAsyncResult(task, state, completedSynchronously: true); - callback?.Invoke(asyncResult); - } - else - { - // For asynchronous completion we need to schedule a callback. Whether we can use the Task as the IAsyncResult - // depends on whether the Task's AsyncState has reference equality with the requested state. - asyncResult = task.AsyncState == state ? (IAsyncResult)task : new TaskWrapperAsyncResult(task, state, completedSynchronously: false); - if (callback != null) - { - InvokeCallbackWhenTaskCompletes(task, callback, asyncResult); - } - } - return asyncResult; - } - - /// Processes an IAsyncResult returned by Begin. - /// The IAsyncResult to unwrap. - public static void End(IAsyncResult asyncResult) - { - Task task; - - // If the IAsyncResult is our task-wrapping IAsyncResult, extract the Task. - var twar = asyncResult as TaskWrapperAsyncResult; - if (twar != null) - { - task = twar.Task; - Debug.Assert(task != null, "TaskWrapperAsyncResult should never wrap a null Task."); - } - else - { - // Otherwise, the IAsyncResult should be a Task. - task = asyncResult as Task; - } - - // Make sure we actually got a task, then complete the operation by waiting on it. - if (task == null) - { - throw new ArgumentNullException(); - } - - task.GetAwaiter().GetResult(); - } - - /// Processes an IAsyncResult returned by Begin. - /// The IAsyncResult to unwrap. - public static TResult End(IAsyncResult asyncResult) - { - Task task; - - // If the IAsyncResult is our task-wrapping IAsyncResult, extract the Task. - var twar = asyncResult as TaskWrapperAsyncResult; - if (twar != null) - { - task = twar.Task as Task; - Debug.Assert(twar.Task != null, "TaskWrapperAsyncResult should never wrap a null Task."); - } - else - { - // Otherwise, the IAsyncResult should be a Task. - task = asyncResult as Task; - } - - // Make sure we actually got a task, then complete the operation by waiting on it. - if (task == null) - { - throw new ArgumentNullException(); - } - - return task.GetAwaiter().GetResult(); - } - - /// Invokes the callback asynchronously when the task has completed. - /// The Task to await. - /// The callback to invoke when the Task completes. - /// The Task used as the IAsyncResult. - private static void InvokeCallbackWhenTaskCompletes(Task antecedent, AsyncCallback callback, IAsyncResult asyncResult) - { - Debug.Assert(antecedent != null); - Debug.Assert(callback != null); - Debug.Assert(asyncResult != null); - - // We use OnCompleted rather than ContinueWith in order to avoid running synchronously - // if the task has already completed by the time we get here. This is separated out into - // its own method currently so that we only pay for the closure if necessary. - antecedent.ConfigureAwait(continueOnCapturedContext: false) - .GetAwaiter() - .OnCompleted(() => callback(asyncResult)); - - // PERFORMANCE NOTE: - // Assuming we're in the default ExecutionContext, the "slow path" of an incomplete - // task will result in four allocations: the new IAsyncResult, the delegate+closure - // in this method, and the continuation object inside of OnCompleted (necessary - // to capture both the Action delegate and the ExecutionContext in a single object). - // In the future, if performance requirements drove a need, those four - // allocations could be reduced to one. This would be achieved by having TaskWrapperAsyncResult - // also implement ITaskCompletionAction (and optionally IThreadPoolWorkItem). It would need - // additional fields to store the AsyncCallback and an ExecutionContext. Once configured, - // it would be set into the Task as a continuation. Its Invoke method would then be run when - // the antecedent completed, and, doing all of the necessary work to flow ExecutionContext, - // it would invoke the AsyncCallback. It could also have a field on it for the antecedent, - // so that the End method would have access to the completed antecedent. For related examples, - // see other implementations of ITaskCompletionAction, and in particular ReadWriteTask - // used in Stream.Begin/EndXx's implementation. - } - - /// - /// Provides a simple IAsyncResult that wraps a Task. This, in effect, allows - /// for overriding what's seen for the CompletedSynchronously and AsyncState values. - /// - private sealed class TaskWrapperAsyncResult : IAsyncResult - { - /// The wrapped Task. - internal readonly Task Task; - /// The new AsyncState value. - private readonly object _state; - /// The new CompletedSynchronously value. - private readonly bool _completedSynchronously; - - /// Initializes the IAsyncResult with the Task to wrap and the overriding AsyncState and CompletedSynchronously values. - /// The Task to wrap. - /// The new AsyncState value - /// The new CompletedSynchronously value. - internal TaskWrapperAsyncResult(Task task, object state, bool completedSynchronously) - { - Debug.Assert(task != null); - Debug.Assert(!completedSynchronously || task.IsCompleted, "If completedSynchronously is true, the task must be completed."); - - this.Task = task; - _state = state; - _completedSynchronously = completedSynchronously; - } - - // The IAsyncResult implementation. - // - IsCompleted and AsyncWaitHandle just pass through to the Task. - // - AsyncState and CompletedSynchronously return the corresponding values stored in this object. - - object IAsyncResult.AsyncState { get { return _state; } } - bool IAsyncResult.CompletedSynchronously { get { return _completedSynchronously; } } - bool IAsyncResult.IsCompleted { get { return this.Task.IsCompleted; } } - WaitHandle IAsyncResult.AsyncWaitHandle { get { return ((IAsyncResult)this.Task).AsyncWaitHandle; } } - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index c35899a762..f13d43c931 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -706,7 +706,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSequentialStream.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSequentialStream.cs index c97e51f577..52b965325d 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSequentialStream.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSequentialStream.cs @@ -4,13 +4,14 @@ using System; using System.Diagnostics; +using System.IO; using System.Threading; using System.Threading.Tasks; using Microsoft.Data.Common; namespace Microsoft.Data.SqlClient { - sealed internal class SqlSequentialStream : System.IO.Stream + internal sealed class SqlSequentialStream : Stream { private SqlDataReader _reader; // The SqlDataReader that we are reading data from private readonly int _columnIndex; // The index of out column in the table @@ -302,7 +303,7 @@ public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, Asy Task readTask = ReadAsync(buffer, offset, count, CancellationToken.None); if (callback != null) { - readTask.ContinueWith((t) => callback(t), TaskScheduler.Default); + readTask.ContinueWith(t => callback(t), TaskScheduler.Default); } return readTask; } @@ -328,11 +329,19 @@ public override int EndRead(IAsyncResult asyncResult) return readTask.Result; } #else - public override IAsyncResult BeginRead(byte[] array, int offset, int count, AsyncCallback asyncCallback, object asyncState) => - TaskToApm.Begin(ReadAsync(array, offset, count, CancellationToken.None), asyncCallback, asyncState); + public override IAsyncResult BeginRead( + byte[] array, + int offset, + int count, + AsyncCallback asyncCallback, + object asyncState) + { + Task readTask = ReadAsync(array, offset, count, CancellationToken.None); + return TaskToAsyncResult.Begin(readTask, asyncCallback, asyncState); + } public override int EndRead(IAsyncResult asyncResult) => - TaskToApm.End(asyncResult); + TaskToAsyncResult.End(asyncResult); #endif } }