Ensure no concurrent access to NSMutableData

I'm pretty sure that this is a bug in Xamarin.iOS because we never write to
the NSMutableData, but just for funsies, synchronize access to the underlying
stream. Fixes #22, maybe.
This commit is contained in:
Paul Betts 2013-12-05 09:55:43 +00:00
Родитель c201b01a38
Коммит 7e06c96130
4 изменённых файлов: 62 добавлений и 4 удалений

Просмотреть файл

@ -65,6 +65,9 @@
<Compile Include="..\Shared\ConcatenatingStream.cs">
<Link>ConcatenatingStream.cs</Link>
</Compile>
<Compile Include="..\Shared\AsyncLock.cs">
<Link>AsyncLock.cs</Link>
</Compile>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Novell\Novell.MonoDroid.CSharp.targets" />
</Project>

Просмотреть файл

@ -17,7 +17,7 @@
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<DefineConstants>DEBUG; UIKIT</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause>
@ -29,7 +29,6 @@
</CustomCommands>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>full</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
@ -41,6 +40,7 @@
<Command type="BeforeBuild" command="make AFNetworking.dll" workingdir="${SolutionDir}" />
</CustomCommands>
</CustomCommands>
<DefineConstants>UIKIT</DefineConstants>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.MonoTouch.CSharp.targets" />
<ItemGroup>
@ -59,5 +59,8 @@
<Compile Include="..\Shared\ConcatenatingStream.cs">
<Link>ConcatenatingStream.cs</Link>
</Compile>
<Compile Include="..\Shared\AsyncLock.cs">
<Link>AsyncLock.cs</Link>
</Compile>
</ItemGroup>
</Project>

35
src/Shared/AsyncLock.cs Normal file
Просмотреть файл

@ -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<IDisposable> m_releaser;
public AsyncLock()
{
m_releaser = Task.FromResult((IDisposable)new Releaser(this));
}
public Task<IDisposable> 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(); }
}
}
}

Просмотреть файл

@ -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;