2

Or a different title:

Why disposing an acquired Mutex is destroying it?

I have the following code, the real one spawns between several methods and does things where this one sleeps:

        bool createdNew;
        using (Mutex mutex = new Mutex(true, "Global\\AAA", out createdNew))
        {
            if (!createdNew)
            {
                throw new InvalidOperationException();
            }
        }

        Thread.Sleep(15000);

        using (Mutex mutex = new Mutex(false, "Global\\AAA", out createdNew))
        {
            if (!createdNew)
            {
                mutex.ReleaseMutex();
            }
            else
            {
                throw new InvalidOperationException();
            }
        }

I was expecting the first time to get a createdNew = true and the second time a false but I get a true both times.

It is related with disposing, If I don't dispose the Mutex then everything works as expected but as I found in several places like here disposing a Mutex does not release it (so I guess it does not destroy it as well as the Mutex is acquired by the current thread).

So again, why disposing is destroying an acquired Mutex?

Ignacio Soler Garcia
  • 21,122
  • 31
  • 128
  • 207

2 Answers2

2

Because mutex is destoyed when last handle to it is closed (reference):

Use the CloseHandle function to close the handle. The system closes the handle automatically when the process terminates. The mutex object is destroyed when its last handle has been closed.

That's what happens in your example - you create mutex and then (with using) close the only handle to it, so it is destroyed.

Note that you should not create mutex like this and close handle to it without releasing mutex anyway:

Mutex mutex = new Mutex(true, "Global\\AAA", out createdNew)

This says - if mutex does not already exists, create it and give this thread ownership of it. So if createdNew is true after this call - you own this mutex and so should release it. By closing handle you either just destoy mutex (but that is useless scenario), or abandon mutex, so that other threads\processes waiting on that mutex with throw AbandonedMutexException. So it should be:

using (Mutex mutex = new Mutex(true, "Global\\AAA", out createdNew)) {
    if (!createdNew) {
        throw new InvalidOperationException();
    }
    // do something useful
    mutex.ReleaseMutex();
}

Second using is also not correct:

Mutex mutex = new Mutex(false, "Global\\AAA", out createdNew)

By passing false as first parameter you say: whether this mutex already exists or not - do not give this thread ownership of it. Then you do:

if (!createdNew)
{
    mutex.ReleaseMutex();
}

But you cannot own mutex anyway at this point, so even if control flow entered this if block - it would not work anyway (throw exception on ReleaseMutex). Instead you should explicitly call WaitOne and ignore result of createdNew (is not relevant in this case):

using (Mutex mutex = new Mutex(false, "Global\\AAA")) {
    if (mutex.WaitOne()) {
        // do something useful
        mutex.ReleaseMutex();
    }
}
Evk
  • 98,527
  • 8
  • 141
  • 191
  • Though as an attempt to narrow something down, it could be that the code does nothing useful because it's narrowed things down too much to reflect the real use. In particular the code here does nothing useful because it's only a single thread of a single process so mutexes don't offer anything as there's no potential for another to own it. – Jon Hanna Nov 30 '17 at 10:41
  • @Evk: the locking mechanism is intended to work with different processes and I was trying to avoid having a Disposable field in the class that uses the Mutex 'cos I thought that the OS would retain a Mutex that was acquired (which I see it is not the case). – Ignacio Soler Garcia Nov 30 '17 at 11:33
  • @IgnacioSolerGarcia hopefully provided information helps you to fix your real issue? – Evk Nov 30 '17 at 11:33
2

That is because the Dispose method closes the WaitHandle of the Mutex (as you can see in the answer of the question that you linked)

You are doing something like this

bool createdNew;
Mutex mutex = new Mutex(true, "Global\\AAA", out createdNew);

if (!createdNew)
{
    throw new InvalidOperationException();
}

mutex.SafeWaitHandle.Close();

thus destroying the only handle to that mutex.

Removing the using bit from your code and calling

mutex.ReleaseMutex();

should allow you to get another handle of that mutex later on.

Edit: And as @Evk pointed out, when you close the last handle to the mutex, it's automatically destroyed.