kyurkchyan
kyurkchyan

Reputation: 2428

Object synchronization method was called from an unsynchronized block of code. Exception on Mutex.Release()

I have found different articles about this exception but none of them was my case. Here is the source code:

class Program
{

    private static Mutex mutex;
    private static bool mutexIsLocked = false;
    static void Main(string[] args)
    {

        ICrmService crmService = 
            new ArmenianSoftware.Crm.Common.CrmServiceWrapper(GetCrmService("Armsoft", "crmserver"));
        //Lock mutex for concurrent access to workflow
        mutex = new Mutex(true, "ArmenianSoftware.Crm.Common.FilterCtiCallLogActivity");
        mutexIsLocked = true;

        //Create object for updating filtered cti call log
        ArmenianSoftware.Crm.Common.FilterCtiCallLog filterCtiCallLog =
            new ArmenianSoftware.Crm.Common.FilterCtiCallLog(crmService);
        //Bind events
        filterCtiCallLog.CtiCallsRetrieved += new EventHandler<ArmenianSoftware.Crm.Common.CtiCallsRetrievedEventArgs>(filterCtiCallLog_CtiCallsRetrieved);

        //Execute filter
        try
        {
            filterCtiCallLog.CreateFilteredCtiCallLogSync();
        }
        catch (Exception ex)
        {
            throw ex;
        }
        finally
        {
            if (mutexIsLocked)
            {
                mutexIsLocked = false;
                mutex.ReleaseMutex();
            }
        }
    }

    static void filterCtiCallLog_CtiCallsRetrieved(object sender,
         ArmenianSoftware.Crm.Common.CtiCallsRetrievedEventArgs e)
    {
        tryasasas
        {
            if (mutexIsLocked)
            {
                mutexIsLocked = false;
                mutex.ReleaseMutex();
            }
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }
}

filterCtiCallLog.CreateFilteredCtiCallLogSync(); function executes requests to server, and raises some events, one of which is CtiCallsRetrieve event. And I need to release the mutex when this event is fired. But on calling the mutex.Release() function exception is thrown. CreateFilteredCtiCallLogSync works synchronously. What is the problem?

Upvotes: 49

Views: 95929

Answers (10)

FindOutIslamNow
FindOutIslamNow

Reputation: 1236

Maybe the compiler was preventing you from using await with lock so you switched to use Monitor.Enter and Monitor.Exit ?

First:

lock (obj)
{
   ...
   await SomeAsyncMethod(); // compilation error
   ...
}

After thinking you are smart to do this workaround:

try
{
   Monitor.Enter(obj);
   ...
   await SomeAsyncMethod(); // NOT compilation error, but runtime Excetpion !!
   ...
}
finally
{
   Monitor.Exit(obj);
}

You can't do that also, since lock is the same as Monitor.Enter with finally clause in it Monitor.Exit

Solution:

Use SemaphoreSlim. For example usage check this article: https://dotnettutorials.net/lesson/semaphoreslim-class-in-csharp/

Upvotes: -1

sina_Islam
sina_Islam

Reputation: 1163

I have read the thread and got some ideas. But did not know what exactly need to do to solve the issue. I face the same error when uploading the image to the s3 at nopCommerce solution.And the below code is working for me.

   using var mutex = new Mutex(false, thumbFileName);
         mutex.WaitOne();
          try
           {
             if (pictureBinary != null)
              {
                  try
                     {
                       using var image = SKBitmap.Decode(pictureBinary);
                       var format = GetImageFormatByMimeType(picture.MimeType);
                       pictureBinary = ImageResize(image, format, targetSize);
                     }
                   catch
                     {
                     }
             }
                
          if (s3Enabled)
           //await S3UploadImageOnThumbsAsync(thumbFileName, pictureBinary, picture.MimeType, picture, targetSize);
           // The above code was causing the issue. Because it is wait for the thread. 
           //So I replace the code below line and the error disappear. This also kind of same implementation by nopCommerce.
           //The thread need to wait.
    
           S3UploadImageOnThumbsAsync(thumbFileName, pictureBinary, picture.MimeType, picture, targetSize).Wait();
         else
            File.WriteAllBytes(thumbFilePath, pictureBinary);    
         }
         finally
         {
             mutex.ReleaseMutex();
         }  

Upvotes: 0

Basalat Raja
Basalat Raja

Reputation: 141

You will also run into this exception if you do the following:

        mutex.WaitOne();
        … Some Work...
        await someTask;
        mutex.ReleaseMutex();

That's because the code after the await can be executed on a different thread from the line just before. Basically, it seems that if you asynch code now (in early 2020), Mutexes simply don't work. Use events or something.

Upvotes: 13

Igor Toropov
Igor Toropov

Reputation: 472

Another reason why this exception may occur:

if (Monitor.TryEnter(_lock))
{
    try
    {
        ... await MyMethodAsync(); ...
    }
    finally
    {
        Monitor.Exit(_lock);
    }
}

I get this exception on Monitor.Exit when after 'await' another thread continues execution.

Edit: Use SemaphoreSlim, because it doesn't require releasing thread to be the same.

Upvotes: 25

Walter Verhoeven
Walter Verhoeven

Reputation: 219

I have seen this happen when you lock code using a Monitor, then call an async code and you get this, when using a lock(object) you get a compiler error, however between monitor.enter(object) and Monitor.Exist(object) the compiler does not complain... unfortunately.

Upvotes: 5

Nemo
Nemo

Reputation: 3373

Maybe not the most meaningful error message, I've seen this happen in some third party code as below,

object obj = new object();
lock (obj)
{
    //do something

    Monitor.Exit(obj);//obj released

}//exception happens here, when trying to release obj

Upvotes: 1

kyurkchyan
kyurkchyan

Reputation: 2428

I have found the problem. First several things about the filterCtiCallLog class. I have designed it so to work both asynchronous and synchronous. For first I have written code for asynchronous execution. I needed a way to trigger events from child worker thread to parent, to report the working state. For this I have used AsyncOperation class and it's post method. Here is the code part for triggering CtiCallsRetrieved event.

public class FilterCtiCallLog
{
    private int RequestCount = 0;
    private AsyncOperation createCallsAsync = null;
    private SendOrPostCallback ctiCallsRetrievedPost;
    public void CreateFilteredCtiCallLogSync()
    {
        createCallsAsync = AsyncOperationManager.CreateOperation(null);
        ctiCallsRetrievedPost = new SendOrPostCallback(CtiCallsRetrievedPost);
        CreateFilteredCtiCallLog();
    }

    private void CreateFilteredCtiCallLog()
    {
        int count=0;
        //do the job
        //............
        //...........
        //Raise the event
        createCallsAsync.Post(CtiCallsRetrievedPost, new CtiCallsRetrievedEventArgs(count));
        //...........
        //...........
    }

    public event EventHandler<CtiCallsRetrievedEventArgs> CtiCallsRetrieved;

    private void CtiCallsRetrievedPost(object state)
    {
        CtiCallsRetrievedEventArgs args = state as CtiCallsRetrievedEventArgs;
        if (CtiCallsRetrieved != null)
            CtiCallsRetrieved(this, args);
    }
}

As you can see the code is executing synchronously. The problem here is in AsyncOperation.Post() method. I presumed that if it is called in the main thread it will act as simply triggering the event, not posting it to parent thread. However it wasn't the case. I don't know how it is working, but I have changed the code, to check if the CreateFilteredCtiCallLog is called sync or async. And if it is async call I used AsyncOperation.Post method, if not, I have simply triggered the EventHandler if it is not null. Here is the corrected code

public class FilterCtiCallLog
{
    private int RequestCount = 0;
    private AsyncOperation createCallsAsync = null;
    private SendOrPostCallback ctiCallsRetrievedPost;
    public void CreateFilteredCtiCallLogSync()
    {
        createCallsAsync = AsyncOperationManager.CreateOperation(null);
        ctiCallsRetrievedPost = new SendOrPostCallback(CtiCallsRetrievedPost);
        CreateFilteredCtiCallLog(false);
    }

    private void CreateFilteredCtiCallLog(bool isAsync)
    {
        int count=0;
        //do the job
        //............
        //...........
        //Raise the event
        RaiseEvent(CtiCallsRetrievedPost, new CtiCallsRetrievedEventArgs(count),isAsync);
        //...........
        //...........
    }

    public event EventHandler<CtiCallsRetrievedEventArgs> CtiCallsRetrieved;

    private void RaiseEvent(SendOrPostCallback callback, object state, bool isAsync)
    {
        if (isAsync)
            createCallsAsync.Post(callback, state);
        else
            callback(state);
    }

    private void CtiCallsRetrievedPost(object state)
    {
        CtiCallsRetrievedEventArgs args = state as CtiCallsRetrievedEventArgs;
        if (CtiCallsRetrieved != null)
            CtiCallsRetrieved(this, args);
    }
}

Thanks everybody for the answers!

Upvotes: 5

Martin James
Martin James

Reputation: 24857

Using a flag to attempt to monitor a kernel synchro object state will just not work - the point of using those synchro calls is that they work correctly without any explicit checking. Setting flags will just cause intermittent problems because the flag may be changed inappropriately due to interrupts between checking the flag and acting on it.

A mutex can only be released by the threat that acquired it. If you callback is called by a different thread, (one internal to CreateFilteredCtiCallLogSync() or a kernel thread pool), the release will fail.

It's not clear exactly what you are attempting to do. Presumably, you want to serialize access to CreateFilteredCtiCallLogSync() and the callback flags that the instance is available for re-use? If so, you could use a semaphore instead - init. it to one unit, wait for it at the start and release it in the callback.

Is there some issue where sometimes the callback is not called, and hence the try/finally/release? If so this way out seems a bit dodgy if the callback is asychronous and may be called by another thread after the setup thread has left the function.

Upvotes: 3

Hans Passant
Hans Passant

Reputation: 941505

Keeping a bool around that indicates that the mutex is owned is a grave mistake. You are not making the bool thread-safe. You got into this pickle because you are using the wrong synchronization object. A mutex has thread-affinity, the owner of a mutex is a thread. The thread that acquired it must also be the one that calls ReleaseMutex(). Which is why your code bombs.

You in all likelihood need an event here, use AutoResetEvent. Create it in the main thread, call Set() in the worker, WaitOne() in the main thread to wait for the worker to complete its job. And dispose it afterwards. Also note that using a thread to perform a job and having your main thread wait for its completion is not productive. You might as well have the main thread do the job.

If you are actually doing this to protect access to an object that's not thread-safe (it isn't clear) then use the lock statement.

Upvotes: 61

Willem van Rumpt
Willem van Rumpt

Reputation: 6570

I only had this one once or twice, and in every case it came about by trying to release a mutex I didn't own.

Are you sure the events are raised on the same thread the mutex was acquired on? Although you mention that filterCtiCallLog.CreateFilteredCtiCallLogSync() is a blocking call, perhaps it spawns of worker threads that raise the event?

Upvotes: 2

Related Questions