From f68635483425fcefd05e91a3d0573eb60f50189f Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Tue, 16 Apr 2013 19:51:31 -0700 Subject: [PATCH] Don't use Task.Factory.FromAsync in StreamExtensions - We have seen WriteAsync hang #1874 --- .../Infrastructure/StreamExtensions.cs | 62 +++++++++++++++---- 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/src/Microsoft.AspNet.SignalR.Client/Infrastructure/StreamExtensions.cs b/src/Microsoft.AspNet.SignalR.Client/Infrastructure/StreamExtensions.cs index 7f1a1620cf..a5263c5418 100644 --- a/src/Microsoft.AspNet.SignalR.Client/Infrastructure/StreamExtensions.cs +++ b/src/Microsoft.AspNet.SignalR.Client/Infrastructure/StreamExtensions.cs @@ -15,33 +15,73 @@ public static Task ReadAsync(this Stream stream, byte[] buffer) #if NETFX_CORE || NET45 return stream.ReadAsync(buffer, 0, buffer.Length); #else + return FromAsync(cb => stream.BeginRead(buffer, 0, buffer.Length, cb, null), ar => stream.EndRead(ar)); +#endif + } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "This is a shared class.")] + public static Task WriteAsync(this Stream stream, byte[] buffer) + { +#if NETFX_CORE || NET45 + return stream.WriteAsync(buffer, 0, buffer.Length); +#else + return FromAsync(cb => stream.BeginWrite(buffer, 0, buffer.Length, cb, null), WrapEndWrite(stream)); +#endif + } + +#if !(NETFX_CORE || NET45) + private static Func WrapEndWrite(Stream stream) + { + return ar => + { + stream.EndWrite(ar); + return null; + }; + } + + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exceptions are flowed back to the caller.")] + private static Task FromAsync(Func begin, Func end) + { + var tcs = new TaskCompletionSource(); try { - return Task.Factory.FromAsync((cb, state) => stream.BeginRead(buffer, 0, buffer.Length, cb, state), ar => stream.EndRead(ar), null); + var result = begin(ar => + { + if (!ar.CompletedSynchronously) + { + CompleteAsync(tcs, ar, end); + } + }); + + if (result.CompletedSynchronously) + { + CompleteAsync(tcs, result, end); + } } catch (Exception ex) { - return TaskAsyncHelper.FromError(ex); + tcs.TrySetException(ex); } -#endif + + return tcs.Task; } - [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "This is a shared class.")] [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exceptions are flowed back to the caller.")] - public static Task WriteAsync(this Stream stream, byte[] buffer) + private static void CompleteAsync(TaskCompletionSource tcs, IAsyncResult ar, Func end) { -#if NETFX_CORE || NET45 - return stream.WriteAsync(buffer, 0, buffer.Length); -#else try { - return Task.Factory.FromAsync((cb, state) => stream.BeginWrite(buffer, 0, buffer.Length, cb, state), ar => stream.EndWrite(ar), null); + tcs.TrySetResult(end(ar)); + } + catch (OperationCanceledException) + { + tcs.TrySetCanceled(); } catch (Exception ex) { - return TaskAsyncHelper.FromError(ex); + tcs.TrySetException(ex); } -#endif } +#endif } }