diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs b/src/mscorlib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs index 05850605b841..1c0392a27cd1 100644 --- a/src/mscorlib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs +++ b/src/mscorlib/src/System/Runtime/CompilerServices/AsyncMethodBuilder.cs @@ -277,7 +277,7 @@ private Task Task public struct AsyncTaskMethodBuilder { /// A cached VoidTaskResult task used for builders that complete synchronously. - private readonly static Task s_cachedCompleted = AsyncTaskMethodBuilder.s_defaultResultTask; + private readonly static Task s_cachedCompleted = Task.s_defaultResultTask; /// The generic builder object to which this non-generic instance delegates. private AsyncTaskMethodBuilder m_builder; // mutable struct: must not be readonly @@ -424,9 +424,6 @@ internal void SetNotificationForWaitCompletion(bool enabled) [HostProtection(Synchronization = true, ExternalThreading = true)] public struct AsyncTaskMethodBuilder { - /// A cached task for default(TResult). - internal readonly static Task s_defaultResultTask = AsyncTaskCache.CreateCacheableTask(default(TResult)); - // WARNING: For performance reasons, the m_task field is lazily initialized. // For correct results, the struct AsyncTaskMethodBuilder must // always be used from the same location/copy, at least until m_task is @@ -594,8 +591,8 @@ public void SetResult(TResult result) var task = m_task; if (task == null) { - m_task = GetTaskForResult(result); - Contract.Assert(m_task != null, "GetTaskForResult should never return null"); + m_task = System.Threading.Tasks.Task.FromResult(result); + Contract.Assert(m_task != null, "Task.FromResult should never return null"); } // Slow path: complete the existing task. else @@ -706,136 +703,6 @@ internal void SetNotificationForWaitCompletion(bool enabled) /// when no other threads are in the middle of accessing this property or this.Task. /// private object ObjectIdForDebugger { get { return this.Task; } } - - /// - /// Gets a task for the specified result. This will either - /// be a cached or new task, never null. - /// - /// The result for which we need a task. - /// The completed task containing the result. - [SecuritySafeCritical] // for JitHelpers.UnsafeCast - private Task GetTaskForResult(TResult result) - { - Contract.Ensures( - EqualityComparer.Default.Equals(result, Contract.Result>().Result), - "The returned task's Result must return the same value as the specified result value."); - - // The goal of this function is to be give back a cached task if possible, - // or to otherwise give back a new task. To give back a cached task, - // we need to be able to evaluate the incoming result value, and we need - // to avoid as much overhead as possible when doing so, as this function - // is invoked as part of the return path from every async method. - // Most tasks won't be cached, and thus we need the checks for those that are - // to be as close to free as possible. This requires some trickiness given the - // lack of generic specialization in .NET. - // - // Be very careful when modifying this code. It has been tuned - // to comply with patterns recognized by both 32-bit and 64-bit JITs. - // If changes are made here, be sure to look at the generated assembly, as - // small tweaks can have big consequences for what does and doesn't get optimized away. - // - // Note that this code only ever accesses a static field when it knows it'll - // find a cached value, since static fields (even if readonly and integral types) - // require special access helpers in this NGEN'd and domain-neutral. - - if (null != (object)default(TResult)) // help the JIT avoid the value type branches for ref types - { - // Special case simple value types: - // - Boolean - // - Byte, SByte - // - Char - // - Decimal - // - Int32, UInt32 - // - Int64, UInt64 - // - Int16, UInt16 - // - IntPtr, UIntPtr - // As of .NET 4.5, the (Type)(object)result pattern used below - // is recognized and optimized by both 32-bit and 64-bit JITs. - - // For Boolean, we cache all possible values. - if (typeof(TResult) == typeof(Boolean)) // only the relevant branches are kept for each value-type generic instantiation - { - Boolean value = (Boolean)(object)result; - Task task = value ? AsyncTaskCache.TrueTask : AsyncTaskCache.FalseTask; - return JitHelpers.UnsafeCast>(task); // UnsafeCast avoids type check we know will succeed - } - // For Int32, we cache a range of common values, e.g. [-1,4). - else if (typeof(TResult) == typeof(Int32)) - { - // Compare to constants to avoid static field access if outside of cached range. - // We compare to the upper bound first, as we're more likely to cache miss on the upper side than on the - // lower side, due to positive values being more common than negative as return values. - Int32 value = (Int32)(object)result; - if (value < AsyncTaskCache.EXCLUSIVE_INT32_MAX && - value >= AsyncTaskCache.INCLUSIVE_INT32_MIN) - { - Task task = AsyncTaskCache.Int32Tasks[value - AsyncTaskCache.INCLUSIVE_INT32_MIN]; - return JitHelpers.UnsafeCast>(task); // UnsafeCast avoids a type check we know will succeed - } - } - // For other known value types, we only special-case 0 / default(TResult). - else if ( - (typeof(TResult) == typeof(UInt32) && default(UInt32) == (UInt32)(object)result) || - (typeof(TResult) == typeof(Byte) && default(Byte) == (Byte)(object)result) || - (typeof(TResult) == typeof(SByte) && default(SByte) == (SByte)(object)result) || - (typeof(TResult) == typeof(Char) && default(Char) == (Char)(object)result) || - (typeof(TResult) == typeof(Decimal) && default(Decimal) == (Decimal)(object)result) || - (typeof(TResult) == typeof(Int64) && default(Int64) == (Int64)(object)result) || - (typeof(TResult) == typeof(UInt64) && default(UInt64) == (UInt64)(object)result) || - (typeof(TResult) == typeof(Int16) && default(Int16) == (Int16)(object)result) || - (typeof(TResult) == typeof(UInt16) && default(UInt16) == (UInt16)(object)result) || - (typeof(TResult) == typeof(IntPtr) && default(IntPtr) == (IntPtr)(object)result) || - (typeof(TResult) == typeof(UIntPtr) && default(UIntPtr) == (UIntPtr)(object)result)) - { - return s_defaultResultTask; - } - } - else if (result == null) // optimized away for value types - { - return s_defaultResultTask; - } - - // No cached task is available. Manufacture a new one for this result. - return new Task(result); - } - } - - /// Provides a cache of closed generic tasks for async methods. - internal static class AsyncTaskCache - { - // All static members are initialized inline to ensure type is beforefieldinit - - /// A cached Task{Boolean}.Result == true. - internal readonly static Task TrueTask = CreateCacheableTask(true); - /// A cached Task{Boolean}.Result == false. - internal readonly static Task FalseTask = CreateCacheableTask(false); - - /// The cache of Task{Int32}. - internal readonly static Task[] Int32Tasks = CreateInt32Tasks(); - /// The minimum value, inclusive, for which we want a cached task. - internal const Int32 INCLUSIVE_INT32_MIN = -1; - /// The maximum value, exclusive, for which we want a cached task. - internal const Int32 EXCLUSIVE_INT32_MAX = 9; - /// Creates an array of cached tasks for the values in the range [INCLUSIVE_MIN,EXCLUSIVE_MAX). - private static Task[] CreateInt32Tasks() - { - Contract.Assert(EXCLUSIVE_INT32_MAX >= INCLUSIVE_INT32_MIN, "Expected max to be at least min"); - var tasks = new Task[EXCLUSIVE_INT32_MAX - INCLUSIVE_INT32_MIN]; - for (int i = 0; i < tasks.Length; i++) - { - tasks[i] = CreateCacheableTask(i + INCLUSIVE_INT32_MIN); - } - return tasks; - } - - /// Creates a non-disposable task. - /// Specifies the result type. - /// The result for the task. - /// The cacheable task. - internal static Task CreateCacheableTask(TResult result) - { - return new Task(false, result, (TaskCreationOptions)InternalTaskOptions.DoNotDispose, default(CancellationToken)); - } } /// Holds state related to the builder's IAsyncStateMachine. diff --git a/src/mscorlib/src/System/Threading/Tasks/Task.cs b/src/mscorlib/src/System/Threading/Tasks/Task.cs index 6031457c5b4a..f72166057f14 100644 --- a/src/mscorlib/src/System/Threading/Tasks/Task.cs +++ b/src/mscorlib/src/System/Threading/Tasks/Task.cs @@ -5521,7 +5521,7 @@ public static int WaitAny(Task[] tasks, int millisecondsTimeout, CancellationTok /// The successfully completed task. public static Task FromResult(TResult result) { - return new Task(result); + return Task.GetTaskForResult(result); } /// Creates a that's completed exceptionally with the specified exception. diff --git a/src/mscorlib/src/System/Threading/Tasks/future.cs b/src/mscorlib/src/System/Threading/Tasks/future.cs index 120782853e91..49171a9ad218 100644 --- a/src/mscorlib/src/System/Threading/Tasks/future.cs +++ b/src/mscorlib/src/System/Threading/Tasks/future.cs @@ -1575,7 +1575,102 @@ ref stackMark #endregion #endregion - + + /// A cached task for default(TResult). + internal readonly static Task s_defaultResultTask = AsyncTaskCache.CreateCacheableTask(default(TResult)); + + /// + /// Gets a task for the specified result. This will either + /// be a cached or new task, never null. + /// + /// The result for which we need a task. + /// The completed task containing the result. + [SecuritySafeCritical] // for JitHelpers.UnsafeCast + internal static Task GetTaskForResult(TResult result) + { + Contract.Ensures( + EqualityComparer.Default.Equals(result, Contract.Result>().Result), + "The returned task's Result must return the same value as the specified result value."); + + // The goal of this function is to be give back a cached task if possible, + // or to otherwise give back a new task. To give back a cached task, + // we need to be able to evaluate the incoming result value, and we need + // to avoid as much overhead as possible when doing so, as this function + // is invoked as part of the return path from every async method. + // Most tasks won't be cached, and thus we need the checks for those that are + // to be as close to free as possible. This requires some trickiness given the + // lack of generic specialization in .NET. + // + // Be very careful when modifying this code. It has been tuned + // to comply with patterns recognized by both 32-bit and 64-bit JITs. + // If changes are made here, be sure to look at the generated assembly, as + // small tweaks can have big consequences for what does and doesn't get optimized away. + // + // Note that this code only ever accesses a static field when it knows it'll + // find a cached value, since static fields (even if readonly and integral types) + // require special access helpers in this NGEN'd and domain-neutral. + + if (null != (object)default(TResult)) // help the JIT avoid the value type branches for ref types + { + // Special case simple value types: + // - Boolean + // - Byte, SByte + // - Char + // - Decimal + // - Int32, UInt32 + // - Int64, UInt64 + // - Int16, UInt16 + // - IntPtr, UIntPtr + // As of .NET 4.5, the (Type)(object)result pattern used below + // is recognized and optimized by both 32-bit and 64-bit JITs. + + // For Boolean, we cache all possible values. + if (typeof(TResult) == typeof(Boolean)) // only the relevant branches are kept for each value-type generic instantiation + { + Boolean value = (Boolean)(object)result; + Task task = value ? AsyncTaskCache.TrueTask : AsyncTaskCache.FalseTask; + return JitHelpers.UnsafeCast>(task); // UnsafeCast avoids type check we know will succeed + } + // For Int32, we cache a range of common values, e.g. [-1,4). + else if (typeof(TResult) == typeof(Int32)) + { + // Compare to constants to avoid static field access if outside of cached range. + // We compare to the upper bound first, as we're more likely to cache miss on the upper side than on the + // lower side, due to positive values being more common than negative as return values. + Int32 value = (Int32)(object)result; + if (value < AsyncTaskCache.EXCLUSIVE_INT32_MAX && + value >= AsyncTaskCache.INCLUSIVE_INT32_MIN) + { + Task task = AsyncTaskCache.Int32Tasks[value - AsyncTaskCache.INCLUSIVE_INT32_MIN]; + return JitHelpers.UnsafeCast>(task); // UnsafeCast avoids a type check we know will succeed + } + } + // For other known value types, we only special-case 0 / default(TResult). + else if ( + (typeof(TResult) == typeof(UInt32) && default(UInt32) == (UInt32)(object)result) || + (typeof(TResult) == typeof(Byte) && default(Byte) == (Byte)(object)result) || + (typeof(TResult) == typeof(SByte) && default(SByte) == (SByte)(object)result) || + (typeof(TResult) == typeof(Char) && default(Char) == (Char)(object)result) || + (typeof(TResult) == typeof(Decimal) && default(Decimal) == (Decimal)(object)result) || + (typeof(TResult) == typeof(Int64) && default(Int64) == (Int64)(object)result) || + (typeof(TResult) == typeof(UInt64) && default(UInt64) == (UInt64)(object)result) || + (typeof(TResult) == typeof(Int16) && default(Int16) == (Int16)(object)result) || + (typeof(TResult) == typeof(UInt16) && default(UInt16) == (UInt16)(object)result) || + (typeof(TResult) == typeof(IntPtr) && default(IntPtr) == (IntPtr)(object)result) || + (typeof(TResult) == typeof(UIntPtr) && default(UIntPtr) == (UIntPtr)(object)result)) + { + return s_defaultResultTask; + } + } + else if (result == null) // optimized away for value types + { + return s_defaultResultTask; + } + + // No cached task is available. Manufacture a new one for this result. + return new Task(result); + } + /// /// Subscribes an to receive notification of the final state of this . /// @@ -1618,6 +1713,44 @@ IDisposable IObservable.Subscribe(IObserver observer) #endif } + /// Provides a cache of closed generic tasks for async methods. + internal static class AsyncTaskCache + { + // All static members are initialized inline to ensure type is beforefieldinit + + /// A cached Task{Boolean}.Result == true. + internal readonly static Task TrueTask = CreateCacheableTask(true); + /// A cached Task{Boolean}.Result == false. + internal readonly static Task FalseTask = CreateCacheableTask(false); + + /// The cache of Task{Int32}. + internal readonly static Task[] Int32Tasks = CreateInt32Tasks(); + /// The minimum value, inclusive, for which we want a cached task. + internal const Int32 INCLUSIVE_INT32_MIN = -1; + /// The maximum value, exclusive, for which we want a cached task. + internal const Int32 EXCLUSIVE_INT32_MAX = 9; + /// Creates an array of cached tasks for the values in the range [INCLUSIVE_MIN,EXCLUSIVE_MAX). + private static Task[] CreateInt32Tasks() + { + Contract.Assert(EXCLUSIVE_INT32_MAX >= INCLUSIVE_INT32_MIN, "Expected max to be at least min"); + var tasks = new Task[EXCLUSIVE_INT32_MAX - INCLUSIVE_INT32_MIN]; + for (int i = 0; i < tasks.Length; i++) + { + tasks[i] = CreateCacheableTask(i + INCLUSIVE_INT32_MIN); + } + return tasks; + } + + /// Creates a non-disposable task. + /// Specifies the result type. + /// The result for the task. + /// The cacheable task. + internal static Task CreateCacheableTask(TResult result) + { + return new Task(false, result, (TaskCreationOptions)InternalTaskOptions.DoNotDispose, default(CancellationToken)); + } + } + #if SUPPORT_IOBSERVABLE // Class that calls RemoveContinuation if Dispose() is called before task completion internal class DisposableSubscription : IDisposable @@ -1644,8 +1777,8 @@ void IDisposable.Dispose() } #endif - // Proxy class for better debugging experience - internal class SystemThreadingTasks_FutureDebugView + // Proxy class for better debugging experience + internal class SystemThreadingTasks_FutureDebugView { private Task m_task;