@@ -5216,8 +5216,9 @@ private static int WaitAnyCore(Task[] tasks, int millisecondsTimeout, Cancellati
5216
5216
/// <param name="result">The result to store into the completed task.</param>
5217
5217
/// <returns>The successfully completed task.</returns>
5218
5218
[ MethodImpl ( MethodImplOptions . AggressiveInlining ) ] // method looks long, but for a given TResult it results in a relatively small amount of asm
5219
- public static Task < TResult > FromResult < TResult > ( TResult result )
5219
+ public static unsafe Task < TResult > FromResult < TResult > ( TResult result )
5220
5220
{
5221
+ #pragma warning disable 8500 // address of / sizeof of managed types
5221
5222
// The goal of this function is to be give back a cached task if possible, or to otherwise give back a new task.
5222
5223
// To give back a cached task, we need to be able to evaluate the incoming result value, and we need to avoid as
5223
5224
// much overhead as possible when doing so, as this function is invoked as part of the return path from every async
@@ -5229,48 +5230,42 @@ public static Task<TResult> FromResult<TResult>(TResult result)
5229
5230
// null reference types and default(Nullable<T>)
5230
5231
return Task < TResult > . s_defaultResultTask ;
5231
5232
}
5232
- else if ( typeof ( TResult ) . IsValueType ) // help the JIT avoid the value type branches for ref types
5233
+
5234
+ // For Boolean, we cache all possible values.
5235
+ if ( typeof ( TResult ) == typeof ( bool ) ) // only the relevant branches are kept for each value-type generic instantiation
5233
5236
{
5234
- // For Boolean, we cache all possible values.
5235
- if ( typeof ( TResult ) == typeof ( bool ) ) // only the relevant branches are kept for each value-type generic instantiation
5236
- {
5237
- bool value = ( bool ) ( object ) result ! ;
5238
- Task < bool > task = value ? TaskCache . s_trueTask : TaskCache . s_falseTask ;
5239
- return Unsafe . As < Task < TResult > > ( task ) ; // UnsafeCast avoids type check we know will succeed
5240
- }
5241
- // For Int32, we cache a range of common values, [-1,9).
5242
- else if ( typeof ( TResult ) == typeof ( int ) )
5237
+ Task < bool > task = * ( bool * ) & result ? TaskCache . s_trueTask : TaskCache . s_falseTask ;
5238
+ return * ( Task < TResult > * ) & task ;
5239
+ }
5240
+
5241
+ // For Int32, we cache a range of common values, [-1,9).
5242
+ if ( typeof ( TResult ) == typeof ( int ) )
5243
+ {
5244
+ // Compare to constants to avoid static field access if outside of cached range.
5245
+ int value = * ( int * ) & result ;
5246
+ if ( ( uint ) ( value - TaskCache . InclusiveInt32Min ) < ( TaskCache . ExclusiveInt32Max - TaskCache . InclusiveInt32Min ) )
5243
5247
{
5244
- // Compare to constants to avoid static field access if outside of cached range.
5245
- int value = ( int ) ( object ) result ! ;
5246
- if ( ( uint ) ( value - TaskCache . InclusiveInt32Min ) < ( TaskCache . ExclusiveInt32Max - TaskCache . InclusiveInt32Min ) )
5247
- {
5248
- Task < int > task = TaskCache . s_int32Tasks [ value - TaskCache . InclusiveInt32Min ] ;
5249
- return Unsafe . As < Task < TResult > > ( task ) ; // Unsafe.As avoids a type check we know will succeed
5250
- }
5248
+ Task < int > task = TaskCache. s_int32Tasks[ value - TaskCache . InclusiveInt32Min ] ;
5249
+ return * ( Task < TResult > * ) & task ;
5251
5250
}
5251
+ }
5252
+ else if ( ! RuntimeHelpers . IsReferenceOrContainsReferences < TResult > ( ) )
5253
+ {
5252
5254
// For other value types, we special-case default(TResult) if we can easily compare bit patterns to default/0.
5253
- else if ( ! RuntimeHelpers . IsReferenceOrContainsReferences < TResult > ( ) )
5255
+ // We don't need to go through the equality operator of the TResult because we cached a task for default(TResult),
5256
+ // so we only need to confirm that this TResult has the same bits as default(TResult).
5257
+ if ( ( sizeof ( TResult ) == sizeof ( byte ) && * ( byte * ) & result == default ( byte ) ) ||
5258
+ ( sizeof ( TResult ) == sizeof ( ushort ) && * ( ushort * ) & result == default ( ushort ) ) ||
5259
+ ( sizeof ( TResult ) == sizeof ( uint ) && * ( uint * ) & result == default ) ||
5260
+ ( sizeof ( TResult ) == sizeof ( ulong ) && * ( ulong * ) & result == default ) )
5254
5261
{
5255
- unsafe
5256
- {
5257
- #pragma warning disable 8500 // sizeof of managed types
5258
- // We don't need to go through the equality operator of the TResult because we cached a task for default(TResult),
5259
- // so we only need to confirm that this TResult has the same bits as default(TResult).
5260
- if ( ( sizeof ( TResult ) == sizeof ( byte ) && Unsafe . As < TResult , byte > ( ref result ) == default ( byte ) ) ||
5261
- ( sizeof ( TResult ) == sizeof ( ushort ) && Unsafe . As < TResult , ushort > ( ref result ) == default ( ushort ) ) ||
5262
- ( sizeof ( TResult ) == sizeof ( uint ) && Unsafe . As < TResult , uint > ( ref result ) == default ) ||
5263
- ( sizeof ( TResult ) == sizeof ( ulong ) && Unsafe . As < TResult , ulong > ( ref result ) == default ) )
5264
- {
5265
- return Task < TResult > . s_defaultResultTask ;
5266
- }
5267
- #pragma warning restore 8500
5268
- }
5262
+ return Task< TResult> . s_defaultResultTask;
5269
5263
}
5270
5264
}
5271
5265
5272
5266
// No cached task is available. Manufacture a new one for this result.
5273
5267
return new Task < TResult > ( result ) ;
5268
+ #pragma warning restore 8500
5274
5269
}
5275
5270
5276
5271
/// <summary>Creates a <see cref="Task{TResult}"/> that's completed exceptionally with the specified exception.</summary>
0 commit comments