diff --git a/src/ModernHttpClient.Android/ModernHttpClient.Android.csproj b/src/ModernHttpClient.Android/ModernHttpClient.Android.csproj index 2baf578..52cbff1 100644 --- a/src/ModernHttpClient.Android/ModernHttpClient.Android.csproj +++ b/src/ModernHttpClient.Android/ModernHttpClient.Android.csproj @@ -65,6 +65,9 @@ ConcatenatingStream.cs + + AsyncLock.cs + diff --git a/src/ModernHttpClient.iOS/ModernHttpClient.iOS.csproj b/src/ModernHttpClient.iOS/ModernHttpClient.iOS.csproj index 02d95c4..0517ab3 100644 --- a/src/ModernHttpClient.iOS/ModernHttpClient.iOS.csproj +++ b/src/ModernHttpClient.iOS/ModernHttpClient.iOS.csproj @@ -17,7 +17,7 @@ full false bin\Debug - DEBUG; + DEBUG; UIKIT prompt 4 false @@ -29,7 +29,6 @@ - full true bin\Release prompt @@ -41,6 +40,7 @@ + UIKIT @@ -59,5 +59,8 @@ ConcatenatingStream.cs + + AsyncLock.cs + diff --git a/src/Shared/AsyncLock.cs b/src/Shared/AsyncLock.cs new file mode 100644 index 0000000..e5fc709 --- /dev/null +++ b/src/Shared/AsyncLock.cs @@ -0,0 +1,35 @@ +using System; +using System.Threading.Tasks; +using System.Threading; + +namespace ModernHttpClient +{ + // Straight-up thieved from http://www.hanselman.com/blog/ComparingTwoTechniquesInNETAsynchronousCoordinationPrimitives.aspx + public sealed class AsyncLock + { + readonly SemaphoreSlim m_semaphore = new SemaphoreSlim(1, 1); + readonly Task m_releaser; + + public AsyncLock() + { + m_releaser = Task.FromResult((IDisposable)new Releaser(this)); + } + + public Task LockAsync() + { + var wait = m_semaphore.WaitAsync(); + return wait.IsCompleted ? + m_releaser : + wait.ContinueWith((_, state) => (IDisposable)state, + m_releaser.Result, CancellationToken.None, + TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); + } + + sealed class Releaser : IDisposable + { + readonly AsyncLock m_toRelease; + internal Releaser(AsyncLock toRelease) { m_toRelease = toRelease; } + public void Dispose() { m_toRelease.m_semaphore.Release(); } + } + } +} \ No newline at end of file diff --git a/src/Shared/ConcatenatingStream.cs b/src/Shared/ConcatenatingStream.cs index 1b75ecc..fa66f74 100644 --- a/src/Shared/ConcatenatingStream.cs +++ b/src/Shared/ConcatenatingStream.cs @@ -12,6 +12,7 @@ namespace ModernHttpClient { readonly CancellationTokenSource cts = new CancellationTokenSource(); readonly Action onDispose; + readonly AsyncLock readLock = new AsyncLock(); long position; bool closeStreams; @@ -83,7 +84,15 @@ namespace ModernHttpClient Stream stream = Current; if (stream == null) break; - int thisCount = await stream.ReadAsync(buffer, offset, count); + + var thisCount = default(int); + #if UIKIT + using (await readLock.LockAsync()) { + thisCount = await stream.ReadAsync(buffer, offset, count); + } + #else + thisCount = await stream.ReadAsync(buffer, offset, count); + #endif result += thisCount; count -= thisCount; @@ -127,7 +136,15 @@ namespace ModernHttpClient Stream stream = Current; if (stream == null) break; - int thisCount = stream.Read(buffer, offset, count); + + var thisCount = default(int); + #if UIKIT + using (readLock.LockAsync().Result) { + thisCount = stream.Read(buffer, offset, count); + } + #else + thisCount = stream.Read(buffer, offset, count); + #endif result += thisCount; count -= thisCount;