blob: 8149938b4d37e7e7f9f642742afd146252c80136 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
|
using System;
using System.Threading.Tasks;
using Jellyfin.Server.Implementations.Users;
using Xunit;
namespace Jellyfin.Server.Implementations.Tests.Users
{
public class UserManagerLockHelperTests
{
[Fact]
public async Task LockAsync_WhenNested_DoesNotAcquireSecondLockAndRestoresStateOnDispose()
{
UserManager.LockHelper.IsNestedLock.Value = 0;
using var helper = new UserManager.LockHelper();
var key = Guid.NewGuid();
Assert.True(helper.ShouldLock());
var outerHandle = await helper.LockAsync(key);
Assert.False(helper.ShouldLock());
var innerHandle = await helper.LockAsync(key);
Assert.False(helper.ShouldLock());
innerHandle.Dispose();
Assert.False(helper.ShouldLock());
outerHandle.Dispose();
Assert.True(helper.ShouldLock());
}
[Fact]
public async Task LockAsync_WithSameKey_BlocksSecondLockUntilFirstIsReleased()
{
UserManager.LockHelper.IsNestedLock.Value = 0;
using var helper = new UserManager.LockHelper();
var key = Guid.NewGuid();
var firstAcquired = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
var releaseFirst = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
var secondEntered = false;
var firstTask = Task.Run(
async () =>
{
using var firstHandle = await helper.LockAsync(key);
firstAcquired.SetResult(true);
await releaseFirst.Task;
},
TestContext.Current.CancellationToken);
await firstAcquired.Task;
var secondTask = Task.Run(
async () =>
{
using var secondHandle = await helper.LockAsync(key);
secondEntered = true;
},
TestContext.Current.CancellationToken);
await Task.Delay(100, TestContext.Current.CancellationToken);
Assert.False(secondEntered);
releaseFirst.SetResult(true);
await Task.WhenAll(firstTask, secondTask);
Assert.True(secondEntered);
}
[Fact]
public async Task LockAsync_WhenDisposed_ThrowsObjectDisposedException()
{
UserManager.LockHelper.IsNestedLock.Value = 0;
using var helper = new UserManager.LockHelper();
helper.Dispose();
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await helper.LockAsync(Guid.NewGuid()));
}
[Fact]
public void Dispose_WhenCalledMultipleTimes_DoesNotThrow()
{
UserManager.LockHelper.IsNestedLock.Value = 0;
using var helper = new UserManager.LockHelper();
helper.Dispose();
var ex = Record.Exception(() => helper.Dispose());
Assert.Null(ex);
}
}
}
|