James Ko
James Ko

Reputation: 34609

What's the most concise way to create a Task that never returns?

For testing purposes, I need to mock a Task-returning method on an interface handing back a task that never runs the continuation. Here's the code I have so far:

// FooTests.cs

[Test]
public void Foo()
{
    var yielder = Substitute.For<IYielder>();
    yielder.YieldAsync().Returns(ThreadingUtilities.NeverReturningTask);

    ...
}

// ThreadingUtilities.cs

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

namespace Repository.Editor.Android.UnitTests.TestInternal.Threading
{
    internal static class ThreadingUtilities
    {
        public static Task NeverReturningTask { get; } = CreateNeverReturningTask();

        private async static Task CreateNeverReturningTask()
        {
            await new StopAwaitable();
            Debug.Fail("Execution shouldn't reach here.");
        }
    }
}

// StopAwaitable.cs

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace Repository.Editor.Android.UnitTests.TestInternal.Threading
{
    internal struct StopAwaitable : INotifyCompletion
    {
        public bool IsCompleted => false;

        public StopAwaitable GetAwaiter() => this;

        public void GetResult()
        {
            Debug.Fail("The continuation shouldn't have been posted!");
        }

        public void OnCompleted(Action continuation)
        {
            // Ignore the continuation.
        }
    }
}

Here, I've created a custom awaitable type that simply ignores the continuation handed to it-- await new StopAwaitable() has essentially the same effect as a return from a sync method. Then, I await it in an async Task method, creating a Task object that will never run the continuation.

My question is, is this the most concise/clear way to do this? Is there any other way to create such a task that doesn't involve a custom awaitable, which will confuse people unfamiliar w/ how async works?

Upvotes: 15

Views: 2072

Answers (2)

Kirk Larkin
Kirk Larkin

Reputation: 93273

You can use:

var taskThatNeverReturns = Task.Delay(Timeout.Infinite);

The docs states that the parameter represents:

The number of milliseconds to wait before completing the returned task, or -1 to wait indefinitely.

Timeout.Infinite is a constant field with a value of -1.

Upvotes: 23

Gusdor
Gusdor

Reputation: 14332

I hope I'm reading the question correctly here...

return new System.Threading.Tasks.TaskCompletionSource<object>().Task;
  1. Return the task created by a Task Completion Source.
  2. Never set the source to complete.

Continuations will never be invoked and await will 'block' forever.

Upvotes: 17

Related Questions