4
votes

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;
        }
    }

}
1
If you pass true as initiallyOwned, the current thread will immediately try to acquire the mutex after it is created.Leandro
@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
What @LeandroTaset said, this pattern is broken as it leads to abandoned mutex. See this answer.Sinatr
"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.Sinatr
@Evk ,thanks for your explanation. I have got my doubt clarified now..Alby

1 Answers

7
votes

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;
    }
}