private void AccrueQuota(long currentTimeFullSeconds)
{
var spin = new SpinWait();
while (true)
{
long lastQuotaAccrualFullSecondsLocal = Interlocked.Read(ref this.lastQuotaAccrualFullSeconds);
long fullSecondsSinceLastQuotaAccrual = currentTimeFullSeconds - lastQuotaAccrualFullSecondsLocal;
// fullSecondsSinceLastQuotaAccrual <= 0 means we're in a second for which some thread has already updated this.lastQuotaAccrualFullSeconds
if (fullSecondsSinceLastQuotaAccrual > 0)
{
// we are in a new second (possibly along with a bunch of competing threads, some of which might actually be in different (also new) seconds)
// only one thread will succeed in updating this.lastQuotaAccrualFullSeconds
long newValue = lastQuotaAccrualFullSecondsLocal + fullSecondsSinceLastQuotaAccrual;
long valueBeforeExchange = Interlocked.CompareExchange(
ref this.lastQuotaAccrualFullSeconds,
newValue,
lastQuotaAccrualFullSecondsLocal);
if (valueBeforeExchange == lastQuotaAccrualFullSecondsLocal)
{
// we have updated this.lastQuotaAccrualFullSeconds, now increase the quota value
this.IncreaseQuota(fullSecondsSinceLastQuotaAccrual);
break;
}
else if (valueBeforeExchange >= newValue)
{
// a thread that was in a later (or same) second has beaten us to updating the value
// we don't have to do anything since the time that has passed between the previous
// update and this thread's current time has already been accounted for by that other thread
break;
}
else
{
// a thread that was in an earlier second (but still a later one compared to the previous update) has beaten us to updating the value
// we have to repeat the attempt to account for the time that has passed since
}
}
else
{
// we're within a second that has already been accounted for, do nothing
break;
}
spin.SpinOnce();
}
}