'Get return value of method from TaskCompletionSource

I have a method RenderReport which generates a PDF file (byte[]). This can sometimes hang indefinitely. It should take no more than 15 seconds to complete when successful. Hence, I'm using a TaskCompletionSource to be able to limit the execution time and throw a TimeoutException if it exceeds the timeout.

However, what I can't determine is: how do you provide the byte[] file returned by RenderReport to the SetResult in the following code? longRunningTask.Wait returns a boolean and not the file so where do you get the file from?

I don't want to use longRunningTask.Result as that can introduce deadlock issues. Here's my code:

public async Task RunLongRunningTaskAsync()
{
    Task<byte[]> longRunningTask = Task.Run(() => RenderReport());
    TaskCompletionSource<byte[]> tcs = new TaskCompletionSource<byte[]>();
    Task toBeAwaited = tcs.Task;

    new Thread(() => ThreadBody(longRunningTask, tcs, 15)).Start();

    await toBeAwaited;
}

private void ThreadBody(Task<byte[]> longRunningTask, TaskCompletionSource<byte[]> tcs, int seconds)
{
    bool completed = longRunningTask.Wait(TimeSpan.FromSeconds(seconds));

    if (completed)
        // The result is a placeholder. How do you get the return value of the RenderReport()?
        tcs.SetResult(new byte[100]);
    else
        tcs.SetException(new TimeoutException("error!"));
}

private byte[] RenderReport()
{
    using (var report = new Microsoft.Reporting.WinForms.LocalReport())
    {
        // Other logic here...
        
        var file = report.Render("PDF", null, out _, out _, out _, out _, out var warnings);

        if (warnings.Any())
        {
            // Log warnings...
        }

        return file; // How do I get this file?
    }
}


Solution 1:[1]

What works for me is to use ContinueWith:

This shows a first run with a successful retrieval in the specified 2-seconds and then a second run where it times out.

enter image description here

So it's just one approach, but is this helpful at all?

using System;
using System.Threading;
using System.Threading.Tasks;

namespace task_completion
{
    class Program
    {
        static void Main(string[] args)
        {
            runAsync();
            Console.ReadKey();            
        }

        static async void runAsync()
        {
            // Normal
            await longRunningByteGetter(1000, new CancellationTokenSource(2000).Token)                
                .ContinueWith((Task<byte[]> t)=> 
                {
                    switch (t.Status)
                    {
                        case TaskStatus.RanToCompletion:
                            var bytesReturned = t.Result;
                            Console.WriteLine($"Received {bytesReturned.Length} bytes");
                            break;
                        default:
                            Console.WriteLine(nameof(TimeoutException));
                            break;
                    }
                });

            // TimeOut
            await longRunningByteGetter(3000, new CancellationTokenSource(2000).Token)
                .ContinueWith((Task<byte[]> t) =>
                {
                    switch (t.Status)
                    {
                        case TaskStatus.RanToCompletion:
                            var bytesReturned = t.Result;
                            Console.WriteLine($"Received {bytesReturned.Length} bytes");
                            break;
                        default:
                            Console.WriteLine(nameof(TimeoutException));
                            break;
                    }
                });
        }

        async static Task<Byte[]> longRunningByteGetter(int delay, CancellationToken token)
        {
            await Task.Delay(delay, token); // This is a mock of your file retrieval
            return new byte[100];
        }
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 IVSoftware