15

I have this pretty straightforward piece of code that very rarely throws "System.ApplicationException : Object synchronization method was called from an unsynchronized block of code." when ReleaseMutex() is called.

I logically analyzed the flow of the method and just cannot understand how/why this could happen. To my understanding, the ownership of mutex is guaranteed in this case:

    readonly string mutexKey;

    public Logger(string dbServer, string dbName)
    {
        this.mutexKey = ServiceManagerHelper.GetServiceName(dbServer, dbName);
    }

    private void Log(LogType type, string message, Exception ex)
    {
        using (var mutex = new Mutex(false, mutexKey))
        {
            bool acquiredMutex;
            try
            {
                acquiredMutex = mutex.WaitOne(TimeSpan.FromSeconds(5));
            }
            catch (AbandonedMutexException)
            {
                acquiredMutex = true;
            }

            if (acquiredMutex)
            {
                try
                {

                    // some application code here

                }
                finally
                {
                    mutex.ReleaseMutex();
                }
            }
        }
    }
Igor Malin
  • 652
  • 2
  • 8
  • 26

3 Answers3

12
        catch (AbandonedMutexException)
        {
            acquiredMutex = true;
        }

This is a very serious bug in your code. Catching an AbandonedMutexException is never correct, it is a very serious mishap. Another thread acquired the mutex but terminated without calling ReleaseMutex(). You've irrecoverably lost synchronization and the mutex is no longer usable.

You were sort of lucky by making a mistake and assuming that you acquired the mutex anyway. You didn't. The ReleaseMutex() call will now bomb with the exception you quoted.

You cannot recover from this mishap, other than by terminating the program (the wise choice) or by disabling logging completely so the mutex will never be used again. Make the wise choice by removing the catch clause. Discovering the true source of the problem, that thread that crashed and didn't call ReleaseMutex(), is out of context for this question, there are no hints. You've been ignoring this problem, papered it over by catching AME, you can't ignore it.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • 2
    Hi Hans, thanks for reply. Although I still don't understand.. MSDN says that: "When a thread abandons a mutex, the exception is thrown in the next thread that acquires the mutex." So if we handle this exception given we can validate the correctness of protected entity - we are fine. Aren't we? If not, why? Also from MSDN: "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." And: http://stackoverflow.com/questions/15456986/how-to-gracefully-get-out-of-abandonedmutexexception – Igor Malin Dec 09 '14 at 00:03
  • 2
    There is no Santa Claus unless you can prove that a sled can really fly through the air. That does require making that sled fly first. Write a test that abandons the mutex intentionally and keep your program running. – Hans Passant Dec 09 '14 at 08:00
8

In my case, i see the same behavior like Nathan Schubkegel. I use await's, and Thread.CurrentThread.ManagedThreadId gives another value for the "same" thread. I mean, thread was started with ManagedThreadId == 10, and Mutex was owned with this thread id, but later ReleaseMutex() causes ApplicationException with message: "Object synchronization method was called from an unsynchronized block of code", and i see that ManagedThreadId == 11 at this time :) . It seems, await sometimes changes thread id when returns. It seems, that is the reason. Mutex thinks that another thread wants to release it. It's sad, that Mutex documentation does not make ATTENTION on this moment.

So, you CAN NOT use asynchronous operator await between Mutex acquire and release. It's because C# compiler replaces plain operator await by asynchronous callback, and this callback can be made by ANOTHER thread. Usually, it's the same thread, but sometimes it's another thread (from thread pool).

Mutex checks thread. Only thread that acquired Mutex may release it. If you need synchronization without this checking, use Semaphore. SemaphoreSlim has asynchronous method WaitAsync() - it's cool.

Mikola Akbal
  • 141
  • 2
  • 5
4

This exception is raised when you call ReleaseMutex() from a thread that does not own the mutex. Search // some application code here for code that releases the mutex.

Also reconsider whether you're actually calling ReleaseMutex() from the same thread where you called WaitOne(). Example: I arrived at this post because I was using async/await and my code resumed on a different thread and tried to release a mutex the thread didn't own.

  • It is a named mutex, so I dont' think you are correct here. – debater Mar 20 '19 at 14:49
  • "because I was using async/await and my code resumed on a different thread and tried to release a mutex the thread didn't own" - this will be the case when you use await xx.ConfigureAwait(false). This exception is thrown for named mutexes, too. – Dilyan Rusev Nov 18 '19 at 16:10
  • This answer is correct. The thread that owns the Mutex will generally not be the same thread that resumes after the await completes after awaiting async code. – Tom Apr 15 '20 at 01:33