Azure-Samples/azure-cache-redis-samples

Incorrect check of lock

Closed this issue · 0 comments

This issue is for a: (mark with an x)

- [x] bug report -> please search issues before submitting
- [ ] feature request
- [ ] documentation issue or request
- [ ] regression (a behavior that used to work and stopped in a new release)

The current implementation of RedisConnection contains this code

try
{
    await _reconnectSemaphore.WaitAsync(RestartConnectionTimeout);
}
catch
{
    // If we fail to enter the semaphore, then it is possible that another thread has already done so.
    // ForceReconnectAsync() can be retried while connectivity problems persist.
    return;
}

Which is an incorrect way to check whether the lock was taken.

The doc of the used overload of WaitAsync says

Returns:
A task that will complete with a result of true if the current thread successfully
entered the System.Threading.SemaphoreSlim, otherwise with a result of false.

So the correct code is

bool lockTaken = await _reconnectSemaphore.WaitAsync(RestartConnectionTimeout);
if (!lockTaken)
{
    // If we fail to enter the semaphore, then it is possible that another thread has already done so.
    // ForceReconnect() can be retried while connectivity problems persist.
    return;
}

With the current implementation a thread that fails to take the lock because of a timeout will proceed with the reconnection, which is not what is intended.

Also, the "double check lock" pattern is implemented incorrectly.
The second check currently looks like this:

try
{
    var utcNow = DateTimeOffset.UtcNow;
    elapsedSinceLastReconnect = utcNow - previousReconnectTime;
// ...
    if (elapsedSinceLastReconnect < ReconnectMinInterval)
    {
        return; // Some other thread made it through the check and the lock, so nothing to do.
    }
//...

The point of the second check is to avoid doing work that has just been done by the thread
that has exited the lock. This goal is not achieved here, because the check is
based on a stale value of _lastReconnectTicks that was read before entering the lock.