AsyncLock + tests. NexusApi swapped to use it

This commit is contained in:
Justin Swanson 2019-12-21 18:17:56 -06:00
parent bef5621441
commit e5aef9a043
5 changed files with 105 additions and 8 deletions

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Disposables;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Wabbajack.Common
{
public class AsyncLock
{
private SemaphoreSlim _lock = new SemaphoreSlim(1, 1);
public async Task<IDisposable> Wait()
{
await _lock.WaitAsync();
return Disposable.Create(() => _lock.Release());
}
}
}

View File

@ -129,6 +129,7 @@
<Compile Include="StatusUpdate.cs" />
<Compile Include="SteamHandler.cs" />
<Compile Include="Utils.cs" />
<Compile Include="Util\AsyncLock.cs" />
<Compile Include="Util\TempFile.cs" />
<Compile Include="Util\TempFolder.cs" />
<Compile Include="WorkQueue.cs" />

View File

@ -62,12 +62,10 @@ namespace Wabbajack.Lib.NexusApi
public async Task<string> Username() => (await UserStatus).name;
private static SemaphoreSlim _getAPIKeyLock = new SemaphoreSlim(1, 1);
private static AsyncLock _getAPIKeyLock = new AsyncLock();
private static async Task<string> GetApiKey()
{
await _getAPIKeyLock.WaitAsync();
try
using (await _getAPIKeyLock.Wait())
{
// Clean up old location
if (File.Exists(API_KEY_CACHE_FILE))
@ -93,10 +91,6 @@ namespace Wabbajack.Lib.NexusApi
result.ToEcryptedJson("nexusapikey");
return result;
}
finally
{
_getAPIKeyLock.Release();
}
}
class RefererHandler : RequestHandler

View File

@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Wabbajack.Common;
namespace Wabbajack.Test
{
[TestClass]
public class AsyncLockTests
{
[TestMethod]
public async Task Typical()
{
var asyncLock = new AsyncLock();
bool firstRun = false;
var first = Task.Run(async () =>
{
using (await asyncLock.Wait())
{
await Task.Delay(500);
firstRun = true;
}
});
var second = Task.Run(async () =>
{
await Task.Delay(200);
using (await asyncLock.Wait())
{
Assert.IsTrue(firstRun);
}
});
await Task.WhenAll(first, second);
}
[TestMethod]
public async Task Exception()
{
var asyncLock = new AsyncLock();
bool firstRun = false;
bool secondRun = false;
// Throw exception inside a lock
await Assert.ThrowsExceptionAsync<Exception>(() =>
{
return Task.Run(async () =>
{
using (await asyncLock.Wait())
{
await Task.Delay(500);
firstRun = true;
throw new Exception();
}
});
});
await Task.WhenAll(
// Try to re-enter lock afterwards
Task.Run(async () =>
{
await Task.Delay(200);
using (await asyncLock.Wait())
{
Assert.IsTrue(firstRun);
secondRun = true;
}
}),
// Add a timeout to fail if we cannot
Task.Run(async () =>
{
await Task.Delay(500);
if (!secondRun)
{
throw new ArgumentException();
}
}));
}
}
}

View File

@ -99,6 +99,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="ACompilerTest.cs" />
<Compile Include="AsyncLockTests.cs" />
<Compile Include="AVortexCompilerTest.cs" />
<Compile Include="CSP\ChannelTests.cs" />
<Compile Include="CSP\CSPTests.cs" />