-
Notifications
You must be signed in to change notification settings - Fork 5.1k
Description
I've been working to support async
/await
in C# for doing concurrent I/O when targeting WASIp2. A key piece of that is the wasi:io/poll#poll
function, which accepts an arbitrary number of pollable
handles representing e.g. socket readiness, HTTP protocol events, timer events, etc. It's analogous to the traditional POSIX poll(2)
function. My goal is to provide a System.Threading.Tasks
-based abstraction on top of wasi:io/poll
that supports idiomatic use of async
/await
, including the various Task
combinators such as WhenAny
, WhenAll
, WhenEach
. I've done similar work for Python (using a custom asyncio
event loop) and Rust (using a custom async
runtime), and am hoping to do the same for .NET.
So far, I've managed to write a custom TaskScheduler which supports simple cases by maintaining a list of Task
s and a list of the Pollable
s those tasks are await
ing. It has a Run
method which, in a loop, alternates between the Task
list and the Pollable
list, executing the former and calling wasi:io/poll#poll
on the latter. That works well for simple cases.
However, I've had trouble building more sophisticated examples using e.g. the new Task.WhenEach
combinator due to the somewhat pervasive use of ThreadPool
as a deferred execution mechanism throughout the System.Threading.Tasks
library code. Given that WASI does not support multithreading and won't for the foreseeable future, ThreadPool
's methods currently throw System.PlatformNotSupportedException
, which makes it something of a landmine. For example, even though there's nothing inherently multithreaded about Task.WhenEach
, the current implementation relies on a ManualResetValueTaskSourceCore
with RunContinuationsAsynchronously
set to true, which means it always queues continuations using ThreadPool.UnsafeQueueUserWorkItem
.
Given that significant pieces of .NET's async
/await
infrastructure currently relies on multithreading to function, I'm wondering what our options are for WASI. A few come to mind:
- Give up on
async
/await
and use callbacks for concurrency in WASIp2. Besides being un-ergonomic, it would significantly restrict the set of both standard and third-party library features available for use on that platform, given that anything that deals with I/O is presumably either synchronous and multithreaded or asynchronous based onasync
/await
. - Support
async
/await
, but disallow use of features such asTask.WhenEach
which requireThreadPool
as an implementation detail, and possibly provide alternative implementations of those features which are single-thread-friendly. - Refactor the parts of
System.Threading.Tasks
which are not inherently multithreaded (but currently useThreadPool
) to support an alternative, single-threaded deferred execution mechanism on platforms that do not support multithreading. - Provide a WASI-specific
ThreadPool
implementation which simply queues work items without executing them, and provide some public API for running them from the main (and only) thread, e.g. as part of an application event loop.
Thoughts?
I should note that I'm quite new to the .NET ecosystem, so I'm happy to be corrected if I've misunderstood anything.
See also #98957, for which I would consider this issue a prerequisite.