Search code examples
c#async-awaitazure-cosmosdb

How is `Task` run only when awaited in C#?


How is Task run only when awaited in C#?

E.g. in Azure Cosmos SDK there is the CreateItemAsync method. According to documentation here:

We don’t start the tasks yet; we add them to a list to start them later.

Task<ItemResponse> firstTask = container.CreateItemAsync(firstProduct, firstPartitionKey);

So, somehow the CreateItemAsync is implemented to run only when there is a respective await.

I checked the await documentation and did not find any instructions on how to run Task only when awaited.

I tried to find source code for the CreateItemAsync, here. But the implementation seems to be omitted and only the abstract method is visible on GitHub.

UPDATE

As was suggested in comments below I went over to my source code and tested it for different scenarios. Here are my findings:

([CosmosClientOptions.AllowBulkExecution] [await near the CreateItemAsync]) [item gets created]

(false, false) false

(false, true) true

(true, false) false

(true, true) true

So, as you can see irrespectively from what is the value of AllowBulkExecution, the await is necessary for the operation to happen.


Solution

  • I'm speculating here based on similar work in other systems; this is not a commentary on Cosmos SDK specifically.

    When we're talking about IO work, like talking to an external database, the task is not a CPU concept. There is no actual workload to perform locally. Instead, what usually happens is:

    • you create a TaskCompletionSource<T> or similar to represent the future value
    • you add some token (including the TCS) to a queue of things that need to be sent
    • you return the TCS .Task value back to the caller

    That's it: you're done, from the enqueue step.

    Somewhere else, the IO loop is dequeuing things that need to be sent, doing the IO work to send that to the server. Either immediately or on a second IO loop, the response will (eventually) be received, mapped back to a the pending incomplete operation token, and the TCS retrieved. Now that we know what happened, TrySetResult etc on the TCS is issued.

    So: everything the SDK says is correct, but: you need to stop thinking of the task as an operation. In this context, it is a "future". There is nothing to execute or schedule locally. It isn't that it only executes when it is awaited; nor is it the case that it was started immediately.