0

Here is the description of the problem:

I need to make an API call synchronized. In order to make this call synchronized, I defined a ManualResetEvent and called WaitOne on it. WaitOne is called after the call to external COM object. In some circumstances it never returns. That's why I must define a timeout for that Wait call. But, I can't pass a constant into Wait method, because if the call was successfull than this API receives events from the COM object and in each handler of an event, timeout passed into WaitOne should be reset.

Consider the example:

private ManualResetEvent operationIsInProcess;
private static readonly IComObject sender;
private int timeout = 30000;

public void Start() {                           
    sender.OnExchange += SenderOnOnExchange;            
}
private void StartOperation(){
    sender.StartAsyncExchange();
    operationIsInProcess.WaitOne(timeout);
}

private void SenderOnOnExchange(){
    //somehow we need to reset or update that timeout on WaitOne
    //operationInProcess.Update(timeout);
}

I'm just wondering whether anybody faced this problem or not. I'm sure this should be a common situation. As I understand there is no "out of the box" solution. So I have to build my own syncronization primitive, or maybe someone have already done it?

Update. I wanted something like this (implemented by myself):

public class UpdateableSpin {
        private readonly object lockObj = new object();
        private bool shouldWait;
        private long taskExecutionStartingTime;

        public UpdateableSpin(bool initialState) {
            shouldWait = initialState;
        }

        public void Wait(TimeSpan executionTimeout, int spinDuration = 0) {
            UpdateTimeout();
            while (shouldWait && DateTime.UtcNow.Ticks - taskExecutionStartingTime < executionTimeout.Ticks) {
                lock (lockObj) {
                    Thread.Sleep(spinDuration);
                }
            }
        }

        public void UpdateTimeout() {
            lock (lockObj) {
                taskExecutionStartingTime = DateTime.UtcNow.Ticks;
            }
        }

        public void Reset() {
            lock (lockObj) {
                shouldWait = true;
            }
        }

        public void Set() {
            lock (lockObj) {
                shouldWait = false;
            }
        }
    }
EngineerSpock
  • 2,575
  • 4
  • 35
  • 57

1 Answers1

1

You could enter a loop and restart the wait:

while (true)
{
    if (!operationIsInProcess.WaitOne(timeout))
    {
        // timed out 
        break;
    }
    else
    {
        // Reset the signal.
        operationIsInProcess.Reset();
    }
}

Then set your event in the event handler.

private void SenderOnOnExchange () 
{
    operationInProcess.Set();
}
RobH
  • 3,604
  • 1
  • 23
  • 46
  • No, it won't work. Consider the following case: exchange was started and a couple of events occured but a transaction is still in process, now for the unknown reason there is a relatively long latency, so it's very possible to be timed out on ManualResetEvent, whereas the process is still executing. There is some logic implemented in the handler. – EngineerSpock Jan 19 '15 at 12:07
  • Ah I see, yes. That is more complicated. So what do want, a timeout on the initial set-up but then just wait? Or do you want a timeout t1 on the set-up, then another time t2 for subsequent waits? – RobH Jan 19 '15 at 12:11
  • If an event occurs, timeout should be evaluated from the beginning. – EngineerSpock Jan 19 '15 at 12:15
  • From the beginning of what? The code I have suggested restarts the timeout every time `SenderOnExchange` is called. In reality, you can restart the timeout as often as you like by calling `Set` on the ManualResetEvent`. – RobH Jan 19 '15 at 12:20
  • There are two types of events. One type presumes that timeout should be reevaluated (started from the beginning) and the other type which presumes that we have to stop waiting immediately. – EngineerSpock Jan 19 '15 at 12:30