1

I have a method initializing an in-memory cache of some objects that is called before entering said cache. To the best of my knowledge I have used a SemaphoreSlim correctly but still several times the semaphore has locked up and no threads have been able to access the block. Is there any way that the lock release in a finally block could be ignored? We are using .NET 4.6.2, ASP.NET MVC and hosting as an Azure application.

private static readonly SemaphoreSlim _lock = new SemaphoreSlim(1);

private static async Task<bool> InitializeCache()
{
    if (Cache != null) return true;

    if (await _lock.WaitAsync(30000))
    {
        try
        {
            if (Cache != null) return true;

            var items = await API.GetKeysFromAPI();
            Cache = new ConcurrentDictionary<string, SamlSessionState>(items);

            return true;
        }
        catch (Exception e)
        {
            throw new Exception("Error initializing the cache", e);
        }
        finally
        {
            _lock.Release();
        }
    }
    else
    {
        // This keeps getting called after the semaphore locks up.
        throw new Exception("Error initializing the cache, the semaphore timed out."); 
    }
}

I would expect that the finally block would be called every time. Could the request being terminated stop the execution so that finally block is never called?

Otto T.
  • 83
  • 1
  • 6
  • 2
    A runtime bug is never the first thing to guess at. It takes very little to cause this code to deadlock, an unresponsive server/api is enough. If you can't see that with a debugger then use a minidump. And if this is a consistent problem with your service provider then you probably need a timeout. – Hans Passant Mar 27 '19 at 15:42
  • 2
    Make sure there's no possibility of re-entrancy - that `API.GetKeysFromAPI` can't possibly cause `InitializeCache` to be executed again. – canton7 Mar 27 '19 at 15:43
  • The functionality here implies singleton semantic so I'm wondering how did you detect that other threads cannot access the block because it should be relatively rare scenario in this case ? – Dmytro Mukalov Mar 27 '19 at 16:35
  • @HansPassant Could you please elaborate? The API request has a timeout of 30 seconds after which it throws an exception and ends execution. – Otto T. Mar 28 '19 at 16:06
  • @canton7 That is a very good idea, unfortunately I checked and that was not the case :/ – Otto T. Mar 28 '19 at 16:06
  • @DmytroMukalov All subsequent requests to the endpoint that initializes the cache cause "... the semaphore timed out." exception which indicates that the semaphore times out. – Otto T. Mar 28 '19 at 16:13
  • "All subsequent requests": which may mean that `Cache` never gets initialized which in turn may mean that the initial request gets stuck either inside `API.GetKeysFromAPI` or on the continuation scheduling because of `.Result` deadlock somewhere. Try to use `ConfigureAwait(false)` to verify this hypothesis. – Dmytro Mukalov Mar 28 '19 at 16:35

2 Answers2

0

As stated in the "long answer" comment on this question, yes in some extreme cases it is possible your finally block will not execute. The same question also has some more answers that will be worth checking.

AsheraH
  • 474
  • 10
  • 15
  • Yes Microsoft also states this in their [documentation](https://learn.microsoft.com/de-de/dotnet/csharp/language-reference/keywords/try-finally): "Within a handled exception, the associated finally block is guaranteed to be run. However, if the exception is unhandled, execution of the finally block is dependent on how the exception unwind operation is triggered. That, in turn, is dependent on how your computer is set up." – Simon Mar 27 '19 at 15:46
0

As stated in the answers above and also the Microsoft Documentation it is possible to skip the finally block in some extreme cases. However I really doubt that this is the problem in your case. It seems to be a simple deadlock caused somewhere in your code. Use a dump of your application to further analyse it or try to find the deadlock with some logging statements!

Simon
  • 1,244
  • 8
  • 21