This is the sixth part of the Concurrency series. For your convenience you can find other parts in the table of contents in Part 1 – Mutex performance in .NET
Let’s start with the following code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
using System; using System.Threading; namespace AbandonedMutex { class Program { static void Main(string[] args) { var mutex = new Mutex(false, "mutexName"); try { while (true) { try { if (mutex.WaitOne(TimeSpan.FromSeconds(3))) { break; } else { Console.WriteLine("Timeout"); } } catch { } } Console.WriteLine("Mutex acquired!"); Console.ReadLine(); } finally { try { mutex.ReleaseMutex(); Console.WriteLine("Mutex released"); } catch { } } Console.ReadLine(); Console.WriteLine("Done"); } } } |
We want to acquire a mutex. Since the operation might fail, we want to log the timeout, handle the exception and try again. Finally, we want to release the mutex, catch all exceptions and then exit.
Start this application three times: first instance should acquire the mutex, second and third should just wait. Now kill forcibly first instance (i.e., using Process Explorer). Now one of two other instances should acquire the mutex. Let’s say, that it was the second instance. Focus it and hit enter. It should say Mutex released
and wait on another Console.ReadLine()
. We expect the third instance to acquire the mutex, however, it still shows timeouts. Why is that?
Explanation
You could use Chain Traversal API to figure out what is happening. However, everything is as designed. If you consult the documentation, you will see the following exception:
1 2 |
AbandonedMutexException The wait completed because a thread exited without releasing a mutex. This exception is not thrown on Windows 98 or Windows Millennium Edition. |
What is not stated explicitly is: when you get this exception, the mutex IS acquired. This means that our first catch just swallows the exception of an operation which actually succeeded. We loop again, acquire the mutex again (since it is recursive). Later when we release it, it is still locked because we miss one more release.
Solution? Whenever you get this exception you need to break the loop because the mutex is acquired.