5

I have written a sample console application which creates a mutex as shown in below code sample. I am launching this application directly from Visual Studio (VS2013) by pressing Ctrl + F5 (running the application without debugger). For the 1st instance of console application I acquire the mutex and the following line is displayed in console:

New Instance created...

However, when I create a second instance of console application using Ctrl + F5 again, I get the following message in the console:

Instance already acquired...

even though I explicitly release mutex after 500ms with this line of code:

mut.ReleaseMutex();

In the same thread which acquires the mutex, I still see that my second instance of console application waits for the mutex to be released.

Can someone explain to me why is it so or correct me if I am doing something wrong? If I understand, ReleaseMutex should release the mutex from the same thread which acquires it via mut.WaitOne(0) call so that any other thread waiting to acquire mutex should be provided ownership. However, in this case I am not able to see it working.

If I close the 1st instance which acquired mutex (still my second is alive) and try to launch a 3rd instance with Ctrl+F5, I can see that there is an AbandonedMutexException:

Unhandled Exception: System.Threading.AbandonedMutexException: The wait completed due to an abandoned mutex.

PS: Interestingly I can see that this works fine if I pass false in mutex constructor as

static Mutex mut = new Mutex(false, "Global\\test");

What's the significance of initiallyOwned parameter in the

public Mutex(bool initiallyOwned, string name);

constructor version of mutex class?

Console output for 2 instances run from VS

class Program
{
    static Mutex mut = new Mutex(true, "Global\\test");

    static void Main(string[] args)
    {

        if (IsInstance())
        {
            Console.WriteLine("New Instance created...");
        }
        else
        {
            Console.WriteLine("Instance already acquired...");
        }

        Console.ReadLine();
    }
    static bool IsInstance()
    {
        if (!mut.WaitOne(0))
        {
            Console.WriteLine("Thread id {0} Waiting at Mutex...",AppDomain.GetCurrentThreadId());
            return false;
        }
        else
        {
           Console.WriteLine("Thread id {0} got Mutex...", AppDomain.GetCurrentThreadId());
           Thread.Sleep(500);
           mut.ReleaseMutex();
           return true;
        }
    }

}
VMAtm
  • 27,943
  • 17
  • 79
  • 125
Alby
  • 53
  • 1
  • 3
  • 1
    If you pass `true` as `initiallyOwned`, the current thread will immediately try to acquire the mutex after it is created. – Leandro Feb 16 '17 at 13:42
  • @Leandro Taset Yes, I understand this , but why the call `mut.ReleaseMutex();` on the same thread is not releasing the mutex ?? This is what I am trying to understand. It is interesting to see with `false` it works fine, why not with `true`. If it is intended to work so , in which situations should `true` be used and which `false`. – Alby Feb 16 '17 at 13:56
  • What @LeandroTaset said, this pattern is broken as it leads to abandoned mutex. See [this](http://stackoverflow.com/a/4369749/1997232) answer. – Sinatr Feb 16 '17 at 13:56
  • *"it is intended to work so "* - nope, if you run your very first copy, it will occupy the mutex (due to constructor parameter), but will fail on `WaitOne` check = no release = abandoned mutex. `WaitOne` is not "check if this thread has ownership", it's "try to take ownership", so it will return `false` (because *something* already own mutex). Read more about [abandoned mutex](https://msdn.microsoft.com/en-us/library/system.threading.abandonedmutexexception(v=vs.110).aspx). – Sinatr Feb 16 '17 at 14:02
  • @Evk ,thanks for your explanation. I have got my doubt clarified now.. – Alby Feb 16 '17 at 17:06

1 Answers1

8

So to understand a problem you need to know about two things:

  1. How initiallyOwned parameter works
  2. Mutex reentrancy (recursive calls).

If you create Mutex with initiallyOwned = true - it will try to immediately acquire ownership BUT only if such mutex is not already created. So the first instance of your application immediatly acquires ownership of the mutex. It's about the same as doing:

 var mut = new Mutex(false, "Global\\test");
 mut.WaitOne();

If this mutex already exists, it will not try to acquire ownership. To see if mutex was created (and so, it you already own it), you can use this overload:

bool createdNew;
mut = new Mutex(true, "Global\\test", out createdNew);

Now, Mutex allows several calls to WaitOne and ReleaseMutex, from the same thread. If you called WaitOne multiple times - you need to call ReleaseMutex the same amount of times to release it. However, for your first instance, you call WaitOne twice: first because of initiallyOwned parameter (and because mutex does not exists yet and is created), and second time you call it explicitly. But you only call ReleaseMutex once, and as such your mutex is not released and is owned by first instance. When you close this instance - mutex is still not released and so becomes abandoned.

Here is a code example illustrating those points:

static Mutex mut;

static void Main(string[] args)
{
    bool createdNew;
    mut = new Mutex(true, "Global\\test", out createdNew);
    if (createdNew) {
        Console.WriteLine("New instance created with initially owned = true");
    }
    else if (IsInstance())
    {
        Console.WriteLine("New Instance created...");
    }
    else
    {
        Console.WriteLine("Instance already acquired...");
    }
    if (createdNew)
    {
        Thread.Sleep(500);
        mut.ReleaseMutex();
    }
    Console.ReadLine();

}
static bool IsInstance()
{
    if (!mut.WaitOne(0))
    {
        Console.WriteLine("Thread id {0} Waiting at Mutex...", AppDomain.GetCurrentThreadId());
        return false;
    }
    else
    {
        Console.WriteLine("Thread id {0} got Mutex...", AppDomain.GetCurrentThreadId());
        Thread.Sleep(500);
        mut.ReleaseMutex();            
        return true;
    }
}
VMAtm
  • 27,943
  • 17
  • 79
  • 125
Evk
  • 98,527
  • 8
  • 141
  • 191
  • 2
    Thank you @Evk.. Your have clearly explained the solution to my problem.I was aware that the no:of time I call `mut.WaitOne()` those many times I have to call `mut.ReleaseMutex`, but I was never knowing that `var mut = new Mutex(true, "Global\\test");` is equivalent to `var mut = new Mutex(false, "Global\\test"); mut.WaitOne();` . This explanation has made it clear for me as why I was not able to acquire Mutex in second instance of my application. – Alby Feb 16 '17 at 17:04