Skip to content

Commit 20e012c

Browse files
committed
Refactor: Remove TryEnter/TryEnterAsync() helpers
They were obscuring which cases the reentrancy lock was obtained and released and which cases it was only obtained, and we need to stop releasing the reentrancy lock in preparation for the next commit/fix.
1 parent 0134a47 commit 20e012c

File tree

1 file changed

+60
-76
lines changed

1 file changed

+60
-76
lines changed

AsyncLock/AsyncLock.cs

Lines changed: 60 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,14 @@ internal InnerLock(AsyncLock parent, long oldId, int oldThreadId)
6868

6969
internal async Task<IDisposable> ObtainLockAsync(CancellationToken ct = default)
7070
{
71-
while (!await TryEnterAsync(ct))
71+
while (true)
7272
{
73+
await _parent._reentrancy.WaitAsync(ct);
74+
if (InnerTryEnter(synchronous: false))
75+
{
76+
break;
77+
}
78+
_parent._reentrancy.Release();
7379
// We need to wait for someone to leave the lock before trying again.
7480
await _parent._retry.WaitAsync(ct);
7581
}
@@ -86,7 +92,8 @@ internal async Task<IDisposable> ObtainLockAsync(CancellationToken ct = default)
8692
// In case of zero-timeout, don't even wait for protective lock contention
8793
if (timeout == TimeSpan.Zero)
8894
{
89-
if (await TryEnterAsync(timeout))
95+
_parent._reentrancy.Wait(timeout);
96+
if (InnerTryEnter(synchronous: false))
9097
{
9198
// Reset the owning thread id after all await calls have finished, otherwise we
9299
// could be resumed on a different thread and set an incorrect value.
@@ -95,6 +102,7 @@ internal async Task<IDisposable> ObtainLockAsync(CancellationToken ct = default)
95102
_parent._reentrancy.Release();
96103
return this;
97104
}
105+
_parent._reentrancy.Release();
98106
return null;
99107
}
100108

@@ -105,7 +113,8 @@ internal async Task<IDisposable> ObtainLockAsync(CancellationToken ct = default)
105113
// We need to wait for someone to leave the lock before trying again.
106114
while (remainder > TimeSpan.Zero)
107115
{
108-
if (await TryEnterAsync(remainder))
116+
await _parent._reentrancy.WaitAsync(remainder);
117+
if (InnerTryEnter(synchronous: false))
109118
{
110119
// Reset the owning thread id after all await calls have finished, otherwise we
111120
// could be resumed on a different thread and set an incorrect value.
@@ -114,6 +123,7 @@ internal async Task<IDisposable> ObtainLockAsync(CancellationToken ct = default)
114123
_parent._reentrancy.Release();
115124
return this;
116125
}
126+
_parent._reentrancy.Release();
117127

118128
now = DateTimeOffset.UtcNow;
119129
remainder -= now - last;
@@ -135,9 +145,15 @@ internal async Task<IDisposable> ObtainLockAsync(CancellationToken ct = default)
135145
{
136146
try
137147
{
138-
while (!await TryEnterAsync(cancel))
148+
while (true)
139149
{
150+
await _parent._reentrancy.WaitAsync(cancel);
151+
if (InnerTryEnter(synchronous: false))
152+
{
153+
break;
154+
}
140155
// We need to wait for someone to leave the lock before trying again.
156+
_parent._reentrancy.Release();
141157
await _parent._retry.WaitAsync(cancel);
142158
}
143159
}
@@ -156,8 +172,15 @@ internal async Task<IDisposable> ObtainLockAsync(CancellationToken ct = default)
156172

157173
internal IDisposable ObtainLock(CancellationToken cancellationToken)
158174
{
159-
while (!TryEnter())
175+
while (true)
160176
{
177+
_parent._reentrancy.Wait(cancellationToken);
178+
if (InnerTryEnter(synchronous: true))
179+
{
180+
_parent._reentrancy.Release();
181+
break;
182+
}
183+
_parent._reentrancy.Release();
161184
// We need to wait for someone to leave the lock before trying again.
162185
_parent._retry.Wait(cancellationToken);
163186
}
@@ -169,10 +192,13 @@ internal IDisposable ObtainLock(CancellationToken cancellationToken)
169192
// In case of zero-timeout, don't even wait for protective lock contention
170193
if (timeout == TimeSpan.Zero)
171194
{
172-
if (TryEnter(timeout))
195+
_parent._reentrancy.Wait(timeout);
196+
if (InnerTryEnter(synchronous: true))
173197
{
198+
_parent._reentrancy.Release();
174199
return this;
175200
}
201+
_parent._reentrancy.Release();
176202
return null;
177203
}
178204

@@ -183,10 +209,13 @@ internal IDisposable ObtainLock(CancellationToken cancellationToken)
183209
// We need to wait for someone to leave the lock before trying again.
184210
while (remainder > TimeSpan.Zero)
185211
{
186-
if (TryEnter(remainder))
212+
_parent._reentrancy.Wait(remainder);
213+
if (InnerTryEnter(synchronous: true))
187214
{
215+
_parent._reentrancy.Release();
188216
return this;
189217
}
218+
_parent._reentrancy.Release();
190219

191220
now = DateTimeOffset.UtcNow;
192221
remainder -= now - last;
@@ -204,88 +233,43 @@ internal IDisposable ObtainLock(CancellationToken cancellationToken)
204233
return null;
205234
}
206235

207-
private async Task<bool> TryEnterAsync(CancellationToken cancel = default)
208-
{
209-
await _parent._reentrancy.WaitAsync(cancel);
210-
return InnerTryEnter();
211-
}
212-
213-
private async Task<bool> TryEnterAsync(TimeSpan timeout)
214-
{
215-
if (!await _parent._reentrancy.WaitAsync(timeout))
216-
{
217-
return false;
218-
}
219-
220-
return InnerTryEnter();
221-
}
222-
223-
private bool TryEnter()
224-
{
225-
_parent._reentrancy.Wait();
226-
return InnerTryEnter(true /* synchronous */);
227-
}
228-
229-
private bool TryEnter(TimeSpan timeout)
230-
{
231-
if (!_parent._reentrancy.Wait(timeout))
232-
{
233-
return false;
234-
}
235-
return InnerTryEnter(true /* synchronous */);
236-
}
237-
238236
private bool InnerTryEnter(bool synchronous = false)
239237
{
240238
bool result = false;
241-
try
239+
if (synchronous)
242240
{
243-
if (synchronous)
241+
if (_parent._owningThreadId == UnlockedId)
244242
{
245-
if (_parent._owningThreadId == UnlockedId)
246-
{
247-
_parent._owningThreadId = ThreadId;
248-
}
249-
else if (_parent._owningThreadId != ThreadId)
250-
{
251-
return false;
252-
}
253-
_parent._owningId = AsyncLock.AsyncId;
243+
_parent._owningThreadId = ThreadId;
254244
}
255-
else
245+
else if (_parent._owningThreadId != ThreadId)
256246
{
257-
if (_parent._owningId == UnlockedId)
258-
{
259-
_parent._owningId = AsyncLock.AsyncId;
260-
}
261-
else if (_parent._owningId != _oldId)
262-
{
263-
// Another thread currently owns the lock
264-
return false;
265-
}
266-
else
267-
{
268-
// Nested re-entrance
269-
_parent._owningId = AsyncId;
270-
}
247+
return false;
271248
}
272-
273-
// We can go in
274-
_parent._reentrances += 1;
275-
result = true;
276-
return result;
249+
_parent._owningId = AsyncLock.AsyncId;
277250
}
278-
finally
251+
else
279252
{
280-
// We can't release this in case the lock was obtained because we still need to
281-
// set the owning thread id, but we may have been called asynchronously in which
282-
// case we could be currently running on a different thread than the one the
283-
// locking will ultimately conclude on.
284-
if (!result || synchronous)
253+
if (_parent._owningId == UnlockedId)
285254
{
286-
_parent._reentrancy.Release();
255+
_parent._owningId = AsyncLock.AsyncId;
256+
}
257+
else if (_parent._owningId != _oldId)
258+
{
259+
// Another thread currently owns the lock
260+
return false;
261+
}
262+
else
263+
{
264+
// Nested re-entrance
265+
_parent._owningId = AsyncId;
287266
}
288267
}
268+
269+
// We can go in
270+
_parent._reentrances += 1;
271+
result = true;
272+
return result;
289273
}
290274

291275
public void Dispose()

0 commit comments

Comments
 (0)