Click here to Skip to main content
15,887,214 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
I have been trying to follow the article here Crafting Task Timeout on how to create a utility extension method to add a timeout to a Task.

My code (with some omissions) is shown below. It shows two versions of the timeout functionality, one non-generic and one generic.

As you can see, I have tried to implement the delegate that is passed into the Timer as a separate function. This is fine in the non-generic version but in the generic version, the cast of the TaskCompletionSource fails because the generic type TResult is not a VoidTypeStruct.

As you can see, the timer delegate doesn't actually care about the type of the TaskCompletionSource (other than needing to know how to do the cast). Is there a way to modify either my timer delegate or the place where it is passed into the new Timer() so that it can cope with the generic version of the function?

This is one of those frustrating examples where I can see the problem and I can see what needs to change but I just don't know how to write it.

C#
public static Task TimeoutAfter( this Task task, int millisecondsTimeout )
{	
    TaskCompletionSource<VoidTypeStruct> tcs = new TaskCompletionSource<VoidTypeStruct>();
    Timer timer = new Timer( TimerEventHandler, tcs, millisecondsTimeout, true );
    timer.Start();
    task.ContinueWith( ( antecedent, state ) =>
    {
        var stateTuple = (Tuple<Timer,TaskCompletionSource<VoidTypeStruct>>)state;
        stateTuple.Item1.Dispose();
        MarshalTaskResults( antecedent, stateTuple.Item2 );
    },
    Tuple.Create<ITimer,TaskCompletionSource<VoidTypeStruct>>( timer, tcs ),
    CancellationToken.None,
    TaskContinuationOptions.ExecuteSynchronously,
    TaskScheduler.Default );
    return tcs.Task;
}

public static Task<TResult> TimeoutAfter<TResult>( this Task<TResult> task, int millisecondsTimeout )
{	
    TaskCompletionSource<TResult> tcs = new TaskCompletionSource<TResult>();
    Timer timer = new Timer( TimerEventHandler, tcs, millisecondsTimeout, true );
    timer.Start();
    task.ContinueWith( ( antecedent, state ) =>
    {
        var stateTuple = (Tuple<ITimer,TaskCompletionSource<TResult>>)state;
        stateTuple.Item1.Dispose();
        MarshalTaskResults( antecedent, stateTuple.Item2 );
    },
    Tuple.Create<ITimer,TaskCompletionSource<TResult>>( timer, tcs ),
    CancellationToken.None,
    TaskContinuationOptions.ExecuteSynchronously,
    TaskScheduler.Default );
    return tcs.Task;
}

// This dummy type is needed because there isn't a non-generic TaskCompletionSource.
private struct VoidTypeStruct { }

internal static void MarshalTaskResults<TResult>( Task source, TaskCompletionSource<TResult> proxy )
{
    // Code omitted because it is not relevant to this problem.
}

private static void TimerEventHandler( object state )
{
    TaskCompletionSource<VoidTypeStruct> tcs = state as TaskCompletionSource<VoidTypeStruct>; // THIS OBVIOUSLY FAILS IN GENERIC VERSION!
    tcs.TrySetException( new TimeoutException() );
}


What I have tried:

I have tried making the TimerEventHandler<t>() generic but then the constructor for the Timer says it 'Cannot convert method group to Action<object>'.
Posted

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900