VS 2005 C#: AbandonedMutexException

In VS 2005 C#, the program shown in the code window below was copied from MSDN. I got AbandonedMutexException messages (see below). After some checking, I saw that it had to do with mutex ownership not being released correctly, a new feature to .NET 2.0. I added the mutex release lines tagged with "// added". Now the code runs correctly.

However I'm puzzled about t3Start(). Note that it uses WaitAny on an array of mutexes. As it happens in this example, mutex gM1 is the one that's released; but it could have been gM2.

I added gM1.Release(); but if I also include gM2.Release(), I get an error (Object sync method was called from an unsynced block of code). So, what's the correct way to release in this case?

==================

System.Threading.AbandonedMutexException was unhandled
  Message="The wait completed due to an abandoned mutex."
  Source="mscorlib"
  MutexIndex=-1
  StackTrace:
       at System.Threading.WaitHandle.WaitOne(Int64 timeout, Boolean exitContext)
       at System.Threading.WaitHandle.WaitOne(Int32 millisecondsTimeout, Boolean exitContext)
       at System.Threading.WaitHandle.WaitOne()
       at Mutex_Example.MutexSample.t2Start() in C:\Users\leal\Documents\Visual Studio 2005\Projects\Mutex Example\Mutex Example\Program.cs:line 90
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;


namespace Mutex_Example
{
    public class MutexSample
    {

        static Mutex gM1;
        static Mutex gM2;
        const int ITERS = 100;
        static AutoResetEvent Event1 = new AutoResetEvent(false);
        static AutoResetEvent Event2 = new AutoResetEvent(false);
        static AutoResetEvent Event3 = new AutoResetEvent(false);
        static AutoResetEvent Event4 = new AutoResetEvent(false);

        public static void Main(String[] args)
        {

            Console.WriteLine("MutexSample.cs ...");
            gM1 = new Mutex(true, "MyMutex");
            // Create Mutext initialOwned, with name of "MyMutex".
            gM2 = new Mutex(true);
            // Create Mutext initialOwned, with no name.
            Console.WriteLine(" - Main Owns gM1 and gM2");

            AutoResetEvent[] evs = new AutoResetEvent[4];
            evs[0] = Event1;
            // Event for t1.
            evs[1] = Event2;
            // Event for t2.
            evs[2] = Event3;
            // Event for t3.
            evs[3] = Event4;
            // Event for t4.

            MutexSample tm = new MutexSample();
            Thread t1 = new Thread(new ThreadStart(tm.t1Start));
            Thread t2 = new Thread(new ThreadStart(tm.t2Start));
            Thread t3 = new Thread(new ThreadStart(tm.t3Start));
            Thread t4 = new Thread(new ThreadStart(tm.t4Start));
            t1.Start();
            // Calls Mutex.WaitAll(Mutex[] of gM1 and gM2).
            t2.Start();
            // Calls Mutex.WaitOne(Mutex gM1).
            t3.Start();
            // Calls Mutex.WaitAny(Mutex[] of gM1 and gM2).
            t4.Start();
            // Calls Mutex.WaitOne(Mutex gM2).

            Thread.Sleep(2000);
            Console.WriteLine(" - Main releases gM1");
            gM1.ReleaseMutex();
            // t2 and t3 will end and signal.

            Thread.Sleep(1000);
            Console.WriteLine(" - Main releases gM2");
            gM2.ReleaseMutex();
            // t1 and t4 will end and signal.

            WaitHandle.WaitAll(evs);
            // Waiting until all four threads signal that they are done.
            Console.WriteLine("... MutexSample.cs");
            Console.ReadLine();
        }

        public void t1Start()
        {
            Console.WriteLine("t1Start started,  Mutex.WaitAll(Mutex[])");
            Mutex[] gMs = new Mutex[2];
            gMs[0] = gM1;
            // Create and load an array of Mutex objects for WaitAll call.
            gMs[1] = gM2;
            Mutex.WaitAll(gMs);
            // Waits until both Mutex objects are released.
            Thread.Sleep(2000);
            Console.WriteLine("t1Start finished, Mutex.WaitAll(Mutex[])");
            gM1.ReleaseMutex(); // added
            gM2.ReleaseMutex(); // added
            Event1.Set();
            // AutoResetEvent.Set( ) flagging method is done.
        }

        public void t2Start()
        {
            Console.WriteLine("t2Start started,  gM1.WaitOne( )");
            gM1.WaitOne();
            // Waits until Mutex gM1 is released.
            Console.WriteLine("t2Start finished, gM1.WaitOne( )");
            gM1.ReleaseMutex();
            Event2.Set();
            // AutoResetEvent.Set( ) flagging method is done.
        }

        public void t3Start()
        {
            Console.WriteLine("t3Start started,  Mutex.WaitAny(Mutex[])");
            Mutex[] gMs = new Mutex[2];
            gMs[0] = gM1;
            // Create and load an array of Mutex objects for WaitAny call.
            gMs[1] = gM2;
            Mutex.WaitAny(gMs);
            // Waits until either Mutex object is released.
            Console.WriteLine("t3Start finished, Mutex.WaitAny(Mutex[])");
            gM1.ReleaseMutex(); // added
            //gM2.ReleaseMutex(); // added
            Event3.Set();
            // AutoResetEvent.Set( ) flagging method is done.
        }

        public void t4Start()
        {
            Console.WriteLine("t4Start started,  gM2.WaitOne( )");
            gM2.WaitOne();
            // Waits until Mutex gM2 is released.
            Console.WriteLine("t4Start finished, gM2.WaitOne( )");
            gM2.ReleaseMutex(); // added
            Event4.Set();
            // AutoResetEvent.Set( ) flagging method is done.
        }
    }
}

Open in new window

LVL 1
BlearyEyeAsked:
Who is Participating?
 
kaylanreilorConnect With a Mentor Commented:
I think that in this case t3 got the ownership of gM1 when having been awaken by gM1 released by the main thread. Nevertheless, I don't think that t3 got the ownership of gM2 because it has been awaken by gM1. So when you try to release gM2, you've got probably this exception : {"Object synchronization method was called from an unsynchronized block of code."}.
Actually I think that MSDN code is not fair. In the doc of the AbandonedMutexException class it is written : "The call to the WaitAny method is interrupted by one of the abandoned mutexes. The other abandoned mutex could still cause an AbandonedMutexException to be thrown by subsequent wait methods." : http://msdn.microsoft.com/en-us/library/system.threading.abandonedmutexexception.aspx.
Now, what would be a good coding ? Well, it depends on your needs ! In my opinion, if you really need such a coding, you should catch the exception and accept that it could happen.
0
 
kaylanreilorCommented:
So personally, I would rather code something like the following
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;


namespace Mutex_Example
{
    public class MutexSample
    {

        static Mutex gM1;
        static Mutex gM2;
        const int ITERS = 100;
        static AutoResetEvent Event1 = new AutoResetEvent(false);
        static AutoResetEvent Event2 = new AutoResetEvent(false);
        static AutoResetEvent Event3 = new AutoResetEvent(false);
        static AutoResetEvent Event4 = new AutoResetEvent(false);

        public static void Main(String[] args)
        {

            Console.WriteLine("MutexSample.cs ...");
            gM1 = new Mutex(true, "MyMutex");
            // Create Mutext initialOwned, with name of "MyMutex". 
            gM2 = new Mutex(true);
            // Create Mutext initialOwned, with no name. 
            Console.WriteLine(" - Main Owns gM1 and gM2");

            AutoResetEvent[] evs = new AutoResetEvent[4];
            evs[0] = Event1;
            // Event for t1. 
            evs[1] = Event2;
            // Event for t2. 
            evs[2] = Event3;
            // Event for t3. 
            evs[3] = Event4;
            // Event for t4. 

            MutexSample tm = new MutexSample();
            Thread t1 = new Thread(new ThreadStart(tm.t1Start));
            Thread t2 = new Thread(new ThreadStart(tm.t2Start));
            Thread t3 = new Thread(new ThreadStart(tm.t3Start));
            Thread t4 = new Thread(new ThreadStart(tm.t4Start));
            t1.Start();
            // Calls Mutex.WaitAll(Mutex[] of gM1 and gM2). 
            t2.Start();
            // Calls Mutex.WaitOne(Mutex gM1). 
            t3.Start();
            // Calls Mutex.WaitAny(Mutex[] of gM1 and gM2). 
            t4.Start();
            // Calls Mutex.WaitOne(Mutex gM2). 

            Thread.Sleep(2000);
            Console.WriteLine(" - Main releases gM1");
            gM1.ReleaseMutex();
            // t2 and t3 will end and signal. 

            Thread.Sleep(1000);
            Console.WriteLine(" - Main releases gM2");
            gM2.ReleaseMutex();
            // t1 and t4 will end and signal. 

            WaitHandle.WaitAll(evs);
            // Waiting until all four threads signal that they are done. 
            Console.WriteLine("... MutexSample.cs");
            Console.ReadLine();
        }

        public void t1Start()
        {
            Console.WriteLine("t1Start started,  Mutex.WaitAll(Mutex[])");
            Mutex[] gMs = new Mutex[2];
            gMs[0] = gM1;
            // Create and load an array of Mutex objects for WaitAll call. 
            gMs[1] = gM2;
            try
            {
                Mutex.WaitAll(gMs);
            }
            catch (AbandonedMutexException ex)
            {
            }
            finally
            {
                // Waits until both Mutex objects are released. 
                Thread.Sleep(2000);
                Console.WriteLine("t1Start finished, Mutex.WaitAll(Mutex[])");
                Event1.Set();
                // AutoResetEvent.Set( ) flagging method is done. 
            }
        }

        public void t2Start()
        {
            Console.WriteLine("t2Start started,  gM1.WaitOne( )");
            try
            {
                gM1.WaitOne();
            }
            catch (AbandonedMutexException ex)
            {
            }
            finally
            {
                // Waits until Mutex gM1 is released. 
                Console.WriteLine("t2Start finished, gM1.WaitOne( )");
                Event2.Set();
                // AutoResetEvent.Set( ) flagging method is done. 
            }
        }

        public void t3Start()
        {
            Console.WriteLine("t3Start started,  Mutex.WaitAny(Mutex[])");
            Mutex[] gMs = new Mutex[2];
            gMs[0] = gM1;
            // Create and load an array of Mutex objects for WaitAny call. 
            gMs[1] = gM2;
            Mutex.WaitAny(gMs);
            // Waits until either Mutex object is released. 
            Console.WriteLine("t3Start finished, Mutex.WaitAny(Mutex[])");
            Event3.Set();
            // AutoResetEvent.Set( ) flagging method is done. 
        }

        public void t4Start()
        {
            Console.WriteLine("t4Start started,  gM2.WaitOne( )");
            gM2.WaitOne();
            // Waits until Mutex gM2 is released. 
            Console.WriteLine("t4Start finished, gM2.WaitOne( )");
            Event4.Set();
            // AutoResetEvent.Set( ) flagging method is done. 
        }
    }
}

Open in new window

0
 
BlearyEyeAuthor Commented:
Hmmm ... according to MS, the abandoned mutex error represents a serious coding error and in a catch block there should be code to try to repair whatever damage was done by lack of mutual exclusion.

The notion of ownership is important, since only an owner should be able to access critical section resources. In this example, t3 via the WaitAny takes ownership of gM1 but not gM2. So it should be able to access critical resources associated with gM1 but not gM2. In this case, it should know somehow that it owns gM1 but not gM2.

Of course, it could be that acquiring either gM1 or gM2 means that some critical section is now available. But either way, it should release the correct mutex and not try to release the other one.

There does not appear to be any easy way to check ownership of a mutex. So in this code I think I would put the try...catch not around the wait but around the release, to catch the "Object synchronization method was called from an unsynchronized block of code" error.
0
Never miss a deadline with monday.com

The revolutionary project management tool is here!   Plan visually with a single glance and make sure your projects get done.

 
kaylanreilorConnect With a Mentor Commented:
"there should be code to try to repair whatever damage" => I don't think so. Actually the exception is thrown to notify that your code has been denied the action it tried to do. Moreover, MS says also as I gave you the link : "The call to the WaitAny method is interrupted by one of the abandoned mutexes. The other abandoned mutex could still cause an AbandonedMutexException to be thrown by subsequent wait methods."

"t3 via the WaitAny takes ownership of gM1 but not gM2" => yes I agree, I told you the same.
"So it should be able to access critical resources associated with gM1 but not gM2" => actually your code doesn't provide you with an elegant mechanism to do so. Your code, or MS code, is just trying to show the differents between WiatOne, WaitAny and WaitAll.
If you want to sync access to a shared ressource maybe we can find some better suited code. If you really want to use Mutex class, may you have a look to this doc : http://msdn.microsoft.com/en-us/library/system.threading.mutex.getaccesscontrol.aspx.
BTW, trying to access a ressource an catch to understand that it is not currently possible is a common behavior in .Net I think.
0
 
BlearyEyeAuthor Commented:
Well, the doc on the AbandonedMutexClass says: "An abandoned mutex indicates a serious programming error. When a thread exits without releasing the mutex, the data structures protected by the mutex might not be in a consistent state. Prior to version 2.0 of the .NET Framework, such problems were hard to discover because no exception was thrown if a wait completed as the result of an abandoned mutex. For more information, see the Mutex  class.

The next thread to request ownership of the mutex can handle this exception and proceed, provided that the integrity of the data structures can be verified."

So if there's an abandoned mutex, the data structures involved should be verified (and repaired if necessary/possible).

I scanned the GetAccessControl topic; it looks like a way to deny system-wide mutex access to a particular user but grant it to an instance that the user is running. This of course can be useful in terms of giving a program access to resources but forbidding the user from doing so. However, it does not address the question of how to fix the MSDN example.

So I guess at this point we consider the example to be broken and move on ... at least it was useful in terms of illustrating abandoned mutexes.
0
 
kaylanreilorConnect With a Mentor Commented:
But in your example there is no data structure protected by the mutex so nothing to do in the catch of the code I sent you back. Moreover, in your example all theards are beginning by waiting on the release. In this example actually the mutex is abandonned because the threads end their job without releasing, but how to do you want to fix it since their is nothing else in the code than demonstrating the differences between WiatOne, WaitAny and WaitAll.
Now if you really want to "fix" the code, so probably should try to release and catch in t3 (and/or everywhere).
0
 
BlearyEyeAuthor Commented:
Of course there's nothing to repair because it's just a demo example. But we can assume that each of the threads actually does something.

However, my basic question is: how to fix the demo so it's right (including no abandoned mutexes)? I don't want to catch and ignore any exceptions. Rather, I want to hand off ownership of the mutexes in an orderly fashion by releasing them appropriately.
0
 
BlearyEyeAuthor Commented:
kaylanreilor made some good observations, though he didn't answer my last question. so points assigned for good try ...
0
All Courses

From novice to tech pro — start learning today.