Link to home
Start Free TrialLog in
Avatar of BlearyEye
BlearyEyeFlag for United States of America

asked on

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

ASKER CERTIFIED SOLUTION
Avatar of kaylanreilor
kaylanreilor
Flag of Luxembourg image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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

Avatar of BlearyEye

ASKER

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.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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.
kaylanreilor made some good observations, though he didn't answer my last question. so points assigned for good try ...