Avoid delegate/work item allocations when setting async continuation (#22373)
When awaiting a task, there's a race between seeing whether the task has completed (in which case we just continue running synchronously), finding the task hasn't completed (in which case we hook up a continuation), and then by the time we try to hook up the continuation finding the task has already completed. In that final case, we don't want to just execute the callback synchronously, as we risk a stack dive, so we queue it. That queueing currently entails two allocations in the common case: one for the work item object, and one for the Action delegate we force into existence for the state machine box's MoveNext method (in the common case it's now never allocated because you only await Tasks and ValueTasks known to the runtime, which bypasses its creation). We can instead just queue the box itself, and avoid both allocations.