Skip to content

Task.WhenAny - avoid allocation when more than 2 tasks are provided #126748

@unsafePtr

Description

@unsafePtr

Description

When there are more than two Tasks passed to Task.WhenAny, an array is allocated as a protection measure.

// Make a defensive copy, as the user may manipulate the input collection
// after we return but before the WhenAny asynchronously completes.
TTask[] tasksCopy = tasks.ToArray();
foreach (TTask task in tasksCopy)

Would it be possible to use ArrayPool to eliminate this allocation in WhenAny? This could be beneficial in high-throughput async scenarios where WhenAny is called frequently, as it currently allocates a Task[] on every call with more than two tasks.

I've looked into the internals and it seems like this change could be straightforward to add — the rented array could be returned to the pool once the work is finished.

for (int i = 0; i < numTasks; i++)
{
TTask task = tasks[i];
if (task != null && // if an element was erroneously nulled out concurrently, just skip it; worst case is we don't remove a continuation
!task.IsCompleted) task.RemoveContinuation(this);
}
_tasks = null;

I'm happy to implement this change myself if the approach is considered feasible — would just appreciate approval before diving in. It may also be worth adding a dedicated fast path for exactly 3/4 tasks, similar to how 2 tasks is currently handled, to avoid the allocation entirely in that case as well.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions