Erwin Mayer
Erwin Mayer

Reputation: 18670

How to check if ManualResetEvent has been disposed, when trying to Set() it inside an EventHandler?

I have the following design pattern:

    var myObjectWithEvents = new ObjectWithEvents();
    using (var mre = new ManualResetEvent(false)) {
        var onEvent = new EventHandler<EventArgs>((sender, e) => { mre.Set(); });   
        try {
            myObjectWithEvents.OnEvent += onEvent;
            var task = Task.Factory.StartNew(() => {
                myObjectWithEvents.DoSomethingThatShouldRaiseAnEvent();
            });
            var timedOut = !mre.WaitOne(10000);
        }
        finally {
            myObjectWithEvents.OnEvent -= onEvent;
        }
    }

My issue is that if OnEvent is raised after the WaitOne times out and execution steps out of the using block, the local onEvent event handler will still be called and try to set the ManualResetEvent mre which will have already been disposed, even though onEvent should have been unregistered from OnEvent.

A simple workaround would be to check if mre has already been disposed, but unfortunately there is no such field, and I believe wrapping mre.Set() inside a try catch block to ignore the exception is not clean given that the exception could occur quite frequently.

What would you suggest as the best and simplest way to achieve the purpose of the above code pattern (i.e. waiting for an event to be raised) without running into this kind of issue?

Edit: Thanks to your answers, I created the following extension and replaced mre.Set() with mre.TrySet():

    public static void TrySet(this ManualResetEvent mre) {
        if (!mre.SafeWaitHandle.IsClosed) mre.Set();
    }

Upvotes: 15

Views: 3585

Answers (3)

Viacheslav Smityukh
Viacheslav Smityukh

Reputation: 5843

You can try to check it by the mre.SafeWaitHandle.IsClosed property

Upvotes: 8

sll
sll

Reputation: 62544

As a case try out using simple boolean switch which indicating whether setting manualResetEvent is actual:

bool isMreSync = true;
var myObjectWithEvents = new ObjectWithEvents();
using (var mre = new ManualResetEvent(false)) 
{
    var onEvent = new EventHandler<EventArgs>((sender, e) => 
                 { 
                     if (isMreSync)
                     {
                         mre.Set(); 
                     }
                 });

  // try ... finally block  
 }

isMreSync = false;

If event might be executed asynchronously - synchronize access to boolean switch.

Upvotes: 1

Guido Zanon
Guido Zanon

Reputation: 3059

ManualResetEvent.SafeWaitHandle.IsClosed

Seems strange, but the only thing that the dispose does is to close the safeHandler, which is the only object that its the dispose intended to...

The Dispose of the SafeWaitHandle, changes this property from False to True.

Upvotes: 15

Related Questions