There is a class TaskCompletionSource<TResult> as described in The Nature of TaskCompletionSource<TResult>:

The TaskCompletionSource<TResult> type serves two related purposes, both alluded to by its name: it is a source for creating a task, and the source for that task’s completion. In essence, a TaskCompletionSource<TResult> acts as the producer for a Task<TResult> and its completion. You create a TaskCompletionSource<TResult> and hand the underlying Task<TResult> it’s created, accessible from its Task property. Unlike Tasks created by Task.Factory.StartNew, the Task handed out by TaskCompletionSource<TResult> does not have any scheduled delegate associated with it

CancellationToken

To me, a classic scenario for using TaskCompletionSource is when it’s possible that my method won’t necessarily have to do a time consuming operation. What it allows us to do is to choose the specific cases where we’d like to use a new thread.

A good example for this is when you use a cache. You can have a GetResourceAsync method, which looks in the cache for the requested resource and returns at once (without using a new thread, by using TaskCompletionSource) if the resource was found. Only if the resource wasn’t found, we’d like to use a new thread and retrieve it using Task.Run().

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

public class AsyncManualResetEvent
{
    private TaskCompletionSource taskCompletionSource = new TaskCompletionSource();

    public Task WaitAsync()
    {
        return taskCompletionSource.Task;
    }

    public void Set()
    {
        TaskCompletionSource tcs = taskCompletionSource;

        Task.Factory.StartNew(s => ((TaskCompletionSource) s).TrySetResult(true),
                              tcs,
                              CancellationToken.None,
                              TaskCreationOptions.PreferFairness,
                              TaskScheduler.Default);
        tcs.Task.Wait();
    }

    public void Reset()
    {
        while (true)
        {
            TaskCompletionSource tcs = taskCompletionSource;
            
            if (!tcs.Task.IsCompleted ||
                Interlocked.CompareExchange(ref taskCompletionSource, new TaskCompletionSource(), tcs) == tcs)
            {
                return;
            }
        }
    }
}

public static class TaskExtensions
{
    public static Task AsAwaitable(this CancellationToken token)
    {
        var ev = new AsyncManualResetEvent();
        token.Register(() => ev.Set());
        return ev.WaitAsync();
    } 
}

Source