-
Notifications
You must be signed in to change notification settings - Fork 337
Expand file tree
/
Copy pathMemoryLock.cs
More file actions
136 lines (104 loc) · 3.75 KB
/
MemoryLock.cs
File metadata and controls
136 lines (104 loc) · 3.75 KB
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
using AsyncKeyedLock;
using System;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
namespace EasyCaching.Core.DistributedLock
{
public class MemoryLock : IDistributedLock
{
private static readonly AsyncKeyedLocker<string> _locker = new AsyncKeyedLocker<string>(o =>
{
o.PoolSize = 20;
o.PoolInitialFill = 1;
});
public string Key { get; }
private readonly Lock _syncObj = LockFactory.Create();
public MemoryLock(string key) => Key = key;
private AsyncKeyedLockReleaser<string> _releaser;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private AsyncKeyedLockReleaser<string> GetOrCreate()
{
if (Volatile.Read(ref _releaser) != null) throw new DistributedLockException();
lock (_syncObj)
{
if (Volatile.Read(ref _releaser) != null) throw new DistributedLockException();
var releaser = _locker.GetOrAdd(Key);
Volatile.Write(ref _releaser, releaser);
return releaser;
}
}
#region Dispose
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual async ValueTask DisposeAsync()
{
await ReleaseAsync();
GC.SuppressFinalize(this);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected virtual void Dispose(bool disposing) => Release();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
~MemoryLock() => Dispose(false);
#endregion Dispose
#region Release
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void InternalRelease()
{
var semaphore = Interlocked.Exchange(ref _releaser, null);
if (semaphore == null) return;
semaphore.Dispose();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual void Release() => InternalRelease();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual ValueTask ReleaseAsync()
{
InternalRelease();
return default;
}
#endregion
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void LockFail()
{
var semaphore = Interlocked.Exchange(ref _releaser, null);
if (semaphore == null) return;
new AsyncKeyedLockTimeoutReleaser<string>(false, semaphore).Dispose();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual bool Lock(int millisecondsTimeout, CancellationToken cancellationToken)
{
var semaphore = GetOrCreate();
bool locked = false;
try
{
locked = semaphore.SemaphoreSlim.Wait(millisecondsTimeout, cancellationToken);
}
finally
{
if (!locked) LockFail();
}
return locked;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual async ValueTask<bool> LockAsync(int millisecondsTimeout, CancellationToken cancellationToken)
{
var semaphore = GetOrCreate();
bool locked = false;
try
{
locked = await semaphore.SemaphoreSlim.WaitAsync(millisecondsTimeout, cancellationToken).ConfigureAwait(false);
}
finally
{
if (!locked) LockFail();
}
return locked;
}
}
}