Skip to content

Fix some cooperative thread limitations#656

Open
lukewagner wants to merge 1 commit into
mainfrom
fix-coop
Open

Fix some cooperative thread limitations#656
lukewagner wants to merge 1 commit into
mainfrom
fix-coop

Conversation

@lukewagner
Copy link
Copy Markdown
Member

@TartanLlama's work implementing pthreads in wasi-libc highlighted a few limitations with cooperative threads as currently spec'd that this PR intends to address:

  • If pthreads are implemented using cooperative threads in sync-typed context, they won't work (due to trapping or no-op yielding) in cases where, if they had instead been implemented via CPS transform (like Asyncify), they could have.
  • pthread APIs like pthread_joinwould like to be able to switch to a thread if that thread is ready to run, but not trap if it's not ready-to-run, b/c the current state of the thread isn't known.

The first bullet suggests replacing the strict "may_block" traps (and the no-op special case in thread.yield) with a more permissive approach where: if a thread blocks (now better-defined after #637) as part of a sync-typed call, but there are other threads in the same component instance that are ready-to-run, they are resumed directly, and repeatedly, until either there are no more ready threads (in which case trap, b/c it's actually blocked) or a value is returned successfully to the caller. This also implicitly addresses the limitation @badeend just pointed out in #651, since blocking is fine as long as there is some other thread that can make progress. (E.g., maybe the blocking call was just doing some asynchronous logging off the critical path.) The "in the same component instance" caveat matches the expressivity of a core wasm CPS transform and is also necessary to maintain the reentrance invariant (re)defined in #650.

This does have the unfortunate side effect of allowing more code to accidentally depend on non-portable behavior (whether various operations actually block in practice), but this is already quite possible when using the async ABI and it's sortof just the nature of concurrency.

The second bullet suggests first renaming (but otherwise keeping the exact same behavior):

  • thread.switch-to => thread.suspend-then-resume = resume other thread (trap if it's not "suspended"), leave current thread "suspended"
  • thread.yield-to => thread.yield-then-resume = resume other thread (trap if it's not "suspended"), leave current thread "ready"

and then symmetrically add two new variants:

  • thread.suspend-then-promote = resume other thread if it's "ready", leave current thread "suspended"
  • thread.yield-then-promote = resume other thread if it's "ready", leave current thread "ready"

Thus, if the other thread is not "ready", thread.{suspend,yield}-then-promote behave the same as thread.{suspend,yield}.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant