Skip to content

Consider adding TaskCompletionSource.SetFromTask(Task) #47998

@davidfowl

Description

@davidfowl

EDITED by @stephentoub on 1/21/2023:

  • I added Try methods.
namespace System.Collections.Threading.Tasks
{
    public class TaskCompletionSource
    {
+        public void SetFromTask(Task task);
+        public bool TrySetFromTask(Task task);
    }

    public class TaskCompletionSource<T>
    {
+        public void SetFromTask(Task<T> task);
+        public bool TrySetFromTask(Task<T> task);
    }
}

These methods require that the task argument has already completed and transfer the result/exception/cancellation state over to the TCS's Task.

Background and Motivation

A common operation when writing async code is to execute code that returns a task and complete. This usually involves creating a TaskCompletionSource to represent the operation, then executing the operation that returns a task at point in the future, then setting the result on completion to either failed, cancelled or success.

Proposed API

namespace System.Collections.Threading.Tasks
{
    public class TaskCompletionSource
    {
+       public void SetFromTask(Task task);
    }

    public class TaskCompletionSource<T>
    {
+       public void SetFromTask(Task<T> task);
    }
}

Usage Examples

public class Connection
{
    private TaskCompletionSource _tcs = new();
    private CancealltionTokenSource _cts = new();

    public Task Run()
    {
        ThreadPool.QueueUserWorkItem(_ => Start());
        return _tcs.Task;
    }
    
    public void Start()
    {
        tcs.SetFromTask(NetworkHelper.StartConnectionAsync(_cts));
    }
    // ...
}

This is a common pattern when you need to kick off an async operation and can't directly wait the operation inline.

Alternative Designs

Do this manually.

public static class TaskCompletionSourceExtensions
{
    public void SetFromTask(this TaskCompletionSource  task, Task task)
    {
        async Task Continue()
        {
            try
            {
                await task;
                tcs.TrySetResult();
            }
            catch (OperationCancelledException ex)
            {
               tcs.TrySetCancelled(ex.Cancelled);
            }
            catch (Exception ex)
            {
               tcs.TrySetException(ex);
            }
        }
        _ = Continue();
    }
}

Risks

None.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions