Skip to content
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

fix Metric livelock by replacing potential infinate loop in MetricValuesBuffer.GetAndResetValue #2612

Merged
merged 10 commits into from
Jun 17, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ public int NextFlushIndex
set { this.nextFlushIndex = value; }
}

protected abstract TValue DefaultValue { get; }

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int IncWriteIndex()
{
Expand Down Expand Up @@ -79,31 +81,29 @@ public void ResetIndicesAndData()

public TValue GetAndResetValue(int index)
{
TValue value = this.GetAndResetValueOnce(this.values, index);

if (this.IsInvalidValue(value))
for (int spinCountdown = 10000; spinCountdown > 0; spinCountdown--)
TimothyMothra marked this conversation as resolved.
Show resolved Hide resolved
{
#pragma warning disable SA1129 // Do not use default value type constructor
var spinWait = new SpinWait();
#pragma warning restore SA1129 // Do not use default value type constructor
TValue value = this.GetAndResetValueOnce(this.values, index);

value = this.GetAndResetValueOnce(this.values, index);
while (this.IsInvalidValue(value))
if (this.IsInvalidValue(value))
{
spinWait.SpinOnce();

if (spinWait.Count % 100 == 0)
if (spinCountdown % 100 == 0)
{
// In tests (including stress tests) we always finished wating before 100 cycles.
// In tests (including stress tests) we always finished waiting before 100 cycles.
// However, this is a protection against en extreme case on a slow machine.
Task.Delay(10).ConfigureAwait(continueOnCapturedContext: false).GetAwaiter().GetResult();
}

value = this.GetAndResetValueOnce(this.values, index);
continue;
}
else
{
return value;
}
}

return value;
// exceeded maximum spin count
return this.DefaultValue;
}

protected abstract void ResetValues(TValue[] values);
Expand All @@ -127,6 +127,8 @@ public MetricValuesBuffer_Double(int capacity)
{
}

protected override double DefaultValue => double.NaN;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override bool IsInvalidValue(double value)
{
Expand Down Expand Up @@ -162,6 +164,8 @@ public MetricValuesBuffer_Object(int capacity)
{
}

protected override object DefaultValue => null;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override bool IsInvalidValue(object value)
{
Expand Down