views:

268

answers:

1

I am trying to get my head around the use of the Action delegate type for use in forcing a timeout when methods called in a 3rd party COM dll hang up. After much searching I find that I can use Action<> or Func<> and pass up to 4 generic parameters depending on whether the method called returns a parameter or not.

For this instance I wish to call a timeout on a series of methods that return void and take 2 parameters. What follows is the code that I am putting together, but I am unable to determine how to correctly code the BeginInvoke, I am prompted to place "T arg1" and "T arg2" but when I enter param1 or param2 VS2008 tells me that these values are indeterminate.

Here is the code as it is so far:

static void CallAndWait(Action<T, T> action, int timeout)
{
    Thread subThread = null;
    Action<T, T> wrappedAction = (param1, param2) =>
    {
        subThread = Thread.CurrentThread;
        action(param1, param2);
    };

    IAsyncResult result = wrappedAction.BeginInvoke(param1, param2, null, null);
    if (((timeout != -1) && !result.IsCompleted) &&
    (!result.AsyncWaitHandle.WaitOne(timeout, false) || !result.IsCompleted))
    {
        if (subThread != null)
        {
            subThread.Abort();
        }

        //TODO: close external resource.

        throw new TimeoutException();
    }
    else
    {
        action.EndInvoke(result);
    }
}

Any ideas on what is wrong here would be much appreciated.

Below is a re-edited code based on the first comment

Thanks for the input so far. The following compiles. I just can't seem to get the syntax right in calling it.

public static void CallAndWait<T1, T2>(Action<T1, T2> action, int timeout)
{
    Thread subThread = null;
    T1 param1 = default(T1);
    T2 param2 = default(T2);

    Action<T1, T2> wrappedAction = (p1, p2) =>
    {
        subThread = Thread.CurrentThread;
        action(param1, param2);
    };

    IAsyncResult result = wrappedAction.BeginInvoke(param1, param2, null, null);
    if (((timeout != -1) && !result.IsCompleted) &&
    (!result.AsyncWaitHandle.WaitOne(timeout, false) || !result.IsCompleted))
    {
        if (subThread != null)
        {
            subThread.Abort();
        }

        //TODO: close external resource.

        throw new TimeoutException();
    }
    else
    {
        action.EndInvoke(result);
    }
}

I am trying to test this by calling the following method with it:

public void LongTimeProcess(int a, string b)
{
    Thread.Sleep(a);
}

But the following code is not correct:

    Action<int, string> action = (s1, s2) => LongTimeProcess(s1, s2);
    CallAndWait<int, string>(action(1500, "hello"), 500);

Updated code I've posted the code for future reference by forum users. The code below appears to work. The only point to check is that my unit test causes an exception to be thrown when calling the routine a second time on the same function at the point where we "action.EndInvoke(result)" as the result is not associated with the action. This is probably because my LongProcess is just a Thread.sleep, which in this instance will mean that it hasn't aborted by the time that my second call is made.

    public static void CallAndWait<T1, T2>(Action<T1, T2> action, T1 arg1, T2 arg2, int timeout)

    {
        Thread subThread = null;
        Action<T1, T2> wrappedAction = (p1, p2) =>
        {
            subThread = Thread.CurrentThread;
            action(arg1, arg2);
        };

        IAsyncResult result = wrappedAction.BeginInvoke(arg1, arg2, null, null);
        if (((timeout != -1) && !result.IsCompleted) &&
        (!result.AsyncWaitHandle.WaitOne(timeout, false) || !result.IsCompleted))
        {
            if (subThread != null)
            {
                subThread.Abort();
            }

            //TODO: close external resource.

            throw new TimeoutException();
        }
        else
        {
            action.EndInvoke(result);
        }
    }
+1  A: 

At first it should probably be

static void CallAndWait<T>(Action<T, T> action, int timeout)

instead of

static void CallAndWait(Action<T, T> action, int timeout)

and if the parameters have different types even the following.

static void CallAndWait<T1, T2>(Action<T1, T2> action, int timeout)

But I don't think that is all. Going to look at it again.

UPDATE

Now I can see your problem ... you are calling the action when you try to call CallAndWait(). The call must be the following

CallWithTimeout.CallAndWait(action, 1500, "hello", 500);

instead of your call.

CallWithTimeout.CallAndWait<int, string>(action(1500, "hello"), 500);

So you have to change the method signature from

void CallAndWait<T1, T2>(Action<T1, T2> action, int timeout)

to

void CallAndWait<T1, T2>(Action<T1, T2> action, T1 arg1, T2 arg2, int timeout)

modify the body a bit and you should be done.

Daniel Brückner
Ah, okay. That makes sense and gives me something to look at further.
ChrisBD
Thanks that's worked a treat.
ChrisBD