1

I have a class that wraps ManualResetEvent (see in the code sample below)

During my application flow I call the WaitOne method with a 0 parameter a few times. It returns true as expected, except for one particular check that returns false although no exception was thrown and I can clearly see from my logs that the event was set.

Could there be another reason for WaitOne to return false?

I will also mention that I have 2 different engines inheriting from the same engine that has a SafeManualResetEvent member. Lets call them A and B. The flow is that A.SafeManualResetEvent was set, and a few calls of A.WaitOne(0) returned true. Then, B.Set() is called and completed. Then, A.WaitOne(0) is called again and returns false.

After that, all A.WaitOne(0) and B.WaitOne(0) return true as expected. Is it possible that the two instances of ManualRestEvent are disturbing each other?

I am adding here a very abstract sample. Please note that the waitOne calls can be from different threads in our app. Please also note that this issue doesn't happen consistently. Out of thousands of users running this app, only one complained about it but we are still struggling to figure out what's different in his environment.

public void Main()
    {
        PolicyEngine policy = new PolicyEngine();

        policy.WaitEngine(); //will return true because initial state is true

        policy.LoadEngine();

        policy.WaitEngine(); //will return true

        FileEngine file = new FileEngine();

        policy.WaitEngine(); //will return true

        file.LoadEngine();

        policy.WaitEngine(); //returns false - unexpected!

        policy.WaitEngine(); //returns true

        file.WaitEngine(); //returns true
    }

    public class Engine
    {
        protected virtual string GetName()
        {
            return "base";
        }

        private readonly SafeManualResetEvent _engineLoadedEvent = new SafeManualResetEvent(true);

        public Engine()
        {
            _engineLoadedEvent._name = GetName();
            Console.WriteLine("base");
        }

        public void LoadEngine()
        {
            _engineLoadedEvent.Reset();

            //some code that loads the engine

            _engineLoadedEvent.Set();
        }

        public void WaitEngine()
        {
            bool result = _engineLoadedEvent.WaitOne(new TimeSpan(0));
            Console.WriteLine($"waitOne result is: {result}");
        }
    }

    public class FileEngine : Engine
    {
        override protected string GetName()
        {
            return "file";
        }
    }

    public class PolicyEngine : Engine
    {
        override protected string GetName()
        {
            return "policy";
        }
    }

    public class SafeManualResetEvent : IDisposable
    {
        public string _name;

        public SafeManualResetEvent(bool initialState)
        {
            ManualResetEvent = new ManualResetEvent(initialState);
        }

        public ManualResetEvent ManualResetEvent { get; }

        public bool WaitOne(TimeSpan timeout)
        {
            try
            {
                Console.WriteLine($"waitOne event for {_name}");
                return ManualResetEvent.WaitOne(timeout);
            }
            catch (ObjectDisposedException)
            {
                return false;
            }
        }

        public void Reset()
        {
            try
            {
                Console.WriteLine($"reset event for {_name}");
                ManualResetEvent.Reset();
            }
            catch (ObjectDisposedException)
            {
            }
        }

        public void Set()
        {
            try
            {
                Console.WriteLine($"set event for {_name}");
                ManualResetEvent.Set();
            }
            catch (ObjectDisposedException)
            {
            }
        }

        public void Dispose()
        {
            Console.WriteLine($"dispose event for {_name}");
            ManualResetEvent.Dispose();
        }
    }
avmerber
  • 11
  • 2
  • I am confused as to why you are wanting a `WaitOne` to *timeout* in 0 milliseconds? – TheGeneral Oct 25 '21 at 06:33
  • 2
    Can you make a working [mcve] of what you are doing, and explain how it differs from your expectations. This could be as simple as there is a race condition somewhere. – TheGeneral Oct 25 '21 at 06:38
  • 2
    "I can clearly see from my logs that the event was set" - are your logs guaranteed to produce a globally consistent timeline in a multithreaded environment? – JonasH Oct 25 '21 at 06:41
  • If you call `ManualResetEvent.Set()` followed immediately by `ManualResetEvent.Reset()` then it's possible that NO threads waiting on the event see it being set. Could that be what's happening? (See [my question about this](https://stackoverflow.com/questions/15067369/manualreseteventslim-calling-set-followed-immediately-by-reset-doesnt-re).) – Matthew Watson Oct 25 '21 at 08:09
  • I am using a timeout of 0 milliseconds because I just want to verify if the event was already set or not, I do not want to wait in case it is not. In the official .net documentation its written: if millisecondsTimeout is zero, the method does not block. It tests the state of the wait handle and returns immediately. – avmerber Oct 25 '21 at 11:14
  • @jonasH you are generally write regarding the logs. I can never be 100% sure about it but in my case I see a few seconds of difference between the logs so it is enough time to give me the confidence I need, even with multithreading. – avmerber Oct 25 '21 at 11:18
  • @MatthewWatson that's not my case, I don't call the reset again. – avmerber Oct 25 '21 at 11:22
  • I will try to re-explain my question - is it possible that running 2 different instances of ManualResetEvent in my system, impact each other? or is it guaranteed that they have no connection and they are 2 totally different instances? – avmerber Oct 25 '21 at 11:25
  • This might be obvious, but are you sure you are using the same object? More than one bug have been caused by components using different objects instead of one single object. Also, a minimal reproducible example would be useful. If I understand it correctly it sounds like a error in the manualResetEvent, that is unlikely, so the issue may be somewhere else that is not shown by the current example. – JonasH Oct 25 '21 at 11:29
  • Added a minimal example, hope it helps. Thanks! – avmerber Oct 27 '21 at 06:56

0 Answers0