-
-
Notifications
You must be signed in to change notification settings - Fork 221
fix(logs): race condition in high volume logging scenarios #4428
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 10 commits
a4d2607
17bbed7
089fffc
34c992f
3608899
0c75df7
0b7c248
43d9772
19ba13a
f6265b0
98b0379
186fe1e
2608bf4
21b4f71
9e10df7
b1d5441
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,7 +40,7 @@ internal ScopedCountdownLock() | |
internal bool IsEngaged => _isEngaged == 1; | ||
|
||
/// <summary> | ||
/// No <see cref="CounterScope"/> will be entered when the <see cref="Count"/> has reached <see langword="0"/> while the lock is engaged via an active <see cref="LockScope"/>. | ||
/// No <see cref="CounterScope"/> will be entered when the <see cref="Count"/> has reached <see langword="0"/>, or while the lock is engaged via an active <see cref="LockScope"/>. | ||
/// Check via <see cref="CounterScope.IsEntered"/> whether the underlying <see cref="CountdownEvent"/> has not been set/signaled yet. | ||
/// To signal the underlying <see cref="CountdownEvent"/>, ensure <see cref="CounterScope.Dispose"/> is called. | ||
/// </summary> | ||
|
@@ -49,6 +49,11 @@ internal ScopedCountdownLock() | |
/// </remarks> | ||
internal CounterScope TryEnterCounterScope() | ||
{ | ||
if (IsEngaged) | ||
{ | ||
return new CounterScope(); | ||
} | ||
|
||
if (_event.TryAddCount(1)) | ||
{ | ||
return new CounterScope(this); | ||
|
@@ -59,6 +64,7 @@ internal CounterScope TryEnterCounterScope() | |
|
||
private void ExitCounterScope() | ||
{ | ||
Debug.Assert(_event.CurrentCount >= 1); | ||
_ = _event.Signal(); | ||
} | ||
|
||
|
@@ -75,7 +81,8 @@ internal LockScope TryEnterLockScope() | |
{ | ||
if (Interlocked.CompareExchange(ref _isEngaged, 1, 0) == 0) | ||
{ | ||
_ = _event.Signal(); // decrement the initial count of 1, so that the event can be set with the count reaching 0 when all 'CounterScope's have exited | ||
Debug.Assert(_event.CurrentCount >= 1); | ||
_ = _event.Signal(); // decrement the initial count of 1, so that the event can be set with the count reaching 0 when all entered 'CounterScope' instances have exited | ||
return new LockScope(this); | ||
} | ||
|
||
|
@@ -84,13 +91,13 @@ internal LockScope TryEnterLockScope() | |
|
||
private void ExitLockScope() | ||
{ | ||
if (Interlocked.CompareExchange(ref _isEngaged, 0, 1) == 1) | ||
Debug.Assert(_event.IsSet); | ||
_event.Reset(); // reset the signaled event to the initial count of 1, so that new 'CounterScope' instances can be entered again | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. note: fix fixed by performing the "Reset before CompareExchange" when existing in the reverse order than the "CompareExchange before Signal" when entering |
||
|
||
if (Interlocked.CompareExchange(ref _isEngaged, 0, 1) != 1) | ||
{ | ||
_event.Reset(); // reset the signaled event to the initial count of 1, so that new `CounterScope`s can be entered again | ||
return; | ||
Debug.Fail("The Lock should have not been disengaged without being engaged first."); | ||
} | ||
|
||
Debug.Fail("The Lock should have not been disengaged without being engaged first."); | ||
} | ||
Flash0ver marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/// <inheritdoc /> | ||
|
@@ -139,7 +146,7 @@ internal LockScope(ScopedCountdownLock lockObj) | |
internal void Wait() | ||
{ | ||
var lockObj = _lockObj; | ||
lockObj?._event.Wait(); | ||
lockObj?._event.Wait(Timeout.Infinite, CancellationToken.None); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. note: this is not a behavioral change .. just making the behavior more apparent by "lifting" the method call of the parameterless |
||
} | ||
|
||
public void Dispose() | ||
|
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note: additional change
no longer allowing to
Add
new logs to a particular Buffer, while aFlush
has been requested (in this case occurring during the5
second timeout), which may have previously "dragged out" the Flush by still allowing newAdd
operations before the Event is actually set by reaching a count of0
(while the buffer is not full yet)this also effects #4425, where - in the case of a terminating unhandled exception - no more
Add
operations are admitted and we only wait until all currently in-progressAdd
operations have concluded in order to do a "safe"Flush
, which should complete quite quickly, not "unreasonably" dragging out the shutdown