diff --git a/design/mvp/CanonicalABI.md b/design/mvp/CanonicalABI.md
index 53c24303..e8255668 100644
--- a/design/mvp/CanonicalABI.md
+++ b/design/mvp/CanonicalABI.md
@@ -16,6 +16,7 @@ specified here.
     * [Component Instance State](#component-instance-state)
     * [Table State](#table-state)
     * [Resource State](#resource-state)
+    * [Stack Switching](#stack-switching)
     * [Thread State](#thread-state)
     * [Waitable State](#waitable-state)
     * [Task State](#task-state)
@@ -589,69 +590,216 @@ class ResourceType(Type):
 ```
 
 
-#### Thread State
+#### Stack Switching
 
-As described in the [concurrency explainer], threads are created both
-*implicitly*, when calling a component export (in `canon_lift` below), and
-*explicitly*, when core wasm code calls the `thread.new-indirect` built-in (in
-`canon_thread_new_indirect` below). Threads are represented here by the
-`Thread` class and the [current thread] is represented by explicitly threading
-a reference to a `Thread` through all Core WebAssembly calls so that the
-`thread` parameter always points to "the current thread". The `Thread` class
-provides a set of primitive control-flow operations that are used by the rest
-of the Canonical ABI definitions.
-
-While `Thread`s are semantically created for each component export call by the
-Python `canon_lift` code, an optimizing runtime should be able to allocate
-`Thread`s lazily, only when needed for actual thread switching operations,
-thereby avoiding cross-component call overhead for simple, short-running
-cross-component calls. To assist in this optimization, `Thread`s are put into
-their own per-component-instance `threads` table so that thread table indices
-and elements can be more-readily reused between calls without interference from
-the other kinds of handles.
-
-`Thread` is implemented using the Python standard library's [`threading`]
-module. While a Python [`threading.Thread`] is a preemptively-scheduled [kernel
-thread], it is coerced to behave like a cooperatively-scheduled [fiber] by
-careful use of [`threading.Lock`]. If Python had built-in fibers (or algebraic
-effects), those could have been used instead since all that's needed is the
-ability to switch stacks. In any case, the use of `threading.Thread` is
-encapsulated by the `Thread` class so that the rest of the Canonical ABI can
-simply use `suspend`, `resume`, etc.
-
-When a `Thread` is suspended and then resumed, it receives a `Cancelled`
-value indicating whether the caller has cooperatively requested that the thread
-cancel itself which is communicated to Core WebAssembly with the following
-integer values:
+Component Model concurrency is defined in terms of the Core WebAssembly
+[stack-switching] proposal's `cont.new`, `resume` and `suspend` instructions so
+that there is a clear composition story between Component Model and Core
+WebAssembly concurrency in the future. Since Python does not natively provide
+algebraic effects, `cont.new`, `resume` and `suspend` are implemented in this
+section in terms of other Python primitives. Thus, this section can be skimmed
+since the semantics of stack-switching are already rigorously defined elsewhere;
+it's only important to understand the Python function signatures and how they
+correspond to the stack-switching instructions.
+
+Since the Component Model only needs a subset of the full expressivity of
+stack-switching, only that subset is implemented, which significantly simplifies
+things. In particular, the Component Model uses stack-switching in the following
+restricted manner:
+
+First, there are only two global [control tags] used with `suspend`:
+```wat
+(tag $block (param $switch-to (ref null $Thread)) (result $cancelled bool))
+(tag $current-thread (result (ref $Thread)))
+```
+Consequently, instead of having a single generic Python `suspend()` function,
+there are `block()` and `current_thread()` Python functions that implement
+`suspend $block` and `suspend $current-thread`, resp.
+
+The `$block` tag is used to suspend a [thread] until some future event. The
+parameters and results will be described in the next section, where they are
+used to define `Thread`.
+
+The `$current-thread` tag is used to retrieve the [current thread], which is
+semantically stored in the `resume` handler's local state (although an
+optimizing implementation would instead maintain the current thread in the VM's
+execution context (or a special Core WebAssembly `global`) so that it could be
+cheaply loaded and/or kept in register state).
+
+Second, there is only a single type of continuation passed to `resume` that
+corresponds to the `$block` tag (`$current-thread` continuations are
+immediately resumed and never "escape"):
+```wat
+(type $ct (cont (func (param bool) (result (ref null $Thread)))))
+```
+
+Third, every `resume` performed by the Canonical ABI always handles both
+`$block` and `$current-thread` *and* every Canonical ABI `suspend` is, by
+construction, always scoped by a Canonical ABI `resume`. Thus, every Canonical
+ABI `suspend` unconditionally transfers control flow directly to the innermost
+enclosing Canonical ABI `resume` without a general handler/tag search.
+
+Given this restricted usage, specialized versions of `cont.new`, `resume` and
+`suspend` that are "monomorphized" to the above types and tags are implemented
+in terms of Python's standard preemptive threading primitives, using
+[`threading.Thread`] to provide a native stack, [`threading.Lock`] to only allow
+a single `threading.Thread` to execute at a time, and [`threading.local`] to
+maintain the dynamic handler scope using thread-local storage. This could have
+been implemented more directly and efficiently using [fibers], but the Python
+standard library doesn't have fibers. However, a realistic implementation is
+expected to use (a pool of) fibers.
+
+Starting with `cont.new`, the monomorphized version takes a function type
+matching `$ct`, as defined above:
 ```python
 class Cancelled(IntEnum):
   FALSE = 0
   TRUE = 1
-```
 
-Introducing the `Thread` class in chunks, a `Thread` has the following fields
-and can be in one of the following 3 states based on these fields:
-* `running`: actively executing with a "parent" thread that is waiting
-  to run once the `running` thread suspends or returns
-* `suspended`: waiting to be `resume`d by another thread
+class Continuation:
+  lock: threading.Lock
+  handler: Handler
+  cancelled: Cancelled
+
+class Handler:
+  thread_local = threading.local()
+  lock: threading.Lock
+  current: Thread
+  cont: Optional[Continuation]
+  switch_to: Optional[Thread]
+
+def new_already_acquired_lock() -> threading.Lock:
+  lock = threading.Lock()
+  lock.acquire()
+  return lock
+
+def cont_new(f: Callable[[Cancelled], Optional[Thread]]) -> Continuation:
+  cont = Continuation()
+  cont.lock = new_already_acquired_lock()
+  def thread_base():
+    cont.lock.acquire()
+    Handler.thread_local.value = cont.handler
+    switch_to = f(cont.cancelled)
+    handler = Handler.thread_local.value
+    handler.cont = None
+    handler.switch_to = switch_to
+    handler.lock.release()
+  threading.Thread(target = thread_base).start()
+  return cont
+```
+`Continuation.cancelled` and `Continuation.handler` are set by `resume` right
+before `resume` calls `Continuation.lock.release()` to transfer control flow to
+the continuation. After resuming the continuation, `resume` calls
+`Handler.lock.acquire()` to wait until the continuation signals suspension or
+return by calling `Handler.lock.release()`. The `Handler` is stored in the
+thread-local variable `Handler.thread_local.value` to implement the dynamic
+scoping that is required for `suspend`. Because the thread created by `cont_new`
+can be suspended and resumed many times (each time with a new `Continuation` and
+`Handler`, resp.), `Handler` must be re-loaded from `Handler.thread_local.value`
+after `f` returns since it may have changed since the initial `resume`.
+
+Next, `resume` is monomorphized to take a continuation of type `$ct`, the
+argument to pass to the continuation, and the `current` `Thread` to use to
+implement the `(on $current-thread)` handler. The remaining `(on $block)` and
+"returned" cases join to produce a single return value, with the `(on $block)`
+case returning a `Continuation` + the `$block` result and the "returned" case
+returning `None` + the continuation's return value.
+```python
+def resume(cont: Continuation, cancelled: Cancelled, current: Thread) -> \
+           tuple[Optional[Continuation], Optional[Thread]]:
+  handler = Handler()
+  handler.lock = new_already_acquired_lock()
+  handler.current = current
+  cont.handler = handler
+  cont.cancelled = cancelled
+  cont.lock.release()
+  handler.lock.acquire()
+  return (handler.cont, handler.switch_to)
+```
+
+Lastly, `suspend` is monomorphized into 2 functions for the `$block` and
+`$current-thread` tags defined above. Since `$current-thread` has a trivial
+handler that immediately `resume`s with the `current` `Thread`, it can simply
+return `Handler.current` without any stack switching.
+```python
+def block(switch_to: Optional[Thread]) -> Cancelled:
+  cont = Continuation()
+  cont.lock = new_already_acquired_lock()
+  handler = Handler.thread_local.value
+  handler.cont = cont
+  handler.switch_to = switch_to
+  handler.lock.release()
+  cont.lock.acquire()
+  Handler.thread_local.value = cont.handler
+  return cont.cancelled
+
+def current_thread() -> Thread:
+  return Handler.thread_local.value.current
+```
+
+Once Core WebAssembly gets stack-switching, the Component Model's `$block` and
+`$current-thread` tags would *not* be exposed to Core WebAssembly. Thus, an
+optimizing implementation would continue to be able to implement `block()` as a
+direct control flow transfer and `current_thread()` with implicit execution
+context, both without a general handler/tag search. In particular, this avoids
+the pathological O(N<sup>2</sup>) behavior which would otherwise arise if
+Component Model cooperative threads were used in conjunction with deeply-nested
+Core WebAssembly handlers.
+
+Additionally, once Core WebAssembly has stack switching, any unhandled events
+that originate in Core WebAssembly would turn into traps if they reach a
+component boundary (just like unhandled exceptions do now; see
+`call_and_trap_on_throw` below). Thus, all cross-component/cross-language stack
+switching would continue to be mediated by the Component Model's types and
+Canonical ABI, with Core WebAssembly stack-switching used to implement
+intra-component concurrency according to the language's own internal ABI.
+
+
+#### Thread State
+
+As described in the [concurrency explainer], threads are created both
+*implicitly*, when calling a component export (in `canon_lift` below), and
+*explicitly*, when core wasm code calls the `thread.new-indirect` built-in (in
+`canon_thread_new_indirect` below). While threads are *logically* created for
+each component export call, an optimizing runtime should be able to allocate
+threads lazily when needed for actual thread switching operations, thereby
+avoiding cross-component call overhead for simple, short-running cross-component
+calls. To assist in this optimization, threads are put into their own
+`ComponentInstance.threads` table to reduce interference from the other kinds of
+handles.
+
+Threads are represented in the Canonical ABI by the `Thread` class defined in
+this section. The `Thread` class is implemented in terms of the stack-switching
+primitives defined in the previous section and contains an optional mutable
+continuation that is set and cleared whenever a thread is suspended and resumed,
+resp. On top of the basic `suspend` and `resume` operations, `Thread` adds the
+following higher-level concurrency concepts:
+* [waiting on external I/O and yielding]
+* [async call stack]
+* [cancellation]
+* [thread index]
+* [thread-local storage]
+
+Introducing the `Thread` class in chunks, a `Thread` can be in one of the
+following 3 states:
+* `running`: actively executing on the stack (thus having no continuation)
+* `suspended`: waiting to be `resume`d by another thread `running` in
+  the same component instance
 * `waiting`: waiting to be `resume`d nondeterministically by the host after
   some condition is met, with `ready` and non-`ready` sub-states, depending on
   whether the condition is met.
 
 ```python
 class Thread:
-  task: Task
-  fiber: threading.Thread
-  fiber_lock: threading.Lock
-  parent_lock: Optional[threading.Lock]
+  cont: Optional[Continuation]
   ready_func: Optional[Callable[[], bool]]
+  task: Task
   cancellable: bool
-  cancelled: Cancelled
   index: Optional[int]
   storage: tuple[int,int]
 
   def running(self):
-    return self.parent_lock is not None
+    return self.cont is None
 
   def suspended(self):
     return not self.running() and self.ready_func is None
@@ -663,55 +811,25 @@ class Thread:
     assert(self.waiting())
     return self.ready_func()
 ```
-The `index` field stores the index of the thread in its containing component
-instance's `threads` table and is initialized only once a thread is allowed to
-start executing (after the backpressure gate). The `storage` field holds the
-[thread-local storage] accessed by the `context.{get,set}` built-ins. All the
-other fields are used directly by `Thread` methods as shown next.
 
-When a `Thread` is created, an internal `threading.Thread` is started and
-immediately blocked `acquire()`ing `fiber_lock` (which will be `release()`ed by
-`Thread.resume`, defined next).
+When a `Thread` is created, a new continuation is created for `thread_func`
+(wrapping the `thread_func` with `cont_func` to exactly match the function type
+expected by `cont_new`) and leaving the thread initially in the `suspended`
+state.
 ```python
   def __init__(self, task, thread_func):
-    self.task = task
-    self.fiber_lock = threading.Lock()
-    self.fiber_lock.acquire()
-    self.parent_lock = None
+    def cont_func(cancelled):
+      assert(self.running() and not cancelled)
+      thread_func()
+      return None
+    self.cont = cont_new(cont_func)
     self.ready_func = None
+    self.task = task
     self.cancellable = False
-    self.cancelled = Cancelled.FALSE
     self.index = None
     self.storage = [0,0]
-    def fiber_func():
-      self.fiber_lock.acquire()
-      thread_func(self)
-      self.parent_lock.release()
-    self.fiber = threading.Thread(target = fiber_func)
-    self.fiber.start()
     assert(self.suspended())
 ```
-Once a `Thread` is created, it will only start `running` when `Thread.resume`
-is called. Once a thread is `running` it can then be `suspended` again by
-calling `Thread.suspend`, after which it can be resumed again and suspended
-again, etc, until the thread exits.
-
-When resuming, the thread calling `Thread.resume` blocks until the resumed
-thread either calls `Thread.suspend` or exits (just like the `resume`
-instruction in the Core WebAssembly [stack-switching] proposal). This waiting
-is accomplished using the `parent_lock` field of the resumed thread, which the
-resumed thread will `release()` when it suspends or exits.
-
-When a thread calls `Thread.suspend`, it indicates whether it is able to handle
-cancellation. This information is stored in the `cancellable` field which is
-used by `Task.request_cancellation` (defined below) to only `resume` with
-`Cancelled.TRUE` when the thread expects it.
-
-Lastly, several `Thread` methods below will set the `ready_func` and add the
-`Thread` to the `Store.waiting` list so that `Store.tick` will call `resume`
-when the `ready_func` returns `True`. Once `Thread.resume` is called, the
-`ready_func` is reset and the `Thread` is removed again from the
-`Store.waiting` list since it's no longer in the `waiting` state.
 
 One way to allow a newly-created thread to start executing is for core wasm to
 call the `thread.resume-later` built-in. This built-in does not immediately
@@ -726,11 +844,13 @@ above) to nondeterministically call `Thread.resume` (defined next).
     assert(self.ready())
 ```
 
-Once its time to resume a `suspended` or `waiting` thread, the `Thread.resume`
+Once its time to execute a `suspended` or `waiting` thread, the `Thread.resume`
 method is called on that thread. This method transitions the thread to the
-`running` state by setting its `parent_lock` to an already-`acquire()`ed lock
-that is then `acquire()`ed again by the calling thread. This makes the current
-thread into the parent thread of the thread being resumed.
+`running` state by clearing and then `resume`ing the `Thread`'s stored
+continuation. If the `resume`d continuation suspends with a `Thread` to
+`switch_to`, `Thread.resume` will `resume` *that* `Thread`'s continuation, and
+so on, repeatedly, until the continuation either returns or suspends with no
+thread to `switch_to`.
 ```python
   def resume(self, cancelled):
     assert(not self.running() and (self.cancellable or not cancelled))
@@ -738,34 +858,38 @@ thread into the parent thread of the thread being resumed.
       assert(cancelled or self.ready())
       self.ready_func = None
       self.task.inst.store.waiting.remove(self)
-    assert(self.cancellable or not cancelled)
-    self.cancelled = cancelled
-    self.parent_lock = threading.Lock()
-    self.parent_lock.acquire()
-    self.fiber_lock.release()
-    self.parent_lock.acquire()
-    self.parent_lock = None
-    assert(not self.running())
-```
-A thread is resumed with a `Cancelled` value that indicates whether the caller
-is requesting cancellation. This `Cancelled` value is temporarily stored in the
-`Thread.cancelled` field for delivery to core wasm once the internal Python
-thread actually resumes execution.
-
-Once a thread is `resume()`ed and starts executing, it can be suspended by
-calling the `thread.suspend` built-in which calls `Thread.suspend` here. This
-method unblocks the parent thread by `release()`ing `parent_lock` and then
-blocking the current thread by `acquire()`ing `fiber_lock`.
+    thread = self
+    while thread is not None:
+      cont = thread.cont
+      thread.cont = None
+      (thread.cont, switch_to) = resume(cont, cancelled, thread)
+      thread = switch_to
+      cancelled = Cancelled.FALSE
+```
+The `Thread.resume` method propagates cancellation requests (from
+`Task.request_cancellation`) to the first continuation that is resumed, allowing
+a thread that opted-in to being `cancellable` to be `resume`d even if it's not
+`ready`.
+
+Note that the `while` loop shown above is effectively implementing the `switch`
+instruction of the [stack-switching] proposal since `switch` is just an
+optimization of `suspend` followed by `resume`. The non-optimized version is
+used here to simplify storing of the new `Continuation` into `Thread.cont`.
+However, an optimized implementation could do the direct switch.
+
+Once a thread is `Thread.resume()`ed and starts executing, it can suspend its
+execution by calling the `thread.suspend` built-in which calls `Thread.suspend`
+here which transfers control flow back to `Thread.resume()` via `block()`. The
+`switch_to` argument `None` tells `Thread.resume()` to return immediately.
 ```python
   def suspend(self, cancellable) -> Cancelled:
     assert(self.running() and self.task.may_block())
     if self.task.deliver_pending_cancel(cancellable):
       return Cancelled.TRUE
     self.cancellable = cancellable
-    self.parent_lock.release()
-    self.fiber_lock.acquire()
-    assert(self.running() and (cancellable or not self.cancelled))
-    return self.cancelled
+    cancelled = block(switch_to = None)
+    assert(self.running() and (cancellable or not cancelled))
+    return cancelled
 ```
 The `cancellable` parameter of `Thread.suspend` indicates whether the caller is
 prepared to handle cancellation. If `cancellable` is false for all of a task's
@@ -822,23 +946,17 @@ The `Thread.switch_to` method is used by the `thread.switch-to` built-in to
 suspend the current thread and immediately transfer execution to some other
 `suspended` thread in the same component instance. Like the other methods,
 before anything else, `Thread.switch_to` reports any pending cancellation if the
-caller is `cancellable`. When switching, the parent of the current thread is
-transferred to the thread being resumed.
+caller is `cancellable`. Then the current thread transfers control flow back to
+`Thread.resume()`, passing the `other` `Thread` to `switch_to`.
 ```python
   def switch_to(self, cancellable, other: Thread) -> Cancelled:
     assert(self.running() and other.suspended())
     if self.task.deliver_pending_cancel(cancellable):
       return Cancelled.TRUE
     self.cancellable = cancellable
-    other.cancelled = Cancelled.FALSE
-    assert(self.parent_lock and not other.parent_lock)
-    other.parent_lock = self.parent_lock
-    self.parent_lock = None
-    assert(not self.running() and other.running())
-    other.fiber_lock.release()
-    self.fiber_lock.acquire()
-    assert(self.running() and (cancellable or not self.cancelled))
-    return self.cancelled
+    cancelled = block(switch_to = other)
+    assert(self.running() and (cancellable or not cancelled))
+    return cancelled
 ```
 
 Lastly, the `Thread.yield_to` method is used by the `thread.yield-to` built-in
@@ -959,22 +1077,22 @@ class WaitableSet:
       if w.has_pending_event():
         return w.get_pending_event()
 
-  def wait_until(self, ready_func, thread, cancellable) -> EventTuple:
+  def wait_until(self, ready_func, cancellable) -> EventTuple:
     def ready_and_has_event():
       return ready_func() and self.has_pending_event()
     self.num_waiting += 1
-    if thread.wait_until(ready_and_has_event, cancellable) == Cancelled.TRUE:
+    if current_thread().wait_until(ready_and_has_event, cancellable) == Cancelled.TRUE:
       event = (EventCode.TASK_CANCELLED, 0, 0)
     else:
       event = self.get_pending_event()
     self.num_waiting -= 1
     return event
 
-  def wait(self, thread, cancellable) -> EventTuple:
-    return self.wait_until(lambda: True, thread, cancellable)
+  def wait(self, cancellable) -> EventTuple:
+    return self.wait_until(lambda: True, cancellable)
 
-  def poll(self, thread, cancellable) -> EventTuple:
-    if thread.task.deliver_pending_cancel(cancellable):
+  def poll(self, cancellable) -> EventTuple:
+    if current_thread().task.deliver_pending_cancel(cancellable):
       return (EventCode.TASK_CANCELLED, 0, 0)
     elif not self.has_pending_event():
       return (EventCode.NONE, 0, 0)
@@ -1010,11 +1128,11 @@ exported function and transitively including additional threads spawned by that
 thread via `thread.new-indirect`.
 
 Tasks are represented here by the `Task` class and the [current task] is
-represented by the `Thread.task` field of the [current thread]. `Task`
-implements the abstract `Call` and `Supertask` interfaces defined as part of
-the [Embedding](#embedding) interface since `Task` serves as both the
-`Supertask` of calls it makes to imports as well as the `Call` object returned
-for calls to exports.
+represented by the `Thread.task` field of `current_thread()`. `Task` implements
+the abstract `Call` and `Supertask` interfaces defined as part of the
+[Embedding](#embedding) interface since `Task` serves as both the `Supertask` of
+calls it makes to imports as well as the `Call` object returned for calls to
+exports.
 
 `Task` is introduced in chunks, starting with fields and initialization:
 ```python
@@ -1090,7 +1208,8 @@ careful to handle this possibility (e.g., while maintaining the linear-memory
 shadow stack pointer) for components with mixed `async`- and non-`async`- typed
 exports.
 ```python
-  def enter(self, thread):
+  def enter(self):
+    thread = current_thread()
     if self.ft.async_:
       def has_backpressure():
         return self.inst.backpressure > 0 or (self.needs_exclusive() and bool(self.inst.exclusive))
@@ -1116,10 +1235,10 @@ exports.
     thread.index = self.inst.threads.add(thread)
 ```
 Since the order in which suspended threads are resumed is nondeterministic (see
-`Store.tick` above), once `Task.enter` suspends the [current thread] due to
-backpressure, the above definition allows the host to arbitrarily select which
-threads to resume in which order. Additionally, the above definition ensures
-the following properties:
+`Store.tick` above), once `Task.enter` suspends the task's implicit thread due
+to backpressure, the above definition allows the host to arbitrarily select
+which threads to resume in which order. Additionally, the above definition
+ensures the following properties:
 * While a callee is waiting to `enter`, if the caller requests cancellation,
   the callee is immediately cancelled. The `Task.waiting_to_enter` field is
   used by `Task.request_cancellation` below to know which thread to
@@ -1140,8 +1259,8 @@ task to start. `Task.unregister_thread` (which is also called by
 `thread.new-indirect`, below) traps if the task's last thread is unregistered
 and the task has not yet returned a value to its caller.
 ```python
-  def exit(self, thread):
-    self.unregister_thread(thread)
+  def exit(self):
+    self.unregister_thread(current_thread())
     if self.ft.async_ and self.needs_exclusive():
       assert(self.inst.exclusive is self)
       self.inst.exclusive = None
@@ -3309,8 +3428,8 @@ a `lift`ed function starts executing:
 def canon_lift(opts, inst, ft, callee, caller, on_start, on_resolve) -> Call:
   trap_if(call_might_be_recursive(caller, inst))
   task = Task(opts, inst, ft, caller, on_resolve)
-  def thread_func(thread):
-    if not task.enter(thread):
+  def thread_func():
+    if not task.enter():
       return
 
     cx = LiftLowerContext(opts, inst, task)
@@ -3343,15 +3462,15 @@ passing the same core wasm results as parameters so that the `post-return`
 function can free any associated allocations.
 ```python
     if not opts.async_:
-      flat_results = call_and_trap_on_throw(callee, thread, flat_args)
+      flat_results = call_and_trap_on_throw(callee, flat_args)
       assert(types_match_values(flat_ft.results, flat_results))
       result = lift_flat_values(cx, MAX_FLAT_RESULTS, CoreValueIter(flat_results), ft.result_type())
       task.return_(result)
       if opts.post_return is not None:
         inst.may_leave = False
-        [] = call_and_trap_on_throw(opts.post_return, thread, flat_results)
+        [] = call_and_trap_on_throw(opts.post_return, flat_results)
         inst.may_leave = True
-      task.exit(thread)
+      task.exit()
       return
 ```
 By clearing `may_leave` for the duration of the `post-return` call, the
@@ -3375,9 +3494,9 @@ by a stackful async function do not prevent any other threads from starting or
 resuming in the same component instance.
 ```python
     if not opts.callback:
-      [] = call_and_trap_on_throw(callee, thread, flat_args)
+      [] = call_and_trap_on_throw(callee, flat_args)
       assert(types_match_values(flat_ft.results, []))
-      task.exit(thread)
+      task.exit()
       return
 ```
 
@@ -3386,7 +3505,7 @@ first calling the core wasm callee and then repeatedly calling the `callback`
 function (specified as a `funcidx` immediate in `canon lift`) until the
 `EXIT` code (`0`) is returned:
 ```python
-    [packed] = call_and_trap_on_throw(callee, thread, flat_args)
+    [packed] = call_and_trap_on_throw(callee, flat_args)
     code,si = unpack_callback_result(packed)
     while code != CallbackCode.EXIT:
       assert(inst.exclusive is task)
@@ -3401,15 +3520,15 @@ function (specified as a `funcidx` immediate in `canon lift`) until the
           trap_if(not task.may_block())
           wset = inst.handles.get(si)
           trap_if(not isinstance(wset, WaitableSet))
-          event = wset.wait_until(lambda: not inst.exclusive, thread, cancellable = True)
+          event = wset.wait_until(lambda: not inst.exclusive, cancellable = True)
         case _:
           trap()
       assert(inst.exclusive is None)
       inst.exclusive = task
       event_code, p1, p2 = event
-      [packed] = call_and_trap_on_throw(opts.callback, thread, [event_code, p1, p2])
+      [packed] = call_and_trap_on_throw(opts.callback, [event_code, p1, p2])
       code,si = unpack_callback_result(packed)
-    task.exit(thread)
+    task.exit()
     return
 ```
 The `Thread.{wait,yield}_until` methods called by the event loop are the same
@@ -3472,14 +3591,15 @@ optimization to avoid allocating stacks for async languages that have avoided
 the need for stackful coroutines by design (e.g., `async`/`await` in JS,
 Python, C# and Rust).
 
-Uncaught Core WebAssembly [exceptions] result in a trap at component
-boundaries. Thus, if a component wishes to signal an error, it must use some
-sort of explicit type such as `result` (whose `error` case particular language
-bindings may choose to map to and from exceptions):
+Uncaught Core WebAssembly [exceptions] or, in a future with [stack-switching],
+unhandled events, result in a trap at component boundaries. Thus, if a component
+wishes to signal an error, it must use some sort of explicit type such as
+`result` (whose `error` case particular language bindings may choose to map to
+and from exceptions):
 ```python
-def call_and_trap_on_throw(callee, thread, args):
+def call_and_trap_on_throw(callee, args):
   try:
-    return callee(thread, args)
+    return callee(args)
   except CoreWebAssemblyException:
     trap()
 ```
@@ -3503,14 +3623,13 @@ validation is performed where `$callee` has type `$ft`:
 * 🔀 if `async` is specified, `memory` must be present
 
 When instantiating component instance `$inst`, `$f` is defined to be the
-partially-bound closure `canon_lower($opts, $ft, $callee)` which has two
-remaining arguments passed at runtime:
-* `thread`, the [current thread]
-* `flat_args`, a list of core wasm values passed by the caller
-
-Based on this, `canon_lower` is defined in chunks as follows:
+partially-bound closure `canon_lower($opts, $ft, $callee)` which leaves only
+the `flat_args` remaining argument to be passed at runtime, which contains the
+list of core wasm values that are passed by the core wasm caller. Based on
+this, `canon_lower` is defined in chunks as follows:
 ```python
-def canon_lower(opts, ft, callee: FuncInst, thread, flat_args):
+def canon_lower(opts, ft, callee: FuncInst, flat_args):
+  thread = current_thread()
   trap_if(not thread.task.inst.may_leave)
   trap_if(not thread.task.may_block() and ft.async_ and not opts.async_)
 ```
@@ -3664,10 +3783,11 @@ Calling `$f` invokes the following function, which adds an owning handle
 containing the given resource representation to the current component
 instance's `handles` table:
 ```python
-def canon_resource_new(rt, thread, rep):
-  trap_if(not thread.task.inst.may_leave)
+def canon_resource_new(rt, rep):
+  inst = current_thread().task.inst
+  trap_if(not inst.may_leave)
   h = ResourceHandle(rt, rep, own = True)
-  i = thread.task.inst.handles.add(h)
+  i = inst.handles.add(h)
   return [i]
 ```
 
@@ -3686,9 +3806,9 @@ Calling `$f` invokes the following function, which removes the handle from the
 current component instance's `handles` table and, if the handle was owning,
 calls the resource's destructor.
 ```python
-def canon_resource_drop(rt, thread, i):
-  trap_if(not thread.task.inst.may_leave)
-  inst = thread.task.inst
+def canon_resource_drop(rt, i):
+  inst = current_thread().task.inst
+  trap_if(not inst.may_leave)
   h = inst.handles.remove(i)
   trap_if(not isinstance(h, ResourceHandle))
   trap_if(h.rt is not rt)
@@ -3702,9 +3822,9 @@ def canon_resource_drop(rt, thread, i):
       caller_opts = CanonicalOptions(async_ = False)
       callee_opts = CanonicalOptions(async_ = rt.dtor_async, callback = rt.dtor_callback)
       ft = FuncType([U32Type()],[], async_ = False)
-      dtor = rt.dtor or (lambda thread, rep: [])
+      dtor = rt.dtor or (lambda rep: [])
       callee = partial(canon_lift, callee_opts, rt.impl, ft, dtor)
-      [] = canon_lower(caller_opts, ft, callee, thread, [h.rep])
+      [] = canon_lower(caller_opts, ft, callee, [h.rep])
   else:
     h.borrow_scope.num_borrows -= 1
   return []
@@ -3742,8 +3862,8 @@ Calling `$f` invokes the following function, which extracts the resource
 representation from the handle in the current component instance's `handles`
 table:
 ```python
-def canon_resource_rep(rt, thread, i):
-  h = thread.task.inst.handles.get(i)
+def canon_resource_rep(rt, i):
+  h = current_thread().task.inst.handles.get(i)
   trap_if(not isinstance(h, ResourceHandle))
   trap_if(h.rt is not rt)
   return [h.rep]
@@ -3769,7 +3889,8 @@ storage] of the [current thread], taking only the low 32-bits if `$t` is `i32`:
 ```python
 MASK_32BIT = (1 << 32) - 1
 
-def canon_context_get(t, i, thread):
+def canon_context_get(t, i):
+  thread = current_thread()
   assert(t == 'i32' or t == 'i64')
   assert(i < len(thread.storage))
   result = thread.storage[i]
@@ -3794,7 +3915,8 @@ validation specifies:
 Calling `$f` invokes the following function, which writes to the [thread-local
 storage] of the [current thread]:
 ```python
-def canon_context_set(t, i, thread, v):
+def canon_context_set(t, i, v):
+  thread = current_thread()
   assert(t == 'i32' or t == 'i64')
   assert(v <= MASK_32BIT or t == 'i64')
   assert(i < len(thread.storage))
@@ -3821,9 +3943,9 @@ Calling `$f` invokes the following function, which sets the `backpressure`
 counter to `1` or `0`. `Task.enter` waits for `backpressure` to be `0` before
 allowing new tasks to start.
 ```python
-def canon_backpressure_set(thread, flat_args):
+def canon_backpressure_set(flat_args):
   assert(len(flat_args) == 1)
-  thread.task.inst.backpressure = int(bool(flat_args[0]))
+  current_thread().task.inst.backpressure = int(bool(flat_args[0]))
   return []
 ```
 
@@ -3839,16 +3961,18 @@ validation specifies:
 
 Calling `$inc` or `$dec` invokes one of the following functions:
 ```python
-def canon_backpressure_inc(thread):
-  assert(0 <= thread.task.inst.backpressure < 2**16)
-  thread.task.inst.backpressure += 1
-  trap_if(thread.task.inst.backpressure == 2**16)
+def canon_backpressure_inc():
+  inst = current_thread().task.inst
+  assert(0 <= inst.backpressure < 2**16)
+  inst.backpressure += 1
+  trap_if(inst.backpressure == 2**16)
   return []
 
-def canon_backpressure_dec(thread):
-  assert(0 <= thread.task.inst.backpressure < 2**16)
-  thread.task.inst.backpressure -= 1
-  trap_if(thread.task.inst.backpressure < 0)
+def canon_backpressure_dec():
+  inst = current_thread().task.inst
+  assert(0 <= inst.backpressure < 2**16)
+  inst.backpressure -= 1
+  trap_if(inst.backpressure < 0)
   return []
 ```
 `Task.enter` waits for `backpressure` to return to `0` before allowing new
@@ -3872,8 +3996,8 @@ specifies:
 Calling `$f` invokes the following function which lifts the results from core
 wasm state and passes them to the [current task]'s caller via `Task.return_`:
 ```python
-def canon_task_return(thread, result_type, opts: LiftOptions, flat_args):
-  task = thread.task
+def canon_task_return(result_type, opts: LiftOptions, flat_args):
+  task = current_thread().task
   trap_if(not task.inst.may_leave)
   trap_if(not task.opts.async_)
   trap_if(result_type != task.ft.result)
@@ -3919,8 +4043,8 @@ Calling `$f` cancels the [current task], confirming a previous `subtask.cancel`
 request made by a supertask and claiming that all `borrow` handles lent to the
 [current task] have already been dropped (and trapping in `Task.cancel` if not).
 ```python
-def canon_task_cancel(thread):
-  task = thread.task
+def canon_task_cancel():
+  task = current_thread().task
   trap_if(not task.inst.may_leave)
   trap_if(not task.opts.async_)
   task.cancel()
@@ -3947,9 +4071,11 @@ validation specifies:
 Calling `$f` invokes the following function, which adds an empty waitable set
 to the current component instance's `handles` table:
 ```python
-def canon_waitable_set_new(thread):
-  trap_if(not thread.task.inst.may_leave)
-  return [ thread.task.inst.handles.add(WaitableSet()) ]
+def canon_waitable_set_new():
+  inst = current_thread().task.inst
+  trap_if(not inst.may_leave)
+  i = inst.handles.add(WaitableSet())
+  return [i]
 ```
 
 
@@ -3968,17 +4094,18 @@ Calling `$f` invokes the following function which waits for progress to be made
 on a `Waitable` in the given waitable set (indicated by index `$si`) and then
 returning its `EventCode` and writing the payload values into linear memory:
 ```python
-def canon_waitable_set_wait(cancellable, mem, thread, si, ptr):
-  trap_if(not thread.task.inst.may_leave)
-  trap_if(not thread.task.may_block())
-  wset = thread.task.inst.handles.get(si)
+def canon_waitable_set_wait(cancellable, mem, si, ptr):
+  task = current_thread().task
+  trap_if(not task.inst.may_leave)
+  trap_if(not task.may_block())
+  wset = task.inst.handles.get(si)
   trap_if(not isinstance(wset, WaitableSet))
-  event = wset.wait(thread, cancellable)
-  return unpack_event(mem, thread, ptr, event)
+  event = wset.wait(cancellable)
+  return unpack_event(mem, task.inst, ptr, event)
 
-def unpack_event(mem, thread, ptr, e: EventTuple):
+def unpack_event(mem, inst, ptr, e: EventTuple):
   event, p1, p2 = e
-  cx = LiftLowerContext(LiftLowerOptions(memory = mem), thread.task.inst)
+  cx = LiftLowerContext(LiftLowerOptions(memory = mem), inst)
   store(cx, p1, U32Type(), ptr)
   store(cx, p2, U32Type(), ptr + 4)
   return [event]
@@ -3987,9 +4114,6 @@ A non-`async`-typed function export that has not yet returned a value
 unconditionally traps if it transitively attempts to call `wait` (regardless of
 whether there are any waitables with pending events).
 
-The `lambda: True` passed to `wait_until` means that `wait_until` will only
-wait for the given `wset` to have a pending event with no extra conditions.
-
 If `cancellable` is set, then `waitable-set.wait` will return whether the
 supertask has already or concurrently requested cancellation.
 `waitable-set.wait` (and other cancellable operations) will only indicate
@@ -4013,12 +4137,13 @@ Calling `$f` invokes the following function, which either returns an event that
 was pending on one of the waitables in the given waitable set (the same way as
 `waitable-set.wait`) or, if there is none, returns `0`.
 ```python
-def canon_waitable_set_poll(cancellable, mem, thread, si, ptr):
-  trap_if(not thread.task.inst.may_leave)
-  wset = thread.task.inst.handles.get(si)
+def canon_waitable_set_poll(cancellable, mem, si, ptr):
+  inst = current_thread().task.inst
+  trap_if(not inst.may_leave)
+  wset = inst.handles.get(si)
   trap_if(not isinstance(wset, WaitableSet))
-  event = wset.poll(thread, cancellable)
-  return unpack_event(mem, thread, ptr, event)
+  event = wset.poll(cancellable)
+  return unpack_event(mem, inst, ptr, event)
 ```
 If `cancellable` is set, then `waitable-set.poll` will return whether the
 supertask has already or concurrently requested cancellation.
@@ -4041,9 +4166,10 @@ Calling `$f` invokes the following function, which removes the indicated
 waitable set from the current component instance's `handles` table, performing
 the guards defined by `WaitableSet.drop` above:
 ```python
-def canon_waitable_set_drop(thread, i):
-  trap_if(not thread.task.inst.may_leave)
-  wset = thread.task.inst.handles.remove(i)
+def canon_waitable_set_drop(i):
+  inst = current_thread().task.inst
+  trap_if(not inst.may_leave)
+  wset = inst.handles.remove(i)
   trap_if(not isinstance(wset, WaitableSet))
   wset.drop()
   return []
@@ -4062,18 +4188,19 @@ For a canonical definition:
 validation specifies:
 * `$f` is given type `(func (param $wi i32) (param $si i32))`
 
-Calling `$f` invokes the following function which adds the Waitable indicated
+Calling `$f` invokes the following function which adds the waitable indicated
 by the index `wi` to the waitable set indicated by the index `si`, removing the
 waitable from any waitable set that it is currently a member of.
 ```python
-def canon_waitable_join(thread, wi, si):
-  trap_if(not thread.task.inst.may_leave)
-  w = thread.task.inst.handles.get(wi)
+def canon_waitable_join(wi, si):
+  inst = current_thread().task.inst
+  trap_if(not inst.may_leave)
+  w = inst.handles.get(wi)
   trap_if(not isinstance(w, Waitable))
   if si == 0:
     w.join(None)
   else:
-    wset = thread.task.inst.handles.get(si)
+    wset = inst.handles.get(si)
     trap_if(not isinstance(wset, WaitableSet))
     w.join(wset)
   return []
@@ -4115,7 +4242,8 @@ the event payload of a future `SUBTASK` event.
 ```python
 BLOCKED = 0xffff_ffff
 
-def canon_subtask_cancel(async_, thread, i):
+def canon_subtask_cancel(async_, i):
+  thread = current_thread()
   trap_if(not thread.task.inst.may_leave)
   trap_if(not thread.task.may_block() and not async_)
   subtask = thread.task.inst.handles.get(i)
@@ -4165,9 +4293,10 @@ Calling `$f` removes the subtask at the given index from the current component
 instance's `handles` table, performing the guards and bookkeeping defined by
 `Subtask.drop()`.
 ```python
-def canon_subtask_drop(thread, i):
-  trap_if(not thread.task.inst.may_leave)
-  s = thread.task.inst.handles.remove(i)
+def canon_subtask_drop(i):
+  inst = current_thread().task.inst
+  trap_if(not inst.may_leave)
+  s = inst.handles.remove(i)
   trap_if(not isinstance(s, Subtask))
   s.drop()
   return []
@@ -4194,18 +4323,20 @@ the readable end is subsequently transferred to another component (or the host)
 via `stream` or `future` parameter/result type (see `lift_{stream,future}`
 above).
 ```python
-def canon_stream_new(stream_t, thread):
-  trap_if(not thread.task.inst.may_leave)
+def canon_stream_new(stream_t):
+  inst = current_thread().task.inst
+  trap_if(not inst.may_leave)
   shared = SharedStreamImpl(stream_t.t)
-  ri = thread.task.inst.handles.add(ReadableStreamEnd(shared))
-  wi = thread.task.inst.handles.add(WritableStreamEnd(shared))
+  ri = inst.handles.add(ReadableStreamEnd(shared))
+  wi = inst.handles.add(WritableStreamEnd(shared))
   return [ ri | (wi << 32) ]
 
-def canon_future_new(future_t, thread):
-  trap_if(not thread.task.inst.may_leave)
+def canon_future_new(future_t):
+  inst = current_thread().task.inst
+  trap_if(not inst.may_leave)
   shared = SharedFutureImpl(future_t.t)
-  ri = thread.task.inst.handles.add(ReadableFutureEnd(shared))
-  wi = thread.task.inst.handles.add(WritableFutureEnd(shared))
+  ri = inst.handles.add(ReadableFutureEnd(shared))
+  wi = inst.handles.add(WritableFutureEnd(shared))
   return [ ri | (wi << 32) ]
 ```
 
@@ -4231,13 +4362,13 @@ specifies:
 The implementation of these built-ins funnels down to a single `stream_copy`
 function that is parameterized by the direction of the copy:
 ```python
-def canon_stream_read(stream_t, opts, thread, i, ptr, n):
+def canon_stream_read(stream_t, opts, i, ptr, n):
   return stream_copy(ReadableStreamEnd, WritableBufferGuestImpl, EventCode.STREAM_READ,
-                     stream_t, opts, thread, i, ptr, n)
+                     stream_t, opts, i, ptr, n)
 
-def canon_stream_write(stream_t, opts, thread, i, ptr, n):
+def canon_stream_write(stream_t, opts, i, ptr, n):
   return stream_copy(WritableStreamEnd, ReadableBufferGuestImpl, EventCode.STREAM_WRITE,
-                     stream_t, opts, thread, i, ptr, n)
+                     stream_t, opts, i, ptr, n)
 ```
 
 Introducing the `stream_copy` function in chunks, a non-`async`-typed function
@@ -4245,7 +4376,8 @@ export that has not yet returned a value unconditionally traps if it
 transitively attempts to perform a synchronous `read` or `write` (regardless of
 whether the operation would have succeeded eagerly without blocking).
 ```python
-def stream_copy(EndT, BufferT, event_code, stream_t, opts, thread, i, ptr, n):
+def stream_copy(EndT, BufferT, event_code, stream_t, opts, i, ptr, n):
+  thread = current_thread()
   trap_if(not thread.task.inst.may_leave)
   trap_if(not thread.task.may_block() and not opts.async_)
 ```
@@ -4352,13 +4484,13 @@ specifies:
 The implementation of these built-ins funnels down to a single `future_copy`
 function that is parameterized by the direction of the copy:
 ```python
-def canon_future_read(future_t, opts, thread, i, ptr):
+def canon_future_read(future_t, opts, i, ptr):
   return future_copy(ReadableFutureEnd, WritableBufferGuestImpl, EventCode.FUTURE_READ,
-                     future_t, opts, thread, i, ptr)
+                     future_t, opts, i, ptr)
 
-def canon_future_write(future_t, opts, thread, i, ptr):
+def canon_future_write(future_t, opts, i, ptr):
   return future_copy(WritableFutureEnd, ReadableBufferGuestImpl, EventCode.FUTURE_WRITE,
-                     future_t, opts, thread, i, ptr)
+                     future_t, opts, i, ptr)
 ```
 
 Introducing the `future_copy` function in chunks, `future_copy` starts with the
@@ -4366,7 +4498,8 @@ same set of guards as `stream_copy` regarding whether suspension is allowed and
 parameters `i` and `ptr`. The only difference is that, with futures, the
 `Buffer` length is fixed to `1`.
 ```python
-def future_copy(EndT, BufferT, event_code, future_t, opts, thread, i, ptr):
+def future_copy(EndT, BufferT, event_code, future_t, opts, i, ptr):
+  thread = current_thread()
   trap_if(not thread.task.inst.may_leave)
   trap_if(not thread.task.may_block() and not opts.async_)
 
@@ -4439,19 +4572,20 @@ validation specifies:
 The implementation of these four built-ins all funnel down to a single
 parameterized `cancel_copy` function:
 ```python
-def canon_stream_cancel_read(stream_t, async_, thread, i):
-  return cancel_copy(ReadableStreamEnd, EventCode.STREAM_READ, stream_t, async_, thread, i)
+def canon_stream_cancel_read(stream_t, async_, i):
+  return cancel_copy(ReadableStreamEnd, EventCode.STREAM_READ, stream_t, async_, i)
 
-def canon_stream_cancel_write(stream_t, async_, thread, i):
-  return cancel_copy(WritableStreamEnd, EventCode.STREAM_WRITE, stream_t, async_, thread, i)
+def canon_stream_cancel_write(stream_t, async_, i):
+  return cancel_copy(WritableStreamEnd, EventCode.STREAM_WRITE, stream_t, async_, i)
 
-def canon_future_cancel_read(future_t, async_, thread, i):
-  return cancel_copy(ReadableFutureEnd, EventCode.FUTURE_READ, future_t, async_, thread, i)
+def canon_future_cancel_read(future_t, async_, i):
+  return cancel_copy(ReadableFutureEnd, EventCode.FUTURE_READ, future_t, async_, i)
 
-def canon_future_cancel_write(future_t, async_, thread, i):
-  return cancel_copy(WritableFutureEnd, EventCode.FUTURE_WRITE, future_t, async_, thread, i)
+def canon_future_cancel_write(future_t, async_, i):
+  return cancel_copy(WritableFutureEnd, EventCode.FUTURE_WRITE, future_t, async_, i)
 
-def cancel_copy(EndT, event_code, stream_or_future_t, async_, thread, i):
+def cancel_copy(EndT, event_code, stream_or_future_t, async_, i):
+  thread = current_thread()
   trap_if(not thread.task.inst.may_leave)
   trap_if(not thread.task.may_block() and not async_)
   e = thread.task.inst.handles.get(i)
@@ -4518,21 +4652,22 @@ the given index from the current component instance's `handles` table,
 performing the guards and bookkeeping defined by
 `{Readable,Writable}{Stream,Future}End.drop()` above.
 ```python
-def canon_stream_drop_readable(stream_t, thread, i):
-  return drop(ReadableStreamEnd, stream_t, thread, i)
+def canon_stream_drop_readable(stream_t, i):
+  return drop(ReadableStreamEnd, stream_t, i)
 
-def canon_stream_drop_writable(stream_t, thread, hi):
-  return drop(WritableStreamEnd, stream_t, thread, hi)
+def canon_stream_drop_writable(stream_t, hi):
+  return drop(WritableStreamEnd, stream_t, hi)
 
-def canon_future_drop_readable(future_t, thread, i):
-  return drop(ReadableFutureEnd, future_t, thread, i)
+def canon_future_drop_readable(future_t, i):
+  return drop(ReadableFutureEnd, future_t, i)
 
-def canon_future_drop_writable(future_t, thread, hi):
-  return drop(WritableFutureEnd, future_t, thread, hi)
+def canon_future_drop_writable(future_t, hi):
+  return drop(WritableFutureEnd, future_t, hi)
 
-def drop(EndT, stream_or_future_t, thread, hi):
-  trap_if(not thread.task.inst.may_leave)
-  e = thread.task.inst.handles.remove(hi)
+def drop(EndT, stream_or_future_t, hi):
+  inst = current_thread().task.inst
+  trap_if(not inst.may_leave)
+  e = inst.handles.remove(hi)
   trap_if(not isinstance(e, EndT))
   trap_if(e.shared.t != stream_or_future_t.t)
   e.drop()
@@ -4552,7 +4687,8 @@ validation specifies:
 Calling `$index` invokes the following function, which extracts the index
 of the [current thread]:
 ```python
-def canon_thread_index(thread):
+def canon_thread_index():
+  thread = current_thread()
   assert(thread.index is not None)
   return [thread.index]
 ```
@@ -4580,16 +4716,16 @@ thread in the current component instance's `threads` table.
 @dataclass
 class CoreFuncRef:
   t: CoreFuncType
-  callee: Callable[[Thread, list[CoreValType]], list[CoreValType]]
+  callee: Callable[[list[CoreValType]], list[CoreValType]]
 
-def canon_thread_new_indirect(ft, ftbl: Table[CoreFuncRef], thread, fi, c):
-  task = thread.task
+def canon_thread_new_indirect(ft, ftbl: Table[CoreFuncRef], fi, c):
+  task = current_thread().task
   trap_if(not task.inst.may_leave)
   f = ftbl.get(fi)
   assert(ft == CoreFuncType(['i32'], []) or ft == CoreFuncType(['i64'], []))
   trap_if(f.t != ft)
-  def thread_func(thread):
-    [] = call_and_trap_on_throw(f.callee, thread, [c])
+  def thread_func():
+    [] = call_and_trap_on_throw(f.callee, [c])
     task.unregister_thread(new_thread)
   new_thread = Thread(task, thread_func)
   assert(new_thread.suspended())
@@ -4615,7 +4751,8 @@ index `$i` from the current component instance's `threads` table, traps if it's
 not [suspended], and then switches to that thread, leaving the [current thread]
 suspended.
 ```python
-def canon_thread_switch_to(cancellable, thread, i):
+def canon_thread_switch_to(cancellable, i):
+  thread = current_thread()
   trap_if(not thread.task.inst.may_leave)
   other_thread = thread.task.inst.threads.get(i)
   trap_if(not other_thread.suspended())
@@ -4643,7 +4780,8 @@ Calling `$suspend` invokes the following function which suspends the [current
 thread], immediately returning control flow to any transitive `async`-lowered
 calling component.
 ```python
-def canon_thread_suspend(cancellable, thread):
+def canon_thread_suspend(cancellable):
+  thread = current_thread()
   trap_if(not thread.task.inst.may_leave)
   trap_if(not thread.task.may_block())
   cancelled = thread.suspend(cancellable)
@@ -4674,7 +4812,8 @@ index `$i` from the current component instance's `threads` table, traps if it's
 not [suspended], and then marks that thread as ready to run at some
 nondeterministic point in the future chosen by the embedder.
 ```python
-def canon_thread_resume_later(thread, i):
+def canon_thread_resume_later(i):
+  thread = current_thread()
   trap_if(not thread.task.inst.may_leave)
   other_thread = thread.task.inst.threads.get(i)
   trap_if(not other_thread.suspended())
@@ -4700,7 +4839,8 @@ not [suspended], and then switches to that thread, leaving the [current thread]
 ready to run at some nondeterministic point in the future chosen by the
 embedder.
 ```python
-def canon_thread_yield_to(cancellable, thread, i):
+def canon_thread_yield_to(cancellable, i):
+  thread = current_thread()
   trap_if(not thread.task.inst.may_leave)
   other_thread = thread.task.inst.threads.get(i)
   trap_if(not other_thread.suspended())
@@ -4730,7 +4870,8 @@ nondeterministic point in the future chosen by the embedder. This allows a
 long-running computation that is not otherwise performing I/O to avoid starving
 other threads in a cooperative setting.
 ```python
-def canon_thread_yield(cancellable, thread):
+def canon_thread_yield(cancellable):
+  thread = current_thread()
   trap_if(not thread.task.inst.may_leave)
   cancelled = thread.yield_(cancellable)
   return [cancelled]
@@ -4772,15 +4913,16 @@ its index.
 class ErrorContext:
   debug_message: String
 
-def canon_error_context_new(opts, thread, ptr, tagged_code_units):
-  trap_if(not thread.task.inst.may_leave)
+def canon_error_context_new(opts, ptr, tagged_code_units):
+  inst = current_thread().task.inst
+  trap_if(not inst.may_leave)
   if DETERMINISTIC_PROFILE or random.randint(0,1):
     s = String(('', 'utf8', 0))
   else:
-    cx = LiftLowerContext(opts, thread.task.inst)
+    cx = LiftLowerContext(opts, inst)
     s = load_string_from_range(cx, ptr, tagged_code_units)
     s = host_defined_transformation(s)
-  i = thread.task.inst.handles.add(ErrorContext(s))
+  i = inst.handles.add(ErrorContext(s))
   return [i]
 ```
 Supporting the requirement (introduced in the
@@ -4813,11 +4955,12 @@ value may nondeterministically discard or transform the debug message, a
 single `error-context` value must return the same debug message from
 `error.debug-message` over time.
 ```python
-def canon_error_context_debug_message(opts, thread, i, ptr):
-  trap_if(not thread.task.inst.may_leave)
-  errctx = thread.task.inst.handles.get(i)
+def canon_error_context_debug_message(opts, i, ptr):
+  inst = current_thread().task.inst
+  trap_if(not inst.may_leave)
+  errctx = inst.handles.get(i)
   trap_if(not isinstance(errctx, ErrorContext))
-  cx = LiftLowerContext(opts, thread.task.inst)
+  cx = LiftLowerContext(opts, inst)
   store_string(cx, errctx.debug_message, ptr)
   return []
 ```
@@ -4838,9 +4981,10 @@ validation specifies:
 Calling `$f` calls the following function, which drops the error context value
 at the given index from the current component instance's `handles` table:
 ```python
-def canon_error_context_drop(thread, i):
-  trap_if(not thread.task.inst.may_leave)
-  errctx = thread.task.inst.handles.remove(i)
+def canon_error_context_drop(i):
+  inst = current_thread().task.inst
+  trap_if(not inst.may_leave)
+  errctx = inst.handles.remove(i)
   trap_if(not isinstance(errctx, ErrorContext))
   return []
 ```
@@ -4876,12 +5020,12 @@ Calling `$spawn_ref` invokes the following function which simply fuses the
 `thread.new_ref` and `thread.resume-later` built-ins, allowing
 thread-creation to skip the intermediate "suspended" state transition.
 ```python
-def canon_thread_spawn_ref(shared, ft, thread, f, c):
-  trap_if(not thread.task.inst.may_leave)
+def canon_thread_spawn_ref(shared, ft, f, c):
+  trap_if(not current_thread().task.inst.may_leave)
   if DETERMINISTIC_PROFILE:
     return [0]
-  [new_thread_index] = canon_thread_new_ref(shared, ft, thread, f, c)
-  [] = canon_thread_resume_later(shared, thread, new_thread_index)
+  [new_thread_index] = canon_thread_new_ref(shared, ft, f, c)
+  [] = canon_thread_resume_later(shared, new_thread_index)
   return [new_thread_index]
 ```
 Note: `canon_thread_new_ref` has not yet been defined, but will be added as
@@ -4913,12 +5057,12 @@ Calling `$spawn_indirect` invokes the following function which simply fuses
 the `thread.new-indirect` and `thread.resume-later` built-ins, allowing
 thread-creation to skip the intermediate "suspended" state transition.
 ```python
-def canon_thread_spawn_indirect(shared, ft, ftbl: Table[CoreFuncRef], thread, fi, c):
-  trap_if(not thread.task.inst.may_leave)
+def canon_thread_spawn_indirect(shared, ft, ftbl: Table[CoreFuncRef], fi, c):
+  trap_if(not current_thread().task.inst.may_leave)
   if DETERMINISTIC_PROFILE:
     return [0]
-  [new_thread_index] = canon_thread_new_indirect(shared, ft, ftbl, thread, fi, c)
-  [] = canon_thread_resume_later(shared, thread, new_thread_index)
+  [new_thread_index] = canon_thread_new_indirect(shared, ft, ftbl, fi, c)
+  [] = canon_thread_resume_later(shared, new_thread_index)
   return [new_thread_index]
 ```
 Note: `canon_thread_new_indirect` has not yet been extended to take a
@@ -4959,7 +5103,9 @@ def canon_thread_available_parallelism():
 [Adapter Functions]: FutureFeatures.md#custom-abis-via-adapter-functions
 [Shared-Everything Dynamic Linking]: examples/SharedEverythingDynamicLinking.md
 [Concurrency Explainer]: Concurrency.md
-[Suspended]: Concurrency#thread-built-ins
+[Suspended]: Concurrency.md#thread-built-ins
+[Thread Index]: Concurrency.md#thread-built-ins
+[Async Call Stack]: Concurrency.md#subtasks-and-supertasks
 [Structured Concurrency]: Concurrency.md#subtasks-and-supertasks
 [Recursive Reentrance]: Concurrency.md#subtasks-and-supertasks
 [Backpressure]: Concurrency.md#backpressure
@@ -4968,10 +5114,12 @@ def canon_thread_available_parallelism():
 [Current Task]: Concurrency.md#current-thread-and-task
 [Blocks]: Concurrency.md#blocking
 [Block]: Concurrency.md#blocking
+[Waiting On External I/O And Yielding]: Concurrency.md#blocking
 [Subtasks]: Concurrency.md#subtasks-and-supertasks
 [Readable and Writable Ends]: Concurrency.md#streams-and-futures
 [Readable or Writable End]: Concurrency.md#streams-and-futures
 [Thread-Local Storage]: Concurrency.md#thread-local-storage
+[Cancellation]: Concurrency.md#cancellation
 [Subtask State Machine]: Concurrency.md#cancellation
 [Stream Readiness]: Concurrency.md#stream-readiness
 
@@ -4994,6 +5142,7 @@ def canon_thread_available_parallelism():
 [WASI]: https://github.com/webassembly/wasi
 [Deterministic Profile]: https://github.com/WebAssembly/profiles/blob/main/proposals/profiles/Overview.md
 [stack-switching]: https://github.com/WebAssembly/stack-switching
+[Control Tags]: https://github.com/WebAssembly/stack-switching/blob/main/proposals/stack-switching/Explainer.md#declaring-control-tags
 [`memaddr`]: https://webassembly.github.io/spec/core/exec/runtime.html#syntax-memaddr
 [`memaddrs` table]: https://webassembly.github.io/spec/core/exec/runtime.html#syntax-moduleinst
 [`memidx`]: https://webassembly.github.io/spec/core/syntax/modules.html#syntax-memidx
@@ -5009,8 +5158,7 @@ def canon_thread_available_parallelism():
 [Code Units]: https://www.unicode.org/glossary/#code_unit
 [Surrogate]: https://unicode.org/faq/utf_bom.html#utf16-2
 [Name Mangling]: https://en.wikipedia.org/wiki/Name_mangling
-[Kernel Thread]: https://en.wikipedia.org/wiki/Thread_(computing)#kernel_thread
-[Fiber]: https://en.wikipedia.org/wiki/Fiber_(computer_science)
+[Fibers]: https://en.wikipedia.org/wiki/Fiber_(computer_science)
 [Asyncify]: https://emscripten.org/docs/porting/asyncify.html
 
 [`import_name`]: https://clang.llvm.org/docs/AttributeReference.html#import-name
@@ -5021,7 +5169,8 @@ def canon_thread_available_parallelism():
 
 [`threading`]: https://docs.python.org/3/library/threading.html
 [`threading.Thread`]: https://docs.python.org/3/library/threading.html#thread-objects
-[`threading.Lock`]:  https://docs.python.org/3/library/threading.html#lock-objects
+[`threading.Lock`]: https://docs.python.org/3/library/threading.html#lock-objects
+[`threading.local`]: https://docs.python.org/3/library/threading.html#thread-local-data
 
 [OIO]: https://en.wikipedia.org/wiki/Overlapped_I/O
 [io_uring]: https://en.wikipedia.org/wiki/Io_uring
diff --git a/design/mvp/Concurrency.md b/design/mvp/Concurrency.md
index 1ab4e1f6..d7ac155b 100644
--- a/design/mvp/Concurrency.md
+++ b/design/mvp/Concurrency.md
@@ -49,9 +49,9 @@ concurrency-specific goals and use cases:
   * promises, futures, streams and channels
   * callbacks, in languages with no other built-in concurrency mechanisms
 * Provide [fiber]-like stack-switching capabilities via Core WebAssembly
-  import calls in a way that complements, but doesn't depend on, new Core
-  WebAssembly proposals including [stack-switching] and
-  [shared-everything-threads].
+  import calls in a way that composes with, and is [specified in terms of],
+  but doesn't actually depend on, the Core WebAssembly [stack-switching]
+  proposal.
 * Allow polyfilling in browsers via JavaScript Promise Integration ([JSPI])
 * Avoid partitioning interfaces and components into separate ecosystems based
   on degree of concurrency; don't give components a "[color]".
@@ -350,11 +350,17 @@ feature is necessary in any case (due to iloops and traps).
 
 At any point in time while executing Core WebAssembly code or a [canonical
 built-in] called by Core WebAssembly code, there is a well-defined **current
-thread** whose containing task is the **current task**. The "current thread" is
-modelled in the Canonical ABI's Python code by explicitly passing a [`Thread`]
-object as an argument to all function calls so that the semantic "current
-thread" is always the value of the `thread` parameter. Threads store their
-containing task so that the "current task" is always `thread.task`.
+thread** whose containing task is the **current task**.
+
+The "current thread" is [specified in terms of] stack-switching with a
+`current-thread` effect for retrieving the current thread from the parent
+`resume` handler's state. However, due to structural invariants, engines can
+reliably optimize this `current-thread` effect by storing the current thread in
+the VM's execution state (or a special Core WebAssembly `global`) so that it
+could be cheaply loaded and/or kept in register state.
+
+Threads store their containing task so that the "current task" is always
+`current_thread.task`.
 
 Because there is always a well-defined current task and tasks are always
 created for calls to typed functions, it is therefore also always well-defined
@@ -490,6 +496,10 @@ exception, to allow it to be called arbitrarily from anywhere, `thread.yield`
 does not trap but instead behaves as a no-op if the current task's function
 type does not contain `async`.
 
+"Blocking" is [specified in terms of] stack-switching, with a `block` effect
+that suspends the current thread to produce a continuation that can be resumed
+once the reason for blocking is addressed.
+
 The [Canonical ABI explainer] defines the above behavior more precisely; search
 for `may_block` to see all the relevant points.
 
@@ -1332,6 +1342,7 @@ comes after:
 [`stream.cancel-write`]: Explainer.md#-streamcancel-read-streamcancel-write-futurecancel-read-and-futurecancel-write
 
 [Canonical ABI Explainer]: CanonicalABI.md
+[specified in terms of]: CanonicalABI.md#stack-switching
 [`canon_lift`]: CanonicalABI.md#canon-lift
 [`unpack_callback_result`]: CanonicalABI.md#canon-lift
 [`canon_lower`]: CanonicalABI.md#canon-lower
diff --git a/design/mvp/canonical-abi/definitions.py b/design/mvp/canonical-abi/definitions.py
index 2b3f4149..b75ea950 100644
--- a/design/mvp/canonical-abi/definitions.py
+++ b/design/mvp/canonical-abi/definitions.py
@@ -389,25 +389,80 @@ def __init__(self, impl, dtor = None, dtor_async = False, dtor_callback = None):
     self.dtor_async = dtor_async
     self.dtor_callback = dtor_callback
 
-#### Thread State
+#### Stack Switching
 
 class Cancelled(IntEnum):
   FALSE = 0
   TRUE = 1
 
+class Continuation:
+  lock: threading.Lock
+  handler: Handler
+  cancelled: Cancelled
+
+class Handler:
+  thread_local = threading.local()
+  lock: threading.Lock
+  current: Thread
+  cont: Optional[Continuation]
+  switch_to: Optional[Thread]
+
+def new_already_acquired_lock() -> threading.Lock:
+  lock = threading.Lock()
+  lock.acquire()
+  return lock
+
+def cont_new(f: Callable[[Cancelled], Optional[Thread]]) -> Continuation:
+  cont = Continuation()
+  cont.lock = new_already_acquired_lock()
+  def thread_base():
+    cont.lock.acquire()
+    Handler.thread_local.value = cont.handler
+    switch_to = f(cont.cancelled)
+    handler = Handler.thread_local.value
+    handler.cont = None
+    handler.switch_to = switch_to
+    handler.lock.release()
+  threading.Thread(target = thread_base).start()
+  return cont
+
+def resume(cont: Continuation, cancelled: Cancelled, current: Thread) -> \
+           tuple[Optional[Continuation], Optional[Thread]]:
+  handler = Handler()
+  handler.lock = new_already_acquired_lock()
+  handler.current = current
+  cont.handler = handler
+  cont.cancelled = cancelled
+  cont.lock.release()
+  handler.lock.acquire()
+  return (handler.cont, handler.switch_to)
+
+def block(switch_to: Optional[Thread]) -> Cancelled:
+  cont = Continuation()
+  cont.lock = new_already_acquired_lock()
+  handler = Handler.thread_local.value
+  handler.cont = cont
+  handler.switch_to = switch_to
+  handler.lock.release()
+  cont.lock.acquire()
+  Handler.thread_local.value = cont.handler
+  return cont.cancelled
+
+def current_thread() -> Thread:
+  return Handler.thread_local.value.current
+
+#### Thread State
+
 class Thread:
-  task: Task
-  fiber: threading.Thread
-  fiber_lock: threading.Lock
-  parent_lock: Optional[threading.Lock]
+  cont: Optional[Continuation]
   ready_func: Optional[Callable[[], bool]]
+  task: Task
   cancellable: bool
-  cancelled: Cancelled
   index: Optional[int]
   storage: tuple[int,int]
 
   def running(self):
-    return self.parent_lock is not None
+    return self.cont is None
 
   def suspended(self):
     return not self.running() and self.ready_func is None
@@ -420,21 +475,16 @@ def ready(self):
     return self.ready_func()
 
   def __init__(self, task, thread_func):
-    self.task = task
-    self.fiber_lock = threading.Lock()
-    self.fiber_lock.acquire()
-    self.parent_lock = None
+    def cont_func(cancelled):
+      assert(self.running() and not cancelled)
+      thread_func()
+      return None
+    self.cont = cont_new(cont_func)
     self.ready_func = None
+    self.task = task
     self.cancellable = False
-    self.cancelled = Cancelled.FALSE
     self.index = None
     self.storage = [0,0]
-    def fiber_func():
-      self.fiber_lock.acquire()
-      thread_func(self)
-      self.parent_lock.release()
-    self.fiber = threading.Thread(target = fiber_func)
-    self.fiber.start()
     assert(self.suspended())
 
   def resume_later(self):
@@ -449,24 +499,22 @@ def resume(self, cancelled):
       assert(cancelled or self.ready())
       self.ready_func = None
       self.task.inst.store.waiting.remove(self)
-    assert(self.cancellable or not cancelled)
-    self.cancelled = cancelled
-    self.parent_lock = threading.Lock()
-    self.parent_lock.acquire()
-    self.fiber_lock.release()
-    self.parent_lock.acquire()
-    self.parent_lock = None
-    assert(not self.running())
+    thread = self
+    while thread is not None:
+      cont = thread.cont
+      thread.cont = None
+      (thread.cont, switch_to) = resume(cont, cancelled, thread)
+      thread = switch_to
+      cancelled = Cancelled.FALSE
 
   def suspend(self, cancellable) -> Cancelled:
     assert(self.running() and self.task.may_block())
     if self.task.deliver_pending_cancel(cancellable):
       return Cancelled.TRUE
     self.cancellable = cancellable
-    self.parent_lock.release()
-    self.fiber_lock.acquire()
-    assert(self.running() and (cancellable or not self.cancelled))
-    return self.cancelled
+    cancelled = block(switch_to = None)
+    assert(self.running() and (cancellable or not cancelled))
+    return cancelled
 
   def wait_until(self, ready_func, cancellable = False) -> Cancelled:
     assert(self.running() and self.task.may_block())
@@ -494,15 +542,9 @@ def switch_to(self, cancellable, other: Thread) -> Cancelled:
     if self.task.deliver_pending_cancel(cancellable):
       return Cancelled.TRUE
     self.cancellable = cancellable
-    other.cancelled = Cancelled.FALSE
-    assert(self.parent_lock and not other.parent_lock)
-    other.parent_lock = self.parent_lock
-    self.parent_lock = None
-    assert(not self.running() and other.running())
-    other.fiber_lock.release()
-    self.fiber_lock.acquire()
-    assert(self.running() and (cancellable or not self.cancelled))
-    return self.cancelled
+    cancelled = block(switch_to = other)
+    assert(self.running() and (cancellable or not cancelled))
+    return cancelled
 
   def yield_to(self, cancellable, other: Thread) -> Cancelled:
     assert(self.running() and other.suspended())
@@ -572,22 +614,22 @@ def get_pending_event(self) -> EventTuple:
       if w.has_pending_event():
         return w.get_pending_event()
 
-  def wait_until(self, ready_func, thread, cancellable) -> EventTuple:
+  def wait_until(self, ready_func, cancellable) -> EventTuple:
     def ready_and_has_event():
       return ready_func() and self.has_pending_event()
     self.num_waiting += 1
-    if thread.wait_until(ready_and_has_event, cancellable) == Cancelled.TRUE:
+    if current_thread().wait_until(ready_and_has_event, cancellable) == Cancelled.TRUE:
       event = (EventCode.TASK_CANCELLED, 0, 0)
     else:
       event = self.get_pending_event()
     self.num_waiting -= 1
     return event
 
-  def wait(self, thread, cancellable) -> EventTuple:
-    return self.wait_until(lambda: True, thread, cancellable)
+  def wait(self, cancellable) -> EventTuple:
+    return self.wait_until(lambda: True, cancellable)
 
-  def poll(self, thread, cancellable) -> EventTuple:
-    if thread.task.deliver_pending_cancel(cancellable):
+  def poll(self, cancellable) -> EventTuple:
+    if current_thread().task.deliver_pending_cancel(cancellable):
       return (EventCode.TASK_CANCELLED, 0, 0)
     elif not self.has_pending_event():
       return (EventCode.NONE, 0, 0)
@@ -634,7 +676,8 @@ def needs_exclusive(self):
   def may_block(self):
     return self.ft.async_ or self.state == Task.State.RESOLVED
 
-  def enter(self, thread):
+  def enter(self):
+    thread = current_thread()
     if self.ft.async_:
       def has_backpressure():
         return self.inst.backpressure > 0 or (self.needs_exclusive() and bool(self.inst.exclusive))
@@ -659,8 +702,8 @@ def register_thread(self, thread):
     assert(thread.index is None)
     thread.index = self.inst.threads.add(thread)
 
-  def exit(self, thread):
-    self.unregister_thread(thread)
+  def exit(self):
+    self.unregister_thread(current_thread())
     if self.ft.async_ and self.needs_exclusive():
       assert(self.inst.exclusive is self)
       self.inst.exclusive = None
@@ -1999,8 +2042,8 @@ def lower_flat_values(cx, max_flat, vs, ts, out_param = None):
 def canon_lift(opts, inst, ft, callee, caller, on_start, on_resolve) -> Call:
   trap_if(call_might_be_recursive(caller, inst))
   task = Task(opts, inst, ft, caller, on_resolve)
-  def thread_func(thread):
-    if not task.enter(thread):
+  def thread_func():
+    if not task.enter():
       return
 
     cx = LiftLowerContext(opts, inst, task)
@@ -2010,24 +2053,24 @@ def thread_func(thread):
     assert(types_match_values(flat_ft.params, flat_args))
 
     if not opts.async_:
-      flat_results = call_and_trap_on_throw(callee, thread, flat_args)
+      flat_results = call_and_trap_on_throw(callee, flat_args)
       assert(types_match_values(flat_ft.results, flat_results))
       result = lift_flat_values(cx, MAX_FLAT_RESULTS, CoreValueIter(flat_results), ft.result_type())
       task.return_(result)
       if opts.post_return is not None:
         inst.may_leave = False
-        [] = call_and_trap_on_throw(opts.post_return, thread, flat_results)
+        [] = call_and_trap_on_throw(opts.post_return, flat_results)
         inst.may_leave = True
-      task.exit(thread)
+      task.exit()
       return
 
     if not opts.callback:
-      [] = call_and_trap_on_throw(callee, thread, flat_args)
+      [] = call_and_trap_on_throw(callee, flat_args)
       assert(types_match_values(flat_ft.results, []))
-      task.exit(thread)
+      task.exit()
       return
 
-    [packed] = call_and_trap_on_throw(callee, thread, flat_args)
+    [packed] = call_and_trap_on_throw(callee, flat_args)
     code,si = unpack_callback_result(packed)
     while code != CallbackCode.EXIT:
       assert(inst.exclusive is task)
@@ -2042,15 +2085,15 @@ def thread_func(thread):
           trap_if(not task.may_block())
           wset = inst.handles.get(si)
           trap_if(not isinstance(wset, WaitableSet))
-          event = wset.wait_until(lambda: not inst.exclusive, thread, cancellable = True)
+          event = wset.wait_until(lambda: not inst.exclusive, cancellable = True)
         case _:
           trap()
       assert(inst.exclusive is None)
       inst.exclusive = task
       event_code, p1, p2 = event
-      [packed] = call_and_trap_on_throw(opts.callback, thread, [event_code, p1, p2])
+      [packed] = call_and_trap_on_throw(opts.callback, [event_code, p1, p2])
       code,si = unpack_callback_result(packed)
-    task.exit(thread)
+    task.exit()
     return
 
   thread = Thread(task, thread_func)
@@ -2071,15 +2114,16 @@ def unpack_callback_result(packed):
   waitable_set_index = packed >> 4
   return (CallbackCode(code), waitable_set_index)
 
-def call_and_trap_on_throw(callee, thread, args):
+def call_and_trap_on_throw(callee, args):
   try:
-    return callee(thread, args)
+    return callee(args)
   except CoreWebAssemblyException:
     trap()
 
 ### `canon lower`
 
-def canon_lower(opts, ft, callee: FuncInst, thread, flat_args):
+def canon_lower(opts, ft, callee: FuncInst, flat_args):
+  thread = current_thread()
   trap_if(not thread.task.inst.may_leave)
   trap_if(not thread.task.may_block() and ft.async_ and not opts.async_)
 
@@ -2149,17 +2193,18 @@ def subtask_event():
 
 ### `canon resource.new`
 
-def canon_resource_new(rt, thread, rep):
-  trap_if(not thread.task.inst.may_leave)
+def canon_resource_new(rt, rep):
+  inst = current_thread().task.inst
+  trap_if(not inst.may_leave)
   h = ResourceHandle(rt, rep, own = True)
-  i = thread.task.inst.handles.add(h)
+  i = inst.handles.add(h)
   return [i]
 
 ### `canon resource.drop`
 
-def canon_resource_drop(rt, thread, i):
-  trap_if(not thread.task.inst.may_leave)
-  inst = thread.task.inst
+def canon_resource_drop(rt, i):
+  inst = current_thread().task.inst
+  trap_if(not inst.may_leave)
   h = inst.handles.remove(i)
   trap_if(not isinstance(h, ResourceHandle))
   trap_if(h.rt is not rt)
@@ -2173,17 +2218,17 @@ def canon_resource_drop(rt, thread, i):
       caller_opts = CanonicalOptions(async_ = False)
       callee_opts = CanonicalOptions(async_ = rt.dtor_async, callback = rt.dtor_callback)
       ft = FuncType([U32Type()],[], async_ = False)
-      dtor = rt.dtor or (lambda thread, rep: [])
+      dtor = rt.dtor or (lambda rep: [])
       callee = partial(canon_lift, callee_opts, rt.impl, ft, dtor)
-      [] = canon_lower(caller_opts, ft, callee, thread, [h.rep])
+      [] = canon_lower(caller_opts, ft, callee, [h.rep])
   else:
     h.borrow_scope.num_borrows -= 1
   return []
 
 ### `canon resource.rep`
 
-def canon_resource_rep(rt, thread, i):
-  h = thread.task.inst.handles.get(i)
+def canon_resource_rep(rt, i):
+  h = current_thread().task.inst.handles.get(i)
   trap_if(not isinstance(h, ResourceHandle))
   trap_if(h.rt is not rt)
   return [h.rep]
@@ -2191,7 +2236,8 @@ def canon_resource_rep(rt, thread, i):
 ### 🔀 `canon context.get`
 MASK_32BIT = (1 << 32) - 1
 
-def canon_context_get(t, i, thread):
+def canon_context_get(t, i):
+  thread = current_thread()
   assert(t == 'i32' or t == 'i64')
   assert(i < len(thread.storage))
   result = thread.storage[i]
@@ -2201,7 +2247,8 @@ def canon_context_get(t, i, thread):
 
 ### 🔀 `canon context.set`
 
-def canon_context_set(t, i, thread, v):
+def canon_context_set(t, i, v):
+  thread = current_thread()
   assert(t == 'i32' or t == 'i64')
   assert(v <= MASK_32BIT or t == 'i64')
   assert(i < len(thread.storage))
@@ -2210,29 +2257,31 @@ def canon_context_set(t, i, thread, v):
 
 ### 🔀 `canon backpressure.set`
 
-def canon_backpressure_set(thread, flat_args):
+def canon_backpressure_set(flat_args):
   assert(len(flat_args) == 1)
-  thread.task.inst.backpressure = int(bool(flat_args[0]))
+  current_thread().task.inst.backpressure = int(bool(flat_args[0]))
   return []
 
 ### 🔀 `canon backpressure.{inc,dec}`
 
-def canon_backpressure_inc(thread):
-  assert(0 <= thread.task.inst.backpressure < 2**16)
-  thread.task.inst.backpressure += 1
-  trap_if(thread.task.inst.backpressure == 2**16)
+def canon_backpressure_inc():
+  inst = current_thread().task.inst
+  assert(0 <= inst.backpressure < 2**16)
+  inst.backpressure += 1
+  trap_if(inst.backpressure == 2**16)
   return []
 
-def canon_backpressure_dec(thread):
-  assert(0 <= thread.task.inst.backpressure < 2**16)
-  thread.task.inst.backpressure -= 1
-  trap_if(thread.task.inst.backpressure < 0)
+def canon_backpressure_dec():
+  inst = current_thread().task.inst
+  assert(0 <= inst.backpressure < 2**16)
+  inst.backpressure -= 1
+  trap_if(inst.backpressure < 0)
   return []
 
 ### 🔀 `canon task.return`
 
-def canon_task_return(thread, result_type, opts: LiftOptions, flat_args):
-  task = thread.task
+def canon_task_return(result_type, opts: LiftOptions, flat_args):
+  task = current_thread().task
   trap_if(not task.inst.may_leave)
   trap_if(not task.opts.async_)
   trap_if(result_type != task.ft.result)
@@ -2244,8 +2293,8 @@ def canon_task_return(thread, result_type, opts: LiftOptions, flat_args):
 
 ### 🔀 `canon task.cancel`
 
-def canon_task_cancel(thread):
-  task = thread.task
+def canon_task_cancel():
+  task = current_thread().task
   trap_if(not task.inst.may_leave)
   trap_if(not task.opts.async_)
   task.cancel()
@@ -2253,55 +2302,61 @@ def canon_task_cancel(thread):
 
 ### 🔀 `canon waitable-set.new`
 
-def canon_waitable_set_new(thread):
-  trap_if(not thread.task.inst.may_leave)
-  return [ thread.task.inst.handles.add(WaitableSet()) ]
+def canon_waitable_set_new():
+  inst = current_thread().task.inst
+  trap_if(not inst.may_leave)
+  i = inst.handles.add(WaitableSet())
+  return [i]
 
 ### 🔀 `canon waitable-set.wait`
 
-def canon_waitable_set_wait(cancellable, mem, thread, si, ptr):
-  trap_if(not thread.task.inst.may_leave)
-  trap_if(not thread.task.may_block())
-  wset = thread.task.inst.handles.get(si)
+def canon_waitable_set_wait(cancellable, mem, si, ptr):
+  task = current_thread().task
+  trap_if(not task.inst.may_leave)
+  trap_if(not task.may_block())
+  wset = task.inst.handles.get(si)
   trap_if(not isinstance(wset, WaitableSet))
-  event = wset.wait(thread, cancellable)
-  return unpack_event(mem, thread, ptr, event)
+  event = wset.wait(cancellable)
+  return unpack_event(mem, task.inst, ptr, event)
 
-def unpack_event(mem, thread, ptr, e: EventTuple):
+def unpack_event(mem, inst, ptr, e: EventTuple):
   event, p1, p2 = e
-  cx = LiftLowerContext(LiftLowerOptions(memory = mem), thread.task.inst)
+  cx = LiftLowerContext(LiftLowerOptions(memory = mem), inst)
   store(cx, p1, U32Type(), ptr)
   store(cx, p2, U32Type(), ptr + 4)
   return [event]
 
 ### 🔀 `canon waitable-set.poll`
 
-def canon_waitable_set_poll(cancellable, mem, thread, si, ptr):
-  trap_if(not thread.task.inst.may_leave)
-  wset = thread.task.inst.handles.get(si)
+def canon_waitable_set_poll(cancellable, mem, si, ptr):
+  inst = current_thread().task.inst
+  trap_if(not inst.may_leave)
+  wset = inst.handles.get(si)
   trap_if(not isinstance(wset, WaitableSet))
-  event = wset.poll(thread, cancellable)
-  return unpack_event(mem, thread, ptr, event)
+  event = wset.poll(cancellable)
+  return unpack_event(mem, inst, ptr, event)
 
 ### 🔀 `canon waitable-set.drop`
 
-def canon_waitable_set_drop(thread, i):
-  trap_if(not thread.task.inst.may_leave)
-  wset = thread.task.inst.handles.remove(i)
+def canon_waitable_set_drop(i):
+  inst = current_thread().task.inst
+  trap_if(not inst.may_leave)
+  wset = inst.handles.remove(i)
   trap_if(not isinstance(wset, WaitableSet))
   wset.drop()
   return []
 
 ### 🔀 `canon waitable.join`
 
-def canon_waitable_join(thread, wi, si):
-  trap_if(not thread.task.inst.may_leave)
-  w = thread.task.inst.handles.get(wi)
+def canon_waitable_join(wi, si):
+  inst = current_thread().task.inst
+  trap_if(not inst.may_leave)
+  w = inst.handles.get(wi)
   trap_if(not isinstance(w, Waitable))
   if si == 0:
     w.join(None)
   else:
-    wset = thread.task.inst.handles.get(si)
+    wset = inst.handles.get(si)
     trap_if(not isinstance(wset, WaitableSet))
     w.join(wset)
   return []
@@ -2310,7 +2365,8 @@ def canon_waitable_join(thread, wi, si):
 
 BLOCKED = 0xffff_ffff
 
-def canon_subtask_cancel(async_, thread, i):
+def canon_subtask_cancel(async_, i):
+  thread = current_thread()
   trap_if(not thread.task.inst.may_leave)
   trap_if(not thread.task.may_block() and not async_)
   subtask = thread.task.inst.handles.get(i)
@@ -2334,40 +2390,44 @@ def canon_subtask_cancel(async_, thread, i):
 
 ### 🔀 `canon subtask.drop`
 
-def canon_subtask_drop(thread, i):
-  trap_if(not thread.task.inst.may_leave)
-  s = thread.task.inst.handles.remove(i)
+def canon_subtask_drop(i):
+  inst = current_thread().task.inst
+  trap_if(not inst.may_leave)
+  s = inst.handles.remove(i)
   trap_if(not isinstance(s, Subtask))
   s.drop()
   return []
 
 ### 🔀 `canon {stream,future}.new`
 
-def canon_stream_new(stream_t, thread):
-  trap_if(not thread.task.inst.may_leave)
+def canon_stream_new(stream_t):
+  inst = current_thread().task.inst
+  trap_if(not inst.may_leave)
   shared = SharedStreamImpl(stream_t.t)
-  ri = thread.task.inst.handles.add(ReadableStreamEnd(shared))
-  wi = thread.task.inst.handles.add(WritableStreamEnd(shared))
+  ri = inst.handles.add(ReadableStreamEnd(shared))
+  wi = inst.handles.add(WritableStreamEnd(shared))
   return [ ri | (wi << 32) ]
 
-def canon_future_new(future_t, thread):
-  trap_if(not thread.task.inst.may_leave)
+def canon_future_new(future_t):
+  inst = current_thread().task.inst
+  trap_if(not inst.may_leave)
   shared = SharedFutureImpl(future_t.t)
-  ri = thread.task.inst.handles.add(ReadableFutureEnd(shared))
-  wi = thread.task.inst.handles.add(WritableFutureEnd(shared))
+  ri = inst.handles.add(ReadableFutureEnd(shared))
+  wi = inst.handles.add(WritableFutureEnd(shared))
   return [ ri | (wi << 32) ]
 
 ### 🔀 `canon stream.{read,write}`
 
-def canon_stream_read(stream_t, opts, thread, i, ptr, n):
+def canon_stream_read(stream_t, opts, i, ptr, n):
   return stream_copy(ReadableStreamEnd, WritableBufferGuestImpl, EventCode.STREAM_READ,
-                     stream_t, opts, thread, i, ptr, n)
+                     stream_t, opts, i, ptr, n)
 
-def canon_stream_write(stream_t, opts, thread, i, ptr, n):
+def canon_stream_write(stream_t, opts, i, ptr, n):
   return stream_copy(WritableStreamEnd, ReadableBufferGuestImpl, EventCode.STREAM_WRITE,
-                     stream_t, opts, thread, i, ptr, n)
+                     stream_t, opts, i, ptr, n)
 
-def stream_copy(EndT, BufferT, event_code, stream_t, opts, thread, i, ptr, n):
+def stream_copy(EndT, BufferT, event_code, stream_t, opts, i, ptr, n):
+  thread = current_thread()
   trap_if(not thread.task.inst.may_leave)
   trap_if(not thread.task.may_block() and not opts.async_)
 
@@ -2413,15 +2473,16 @@ def on_copy_done(result):
 
 ### 🔀 `canon future.{read,write}`
 
-def canon_future_read(future_t, opts, thread, i, ptr):
+def canon_future_read(future_t, opts, i, ptr):
   return future_copy(ReadableFutureEnd, WritableBufferGuestImpl, EventCode.FUTURE_READ,
-                     future_t, opts, thread, i, ptr)
+                     future_t, opts, i, ptr)
 
-def canon_future_write(future_t, opts, thread, i, ptr):
+def canon_future_write(future_t, opts, i, ptr):
   return future_copy(WritableFutureEnd, ReadableBufferGuestImpl, EventCode.FUTURE_WRITE,
-                     future_t, opts, thread, i, ptr)
+                     future_t, opts, i, ptr)
 
-def future_copy(EndT, BufferT, event_code, future_t, opts, thread, i, ptr):
+def future_copy(EndT, BufferT, event_code, future_t, opts, i, ptr):
+  thread = current_thread()
   trap_if(not thread.task.inst.may_leave)
   trap_if(not thread.task.may_block() and not opts.async_)
 
@@ -2461,19 +2522,20 @@ def on_copy_done(result):
 
 ### 🔀 `canon {stream,future}.cancel-{read,write}`
 
-def canon_stream_cancel_read(stream_t, async_, thread, i):
-  return cancel_copy(ReadableStreamEnd, EventCode.STREAM_READ, stream_t, async_, thread, i)
+def canon_stream_cancel_read(stream_t, async_, i):
+  return cancel_copy(ReadableStreamEnd, EventCode.STREAM_READ, stream_t, async_, i)
 
-def canon_stream_cancel_write(stream_t, async_, thread, i):
-  return cancel_copy(WritableStreamEnd, EventCode.STREAM_WRITE, stream_t, async_, thread, i)
+def canon_stream_cancel_write(stream_t, async_, i):
+  return cancel_copy(WritableStreamEnd, EventCode.STREAM_WRITE, stream_t, async_, i)
 
-def canon_future_cancel_read(future_t, async_, thread, i):
-  return cancel_copy(ReadableFutureEnd, EventCode.FUTURE_READ, future_t, async_, thread, i)
+def canon_future_cancel_read(future_t, async_, i):
+  return cancel_copy(ReadableFutureEnd, EventCode.FUTURE_READ, future_t, async_, i)
 
-def canon_future_cancel_write(future_t, async_, thread, i):
-  return cancel_copy(WritableFutureEnd, EventCode.FUTURE_WRITE, future_t, async_, thread, i)
+def canon_future_cancel_write(future_t, async_, i):
+  return cancel_copy(WritableFutureEnd, EventCode.FUTURE_WRITE, future_t, async_, i)
 
-def cancel_copy(EndT, event_code, stream_or_future_t, async_, thread, i):
+def cancel_copy(EndT, event_code, stream_or_future_t, async_, i):
+  thread = current_thread()
   trap_if(not thread.task.inst.may_leave)
   trap_if(not thread.task.may_block() and not async_)
   e = thread.task.inst.handles.get(i)
@@ -2494,21 +2556,22 @@ def cancel_copy(EndT, event_code, stream_or_future_t, async_, thread, i):
 
 ### 🔀 `canon {stream,future}.drop-{readable,writable}`
 
-def canon_stream_drop_readable(stream_t, thread, i):
-  return drop(ReadableStreamEnd, stream_t, thread, i)
+def canon_stream_drop_readable(stream_t, i):
+  return drop(ReadableStreamEnd, stream_t, i)
 
-def canon_stream_drop_writable(stream_t, thread, hi):
-  return drop(WritableStreamEnd, stream_t, thread, hi)
+def canon_stream_drop_writable(stream_t, hi):
+  return drop(WritableStreamEnd, stream_t, hi)
 
-def canon_future_drop_readable(future_t, thread, i):
-  return drop(ReadableFutureEnd, future_t, thread, i)
+def canon_future_drop_readable(future_t, i):
+  return drop(ReadableFutureEnd, future_t, i)
 
-def canon_future_drop_writable(future_t, thread, hi):
-  return drop(WritableFutureEnd, future_t, thread, hi)
+def canon_future_drop_writable(future_t, hi):
+  return drop(WritableFutureEnd, future_t, hi)
 
-def drop(EndT, stream_or_future_t, thread, hi):
-  trap_if(not thread.task.inst.may_leave)
-  e = thread.task.inst.handles.remove(hi)
+def drop(EndT, stream_or_future_t, hi):
+  inst = current_thread().task.inst
+  trap_if(not inst.may_leave)
+  e = inst.handles.remove(hi)
   trap_if(not isinstance(e, EndT))
   trap_if(e.shared.t != stream_or_future_t.t)
   e.drop()
@@ -2516,7 +2579,8 @@ def drop(EndT, stream_or_future_t, thread, hi):
 
 ### 🧵 `canon thread.index`
 
-def canon_thread_index(thread):
+def canon_thread_index():
+  thread = current_thread()
   assert(thread.index is not None)
   return [thread.index]
 
@@ -2525,16 +2589,16 @@ def canon_thread_index(thread):
 @dataclass
 class CoreFuncRef:
   t: CoreFuncType
-  callee: Callable[[Thread, list[CoreValType]], list[CoreValType]]
+  callee: Callable[[list[CoreValType]], list[CoreValType]]
 
-def canon_thread_new_indirect(ft, ftbl: Table[CoreFuncRef], thread, fi, c):
-  task = thread.task
+def canon_thread_new_indirect(ft, ftbl: Table[CoreFuncRef], fi, c):
+  task = current_thread().task
   trap_if(not task.inst.may_leave)
   f = ftbl.get(fi)
   assert(ft == CoreFuncType(['i32'], []) or ft == CoreFuncType(['i64'], []))
   trap_if(f.t != ft)
-  def thread_func(thread):
-    [] = call_and_trap_on_throw(f.callee, thread, [c])
+  def thread_func():
+    [] = call_and_trap_on_throw(f.callee, [c])
     task.unregister_thread(new_thread)
   new_thread = Thread(task, thread_func)
   assert(new_thread.suspended())
@@ -2543,7 +2607,8 @@ def thread_func(thread):
 
 ### 🧵 `canon thread.switch-to`
 
-def canon_thread_switch_to(cancellable, thread, i):
+def canon_thread_switch_to(cancellable, i):
+  thread = current_thread()
   trap_if(not thread.task.inst.may_leave)
   other_thread = thread.task.inst.threads.get(i)
   trap_if(not other_thread.suspended())
@@ -2552,7 +2617,8 @@ def canon_thread_switch_to(cancellable, thread, i):
 
 ### 🧵 `canon thread.suspend`
 
-def canon_thread_suspend(cancellable, thread):
+def canon_thread_suspend(cancellable):
+  thread = current_thread()
   trap_if(not thread.task.inst.may_leave)
   trap_if(not thread.task.may_block())
   cancelled = thread.suspend(cancellable)
@@ -2560,7 +2626,8 @@ def canon_thread_suspend(cancellable, thread):
 
 ### 🧵 `canon thread.resume-later`
 
-def canon_thread_resume_later(thread, i):
+def canon_thread_resume_later(i):
+  thread = current_thread()
   trap_if(not thread.task.inst.may_leave)
   other_thread = thread.task.inst.threads.get(i)
   trap_if(not other_thread.suspended())
@@ -2569,7 +2636,8 @@ def canon_thread_resume_later(thread, i):
 
 ### 🧵 `canon thread.yield-to`
 
-def canon_thread_yield_to(cancellable, thread, i):
+def canon_thread_yield_to(cancellable, i):
+  thread = current_thread()
   trap_if(not thread.task.inst.may_leave)
   other_thread = thread.task.inst.threads.get(i)
   trap_if(not other_thread.suspended())
@@ -2578,7 +2646,8 @@ def canon_thread_yield_to(cancellable, thread, i):
 
 ### 🧵 `canon thread.yield`
 
-def canon_thread_yield(cancellable, thread):
+def canon_thread_yield(cancellable):
+  thread = current_thread()
   trap_if(not thread.task.inst.may_leave)
   cancelled = thread.yield_(cancellable)
   return [cancelled]
@@ -2589,31 +2658,34 @@ def canon_thread_yield(cancellable, thread):
 class ErrorContext:
   debug_message: String
 
-def canon_error_context_new(opts, thread, ptr, tagged_code_units):
-  trap_if(not thread.task.inst.may_leave)
+def canon_error_context_new(opts, ptr, tagged_code_units):
+  inst = current_thread().task.inst
+  trap_if(not inst.may_leave)
   if DETERMINISTIC_PROFILE or random.randint(0,1):
     s = String(('', 'utf8', 0))
   else:
-    cx = LiftLowerContext(opts, thread.task.inst)
+    cx = LiftLowerContext(opts, inst)
     s = load_string_from_range(cx, ptr, tagged_code_units)
     s = host_defined_transformation(s)
-  i = thread.task.inst.handles.add(ErrorContext(s))
+  i = inst.handles.add(ErrorContext(s))
   return [i]
 
 ### 📝 `canon error-context.debug-message`
 
-def canon_error_context_debug_message(opts, thread, i, ptr):
-  trap_if(not thread.task.inst.may_leave)
-  errctx = thread.task.inst.handles.get(i)
+def canon_error_context_debug_message(opts, i, ptr):
+  inst = current_thread().task.inst
+  trap_if(not inst.may_leave)
+  errctx = inst.handles.get(i)
   trap_if(not isinstance(errctx, ErrorContext))
-  cx = LiftLowerContext(opts, thread.task.inst)
+  cx = LiftLowerContext(opts, inst)
   store_string(cx, errctx.debug_message, ptr)
   return []
 
 ### 📝 `canon error-context.drop`
 
-def canon_error_context_drop(thread, i):
-  trap_if(not thread.task.inst.may_leave)
-  errctx = thread.task.inst.handles.remove(i)
+def canon_error_context_drop(i):
+  inst = current_thread().task.inst
+  trap_if(not inst.may_leave)
+  errctx = inst.handles.remove(i)
   trap_if(not isinstance(errctx, ErrorContext))
   return []
diff --git a/design/mvp/canonical-abi/run_tests.py b/design/mvp/canonical-abi/run_tests.py
index 34c2abe3..7f858f1a 100644
--- a/design/mvp/canonical-abi/run_tests.py
+++ b/design/mvp/canonical-abi/run_tests.py
@@ -66,8 +66,8 @@ def mk_task(caller, on_resolve, thread_func):
   return task
 
 def mk_done_task(caller):
-  def empty(thread):
-    thread.task.state = Task.State.RESOLVED
+  def empty():
+    current_thread().task.state = Task.State.RESOLVED
   return mk_task(caller, lambda _:(), empty)
 
 def mk_str(s):
@@ -429,7 +429,7 @@ def test_roundtrip(t, v, addr_type='i32'):
     store = Store()
 
     ft = FuncType([t],[t])
-    def callee(thread, x):
+    def callee(x):
       return x
 
     callee_heap = Heap(1000)
@@ -472,7 +472,7 @@ def test_handles():
   definitions.MAX_FLAT_RESULTS = 16
 
   dtor_value = None
-  def dtor(thread, args):
+  def dtor(args):
     nonlocal dtor_value
     assert(len(args) == 1)
     dtor_value = args[0]
@@ -492,7 +492,7 @@ def host_import(caller, on_start, on_resolve):
     on_resolve([45])
     return mk_done_task(caller)
 
-  def core_wasm(thread, args):
+  def core_wasm(args):
     nonlocal dtor_value
 
     assert(len(args) == 4)
@@ -505,9 +505,9 @@ def core_wasm(thread, args):
     h1 = args[0]
     h2 = args[1]
     h3 = args[2]
-    assert((canon_resource_rep(rt, thread, h1))[0] == 42)
-    assert((canon_resource_rep(rt, thread, h2))[0] == 43)
-    assert((canon_resource_rep(rt, thread, h3))[0] == 44)
+    assert((canon_resource_rep(rt, h1))[0] == 42)
+    assert((canon_resource_rep(rt, h2))[0] == 43)
+    assert((canon_resource_rep(rt, h3))[0] == 44)
 
     host_ft = FuncType([
       BorrowType(rt),
@@ -519,27 +519,27 @@ def core_wasm(thread, args):
       h1,
       h3
     ]
-    results = canon_lower(opts, host_ft, host_import, thread, args)
+    results = canon_lower(opts, host_ft, host_import, args)
     assert(len(results) == 1)
     assert(results[0] == 4)
     h4 = results[0]
-    assert((canon_resource_rep(rt, thread, h4))[0] == 45)
+    assert((canon_resource_rep(rt, h4))[0] == 45)
 
     dtor_value = None
-    [] = canon_resource_drop(rt, thread, h1)
+    [] = canon_resource_drop(rt, h1)
     assert(dtor_value == 42)
     assert(len(inst.handles.array) == 5)
     assert(inst.handles.array[h1] is None)
     assert(len(inst.handles.free) == 1)
 
-    h = (canon_resource_new(rt, thread, 46))[0]
+    h = (canon_resource_new(rt, 46))[0]
     assert(h == h1)
     assert(len(inst.handles.array) == 5)
     assert(inst.handles.array[h] is not None)
     assert(len(inst.handles.free) == 0)
 
     dtor_value = None
-    [] = canon_resource_drop(rt, thread, h3)
+    [] = canon_resource_drop(rt, h3)
     assert(dtor_value is None)
     assert(len(inst.handles.array) == 5)
     assert(inst.handles.array[h3] is None)
@@ -587,32 +587,34 @@ def test_async_to_async():
   producer_inst = ComponentInstance(store)
 
   eager_ft = FuncType([], [U8Type()], async_=True)
-  def core_eager_producer(thread, args):
+  def core_eager_producer(args):
     assert(len(args) == 0)
-    [] = canon_task_return(thread, [U8Type()], producer_opts, [43])
+    [] = canon_task_return([U8Type()], producer_opts, [43])
     return []
   eager_callee = partial(canon_lift, producer_opts, producer_inst, eager_ft, core_eager_producer)
 
   toggle_ft = FuncType([], [], async_=True)
   fut1_1 = RacyBool(False)
   fut1_2 = RacyBool(False)
-  def core_toggle(thread, args):
+  def core_toggle(args):
+    thread = current_thread()
     assert(len(args) == 0)
-    [] = canon_backpressure_inc(thread)
+    [] = canon_backpressure_inc()
     thread.wait_until(fut1_1.is_set)
-    [] = canon_task_return(thread, [], producer_opts, [])
+    [] = canon_task_return([], producer_opts, [])
     thread.wait_until(fut1_2.is_set)
-    [] = canon_backpressure_dec(thread)
+    [] = canon_backpressure_dec()
     return []
   toggle_callee = partial(canon_lift, producer_opts, producer_inst, toggle_ft, core_toggle)
 
   fut2, fut3, fut4 = RacyBool(False), RacyBool(False), RacyBool(False)
   blocking_ft = FuncType([U8Type()], [U8Type()], async_=True)
-  def core_blocking_producer(thread, args):
+  def core_blocking_producer(args):
+    thread = current_thread()
     [x] = args
     assert(x == 83)
     thread.wait_until(fut2.is_set)
-    [] = canon_task_return(thread, [U8Type()], producer_opts, [44])
+    [] = canon_task_return([U8Type()], producer_opts, [44])
     thread.wait_until(fut3.is_set)
     fut4.set()
     return []
@@ -622,54 +624,54 @@ def core_blocking_producer(thread, args):
   consumer_opts = mk_opts(MemInst(consumer_heap.memory, 'i32'))
   consumer_opts.async_ = True
 
-  def consumer(thread, args):
+  def consumer(args):
     [b] = args
-    [seti] = canon_waitable_set_new(thread)
+    [seti] = canon_waitable_set_new()
     ptr = consumer_heap.realloc(0, 0, 1, 1)
-    [ret] = canon_lower(consumer_opts, eager_ft, eager_callee, thread, [ptr])
+    [ret] = canon_lower(consumer_opts, eager_ft, eager_callee, [ptr])
     assert(ret == Subtask.State.RETURNED)
     u8 = consumer_heap.memory[ptr]
     assert(u8 == 43)
-    [ret] = canon_lower(consumer_opts, toggle_ft, toggle_callee, thread, [])
+    [ret] = canon_lower(consumer_opts, toggle_ft, toggle_callee, [])
     state,subi1 = unpack_result(ret)
     assert(subi1 == 2)
     assert(state == Subtask.State.STARTED)
-    [] = canon_waitable_join(thread, subi1, seti)
+    [] = canon_waitable_join(subi1, seti)
     retp = ptr
     consumer_heap.memory[retp] = 13
-    [ret] = canon_lower(consumer_opts, blocking_ft, blocking_callee, thread, [83, retp])
+    [ret] = canon_lower(consumer_opts, blocking_ft, blocking_callee, [83, retp])
     state,subi2 = unpack_result(ret)
     assert(subi2 == 3)
     assert(state == Subtask.State.STARTING)
     assert(consumer_heap.memory[retp] == 13)
-    [] = canon_waitable_join(thread, subi2, seti)
+    [] = canon_waitable_join(subi2, seti)
     fut1_1.set()
 
     waitretp = consumer_heap.realloc(0, 0, 8, 4)
-    [event] = canon_waitable_set_wait(True, MemInst(consumer_heap.memory, 'i32'), thread, seti, waitretp)
+    [event] = canon_waitable_set_wait(True, MemInst(consumer_heap.memory, 'i32'), seti, waitretp)
     assert(event == EventCode.SUBTASK)
     assert(consumer_heap.memory[waitretp] == subi1)
     assert(consumer_heap.memory[waitretp+4] == Subtask.State.RETURNED)
-    [] = canon_subtask_drop(thread, subi1)
+    [] = canon_subtask_drop(subi1)
     fut1_2.set()
 
-    [event] = canon_waitable_set_wait(True, MemInst(consumer_heap.memory, 'i32'), thread, seti, waitretp)
+    [event] = canon_waitable_set_wait(True, MemInst(consumer_heap.memory, 'i32'), seti, waitretp)
     assert(event == EventCode.SUBTASK)
     assert(consumer_heap.memory[waitretp] == subi2)
     assert(consumer_heap.memory[waitretp+4] == Subtask.State.STARTED)
     assert(consumer_heap.memory[retp] == 13)
     fut2.set()
 
-    [event] = canon_waitable_set_wait(True, MemInst(consumer_heap.memory, 'i32'), thread, seti, waitretp)
+    [event] = canon_waitable_set_wait(True, MemInst(consumer_heap.memory, 'i32'), seti, waitretp)
     assert(event == EventCode.SUBTASK)
     assert(consumer_heap.memory[waitretp] == subi2)
     assert(consumer_heap.memory[waitretp+4] == Subtask.State.RETURNED)
     assert(consumer_heap.memory[retp] == 44)
-    [] = canon_subtask_drop(thread, subi2)
+    [] = canon_subtask_drop(subi2)
     fut3.set()
-    thread.wait_until(fut4.is_set)
+    current_thread().wait_until(fut4.is_set)
 
-    [] = canon_task_return(thread, [U8Type()], consumer_opts, [42])
+    [] = canon_task_return([U8Type()], consumer_opts, [42])
     return []
 
   ft = FuncType([BoolType()],[U8Type()], async_=True)
@@ -695,10 +697,10 @@ def test_async_callback():
   producer_opts.async_ = True
   producer_ft = FuncType([], [], async_ = True)
 
-  def core_producer_pre(fut, thread, args):
+  def core_producer_pre(fut, args):
     assert(len(args) == 0)
-    thread.wait_until(fut.is_set)
-    canon_task_return(thread, [], producer_opts, [])
+    current_thread().wait_until(fut.is_set)
+    canon_task_return([], producer_opts, [])
     return []
   fut1 = RacyBool(False)
   core_producer1 = partial(core_producer_pre, fut1)
@@ -709,55 +711,55 @@ def core_producer_pre(fut, thread, args):
 
   consumer_ft = FuncType([],[U32Type()], async_ = True)
   seti = 0
-  def consumer(thread, args):
+  def consumer(args):
     assert(len(args) == 0)
 
-    [ret] = canon_lower(opts, producer_ft, producer1, thread, [])
+    [ret] = canon_lower(opts, producer_ft, producer1, [])
     state,subi1 = unpack_result(ret)
     assert(subi1 == 1)
     assert(state == Subtask.State.STARTED)
 
-    [ret] = canon_lower(opts, producer_ft, producer2, thread, [])
+    [ret] = canon_lower(opts, producer_ft, producer2, [])
     state,subi2 = unpack_result(ret)
     assert(subi2 == 2)
     assert(state == Subtask.State.STARTED)
 
     nonlocal seti
-    [seti] = canon_waitable_set_new(thread)
+    [seti] = canon_waitable_set_new()
     assert(seti == 3)
-    [] = canon_waitable_join(thread, subi1, seti)
-    [] = canon_waitable_join(thread, subi2, seti)
+    [] = canon_waitable_join(subi1, seti)
+    [] = canon_waitable_join(subi2, seti)
 
     fut1.set()
-    [] = canon_context_set('i32', 0, thread, 42)
+    [] = canon_context_set('i32', 0, 42)
     return [definitions.CallbackCode.WAIT|(seti << 4)]
 
-  def callback(thread, args):
+  def callback(args):
     assert(len(args) == 3)
-    [ctx] = canon_context_get('i32', 0, thread)
+    [ctx] = canon_context_get('i32', 0)
     match ctx:
       case 42:
         assert(args[0] == EventCode.SUBTASK)
         assert(args[1] == 1)
         assert(args[2] == Subtask.State.RETURNED)
         subi = args[1]
-        canon_subtask_drop(thread, subi)
-        [] = canon_context_set('i32', 0, thread, 52)
+        canon_subtask_drop(subi)
+        [] = canon_context_set('i32', 0, 52)
         return [definitions.CallbackCode.YIELD]
       case 52:
         assert(args[0] == EventCode.NONE)
         assert(args[1] == 0)
         assert(args[2] == 0)
         fut2.set()
-        [] = canon_context_set('i32', 0, thread, 62)
+        [] = canon_context_set('i32', 0, 62)
         return [definitions.CallbackCode.WAIT | (seti << 4)]
       case 62:
         assert(args[0] == EventCode.SUBTASK)
         assert(args[1] == 2)
         assert(args[2] == Subtask.State.RETURNED)
         subi = args[1]
-        canon_subtask_drop(thread, subi)
-        [] = canon_task_return(thread, [U32Type()], opts, [83])
+        canon_subtask_drop(subi)
+        [] = canon_task_return([U32Type()], opts, [83])
         return [definitions.CallbackCode.EXIT]
       case _:
         assert(False)
@@ -783,36 +785,36 @@ def test_callback_interleaving():
   producer_inst = ComponentInstance(store)
   producer_ft = FuncType([U32Type(), FutureType(None),FutureType(None),FutureType(None)],[U32Type()], async_ = True)
   fut3s = [None,None]
-  def core_producer(thread, args):
+  def core_producer(args):
     [i,fut1,fut2,fut3] = args
     fut3s[i] = fut3
 
-    [] = canon_context_set('i32', 0, thread, i)
+    [] = canon_context_set('i32', 0, i)
 
     sync_opts = mk_opts()
-    [ret] = canon_future_read(FutureType(None), sync_opts, thread, fut1, 0xdeadbeef)
+    [ret] = canon_future_read(FutureType(None), sync_opts, fut1, 0xdeadbeef)
     assert(ret == CopyResult.COMPLETED)
 
-    [seti] = canon_waitable_set_new(thread)
+    [seti] = canon_waitable_set_new()
 
     async_opts = mk_opts(async_ = True)
-    [ret] = canon_future_read(FutureType(None), async_opts, thread, fut2, 0xdeadbeef)
+    [ret] = canon_future_read(FutureType(None), async_opts, fut2, 0xdeadbeef)
     assert(ret == definitions.BLOCKED)
 
-    [] = canon_waitable_join(thread, fut2, seti)
+    [] = canon_waitable_join(fut2, seti)
     return [CallbackCode.WAIT|(seti << 4)]
 
-  def core_producer_callback(thread, args):
+  def core_producer_callback(args):
     [event,payload1,payload2] = args
     assert(event == EventCode.FUTURE_READ)
     assert(payload2 == CopyResult.COMPLETED)
 
-    [i] = canon_context_get('i32', 0, thread)
-    [] = canon_task_return(thread, [U32Type()], mk_opts(), [42 + i])
+    [i] = canon_context_get('i32', 0)
+    [] = canon_task_return([U32Type()], mk_opts(), [42 + i])
 
     fut3 = fut3s[i]
     sync_opts = mk_opts()
-    [ret] = canon_future_read(FutureType(None), sync_opts, thread, fut3, 0xdeadbeef)
+    [ret] = canon_future_read(FutureType(None), sync_opts, fut3, 0xdeadbeef)
     assert(ret == CopyResult.COMPLETED)
 
     return [CallbackCode.EXIT]
@@ -822,7 +824,7 @@ def core_producer_callback(thread, args):
   producer_callee = partial(canon_lift, producer_opts, producer_inst, producer_ft, core_producer)
 
   sync_callee_ft = FuncType([], [U32Type()], async_ = True)
-  def core_sync_callee(thread, args):
+  def core_sync_callee(args):
     assert(len(args) == 0)
     return [100]
   sync_callee_opts = mk_opts()
@@ -832,113 +834,113 @@ def core_sync_callee(thread, args):
   consumer_ft = FuncType([], [], async_ = True)
   consumer_mem = bytearray(24)
   consumer_opts = mk_opts(MemInst(consumer_mem, 'i32'), async_ = True)
-  def core_consumer(thread, args):
+  def core_consumer(args):
     assert(len(args) == 0)
 
-    [packed] = canon_future_new(FutureType(None), thread)
+    [packed] = canon_future_new(FutureType(None))
     rfut11,wfut11 = unpack_new_ends(packed)
-    [packed] = canon_future_new(FutureType(None), thread)
+    [packed] = canon_future_new(FutureType(None))
     rfut12,wfut12 = unpack_new_ends(packed)
-    [packed] = canon_future_new(FutureType(None), thread)
+    [packed] = canon_future_new(FutureType(None))
     rfut13,wfut13 = unpack_new_ends(packed)
-    [packed] = canon_future_new(FutureType(None), thread)
+    [packed] = canon_future_new(FutureType(None))
     rfut21,wfut21 = unpack_new_ends(packed)
-    [packed] = canon_future_new(FutureType(None), thread)
+    [packed] = canon_future_new(FutureType(None))
     rfut22,wfut22 = unpack_new_ends(packed)
-    [packed] = canon_future_new(FutureType(None), thread)
+    [packed] = canon_future_new(FutureType(None))
     rfut23,wfut23 = unpack_new_ends(packed)
 
     producer_inst.backpressure = True
-    [ret] = canon_lower(consumer_opts, producer_ft, producer_callee, thread, [0, rfut11, rfut12, rfut13, 0xdeadbeef])
+    [ret] = canon_lower(consumer_opts, producer_ft, producer_callee, [0, rfut11, rfut12, rfut13, 0xdeadbeef])
     state,todie = unpack_result(ret)
     assert(state == Subtask.State.STARTING)
-    [ret] = canon_subtask_cancel(False, thread, todie)
+    [ret] = canon_subtask_cancel(False, todie)
     assert(ret == Subtask.State.CANCELLED_BEFORE_STARTED)
     producer_inst.backpressure = False
 
     subi1ret = 12
-    [ret] = canon_lower(consumer_opts, producer_ft, producer_callee, thread, [0, rfut11, rfut12, rfut13, subi1ret])
+    [ret] = canon_lower(consumer_opts, producer_ft, producer_callee, [0, rfut11, rfut12, rfut13, subi1ret])
     state,subi1 = unpack_result(ret)
     assert(state == Subtask.State.STARTED)
 
-    [ret] = canon_lower(consumer_opts, producer_ft, producer_callee, thread, [1, rfut21, rfut22, rfut23, 0xdeadbeef])
+    [ret] = canon_lower(consumer_opts, producer_ft, producer_callee, [1, rfut21, rfut22, rfut23, 0xdeadbeef])
     state,todie = unpack_result(ret)
     assert(state == Subtask.State.STARTING)
 
-    [ret] = canon_subtask_cancel(False, thread, todie)
+    [ret] = canon_subtask_cancel(False, todie)
     assert(ret == Subtask.State.CANCELLED_BEFORE_STARTED)
 
     subi2ret = 16
-    [ret] = canon_lower(consumer_opts, producer_ft, producer_callee, thread, [1, rfut21, rfut22, rfut23, subi2ret])
+    [ret] = canon_lower(consumer_opts, producer_ft, producer_callee, [1, rfut21, rfut22, rfut23, subi2ret])
     state,subi2 = unpack_result(ret)
     assert(state == Subtask.State.STARTING)
 
-    [seti] = canon_waitable_set_new(thread)
-    [] = canon_waitable_join(thread, subi1, seti)
-    [] = canon_waitable_join(thread, subi2, seti)
+    [seti] = canon_waitable_set_new()
+    [] = canon_waitable_join(subi1, seti)
+    [] = canon_waitable_join(subi2, seti)
 
-    [ret] = canon_future_write(FutureType(None), consumer_opts, thread, wfut11, 0xdeadbeef)
+    [ret] = canon_future_write(FutureType(None), consumer_opts, wfut11, 0xdeadbeef)
     assert(ret == CopyResult.COMPLETED)
 
     retp = 0
-    [event] = canon_waitable_set_wait(True, MemInst(consumer_mem, 'i32'), thread, seti, retp)
+    [event] = canon_waitable_set_wait(True, MemInst(consumer_mem, 'i32'), seti, retp)
     assert(event == EventCode.SUBTASK)
     assert(consumer_mem[retp+0] == subi2)
     assert(consumer_mem[retp+4] == Subtask.State.STARTED)
 
-    [ret] = canon_future_write(FutureType(None), consumer_opts, thread, wfut12, 0xdeadbeef)
+    [ret] = canon_future_write(FutureType(None), consumer_opts, wfut12, 0xdeadbeef)
     assert(ret == CopyResult.COMPLETED)
 
     for i in range(10):
-      [ret] = canon_thread_yield(True, thread)
+      [ret] = canon_thread_yield(True)
       assert(ret == 0)
       retp = 0
-      [ret] = canon_waitable_set_poll(True, MemInst(consumer_mem, 'i32'), thread, seti, retp)
+      [ret] = canon_waitable_set_poll(True, MemInst(consumer_mem, 'i32'), seti, retp)
       assert(ret == EventCode.NONE)
 
-    [ret] = canon_future_write(FutureType(None), consumer_opts, thread, wfut21, 0xdeadbeef)
+    [ret] = canon_future_write(FutureType(None), consumer_opts, wfut21, 0xdeadbeef)
     assert(ret == CopyResult.COMPLETED)
 
     retp = 0
-    [event] = canon_waitable_set_wait(True, MemInst(consumer_mem, 'i32'), thread, seti, retp)
+    [event] = canon_waitable_set_wait(True, MemInst(consumer_mem, 'i32'), seti, retp)
     assert(event == EventCode.SUBTASK)
     assert(consumer_mem[retp+0] == subi1)
     assert(consumer_mem[retp+4] == Subtask.State.RETURNED)
     assert(consumer_mem[subi1ret] == 42)
-    [] = canon_subtask_drop(thread, subi1)
+    [] = canon_subtask_drop(subi1)
 
-    [ret] = canon_future_write(FutureType(None), consumer_opts, thread, wfut22, 0xdeadbeef)
+    [ret] = canon_future_write(FutureType(None), consumer_opts, wfut22, 0xdeadbeef)
     assert(ret == CopyResult.COMPLETED)
 
     for i in range(10):
-      [ret] = canon_thread_yield(True, thread)
+      [ret] = canon_thread_yield(True)
       assert(ret == 0)
       retp = 0
-      [ret] = canon_waitable_set_poll(True, MemInst(consumer_mem, 'i32'), thread, seti, retp)
+      [ret] = canon_waitable_set_poll(True, MemInst(consumer_mem, 'i32'), seti, retp)
       assert(ret == EventCode.NONE)
 
-    [ret] = canon_future_write(FutureType(None), consumer_opts, thread, wfut13, 0xdeadbeef)
+    [ret] = canon_future_write(FutureType(None), consumer_opts, wfut13, 0xdeadbeef)
     assert(ret == CopyResult.COMPLETED)
 
     retp = 0
-    [event] = canon_waitable_set_wait(True, MemInst(consumer_mem, 'i32'), thread, seti, retp)
+    [event] = canon_waitable_set_wait(True, MemInst(consumer_mem, 'i32'), seti, retp)
     assert(event == EventCode.SUBTASK)
     assert(consumer_mem[retp+0] == subi2)
     assert(consumer_mem[retp+4] == Subtask.State.RETURNED)
     assert(consumer_mem[subi2ret] == 43)
-    [] = canon_subtask_drop(thread, subi2)
+    [] = canon_subtask_drop(subi2)
 
     subi3ret = 20
-    [ret] = canon_lower(consumer_opts, sync_callee_ft, sync_callee, thread, [subi3ret])
+    [ret] = canon_lower(consumer_opts, sync_callee_ft, sync_callee, [subi3ret])
     state,subi3 = unpack_result(ret)
     assert(state == Subtask.State.STARTING)
-    [] = canon_waitable_join(thread, subi3, seti)
+    [] = canon_waitable_join(subi3, seti)
 
-    [ret] = canon_future_write(FutureType(None), consumer_opts, thread, wfut23, 0xdeadbeef)
+    [ret] = canon_future_write(FutureType(None), consumer_opts, wfut23, 0xdeadbeef)
     assert(ret == CopyResult.COMPLETED)
 
     retp = 0
-    [event] = canon_waitable_set_wait(True, MemInst(consumer_mem, 'i32'), thread, seti, retp)
+    [event] = canon_waitable_set_wait(True, MemInst(consumer_mem, 'i32'), seti, retp)
     assert(event == EventCode.SUBTASK)
     assert(consumer_mem[retp+0] == subi3)
     assert(consumer_mem[retp+4] == Subtask.State.RETURNED)
@@ -957,15 +959,15 @@ def test_sync_ignores_backpressure():
   callee_inst = ComponentInstance(store)
 
   async_ft = FuncType([U32Type(), FutureType(None)],[U32Type()], async_ = True)
-  def core_callee1(thread, args):
+  def core_callee1(args):
     [i,fut] = args
-    [ret] = canon_future_read(FutureType(None), sync_opts, thread, fut, 0xdeadbeef)
+    [ret] = canon_future_read(FutureType(None), sync_opts, fut, 0xdeadbeef)
     assert(ret == CopyResult.COMPLETED)
     return [42 + i]
   async_callee = partial(canon_lift, sync_opts, callee_inst, async_ft, core_callee1)
 
   sync_ft = FuncType([U32Type()], [U32Type()])
-  def core_callee2(thread, args):
+  def core_callee2(args):
     [i] = args
     return [84 + i]
   sync_callee = partial(canon_lift, sync_opts, callee_inst, sync_ft, core_callee2)
@@ -974,34 +976,34 @@ def core_callee2(thread, args):
   caller_ft = FuncType([], [], async_ = True)
   caller_mem = bytearray(24)
   caller_opts = mk_opts(memory = MemInst(caller_mem, 'i32'), async_ = True)
-  def core_caller(thread, args):
+  def core_caller(args):
     assert(len(args) == 0)
 
-    [packed] = canon_future_new(FutureType(None), thread)
+    [packed] = canon_future_new(FutureType(None))
     rfut,wfut = unpack_new_ends(packed)
 
     retp1 = 4
-    [ret] = canon_lower(caller_opts, async_ft, async_callee, thread, [1, rfut, retp1])
+    [ret] = canon_lower(caller_opts, async_ft, async_callee, [1, rfut, retp1])
     state,subi = unpack_result(ret)
     assert(state == Subtask.State.STARTED)
 
     retp2 = 8
-    [ret] = canon_lower(caller_opts, sync_ft, sync_callee, thread, [2, retp2])
+    [ret] = canon_lower(caller_opts, sync_ft, sync_callee, [2, retp2])
     assert(ret == Subtask.State.RETURNED)
     assert(caller_mem[retp2] == 86)
 
-    [ret] = canon_future_write(FutureType(None), sync_opts, thread, wfut, 0xdeadbeef)
+    [ret] = canon_future_write(FutureType(None), sync_opts, wfut, 0xdeadbeef)
     assert(ret == CopyResult.COMPLETED)
 
-    [seti] = canon_waitable_set_new(thread)
-    [] = canon_waitable_join(thread, subi, seti)
+    [seti] = canon_waitable_set_new()
+    [] = canon_waitable_join(subi, seti)
     retp3 = 12
-    [event] = canon_waitable_set_wait(True, MemInst(caller_mem, 'i32'), thread, seti, retp3)
+    [event] = canon_waitable_set_wait(True, MemInst(caller_mem, 'i32'), seti, retp3)
     assert(event == EventCode.SUBTASK)
     assert(caller_mem[retp3+0] == subi)
     assert(caller_mem[retp3+4] == Subtask.State.RETURNED)
 
-    [] = canon_subtask_drop(thread, subi)
+    [] = canon_subtask_drop(subi)
     return []
 
   run_lift(mk_opts(), caller_inst, caller_ft, core_caller, lambda:[], lambda _:())
@@ -1014,15 +1016,15 @@ def test_async_to_sync():
   producer_ft = FuncType([],[], async_ = True)
   fut = RacyBool(False)
   producer1_done = False
-  def producer1_core(thread, args):
+  def producer1_core(args):
     nonlocal producer1_done
     assert(len(args) == 0)
-    thread.wait_until(fut.is_set)
+    current_thread().wait_until(fut.is_set)
     producer1_done = True
     return []
 
   producer2_done = False
-  def producer2_core(thread, args):
+  def producer2_core(args):
     nonlocal producer2_done
     assert(len(args) == 0)
     assert(producer1_done == True)
@@ -1037,22 +1039,22 @@ def producer2_core(thread, args):
   consumer_opts.async_ = True
 
   consumer_ft = FuncType([],[U8Type()], async_ = True)
-  def consumer(thread, args):
+  def consumer(args):
     assert(len(args) == 0)
 
-    [ret] = canon_lower(consumer_opts, producer_ft, producer1, thread, [])
+    [ret] = canon_lower(consumer_opts, producer_ft, producer1, [])
     state,subi1 = unpack_result(ret)
     assert(subi1 == 1)
     assert(state == Subtask.State.STARTED)
 
-    [ret] = canon_lower(consumer_opts, producer_ft, producer2, thread, [])
+    [ret] = canon_lower(consumer_opts, producer_ft, producer2, [])
     state,subi2 = unpack_result(ret)
     assert(subi2 == 2)
     assert(state == Subtask.State.STARTING)
 
-    [seti] = canon_waitable_set_new(thread)
-    [] = canon_waitable_join(thread, subi1, seti)
-    [] = canon_waitable_join(thread, subi2, seti)
+    [seti] = canon_waitable_set_new()
+    [] = canon_waitable_join(subi1, seti)
+    [] = canon_waitable_join(subi2, seti)
 
     fut.set()
     assert(producer1_done == False)
@@ -1060,24 +1062,24 @@ def consumer(thread, args):
 
     remain = [subi1, subi2]
     while remain:
-      [ret] = canon_thread_yield(True, thread)
+      [ret] = canon_thread_yield(True)
       assert(ret == 0)
       retp = 8
-      [event] = canon_waitable_set_poll(True, MemInst(consumer_heap.memory, 'i32'), thread, seti, retp)
+      [event] = canon_waitable_set_poll(True, MemInst(consumer_heap.memory, 'i32'), seti, retp)
       if event == EventCode.NONE:
         continue
       assert(event == EventCode.SUBTASK)
       assert(consumer_heap.memory[retp+4] == Subtask.State.RETURNED)
       subi = consumer_heap.memory[retp]
       remain.remove(subi)
-      canon_subtask_drop(thread, subi)
+      canon_subtask_drop(subi)
 
     assert(producer1_done == True)
     assert(producer2_done == True)
 
-    [] = canon_waitable_set_drop(thread, seti)
+    [] = canon_waitable_set_drop(seti)
 
-    canon_task_return(thread, [U8Type()], consumer_opts, [83])
+    canon_task_return([U8Type()], consumer_opts, [83])
     return []
 
   consumer_inst = ComponentInstance(store)
@@ -1101,20 +1103,20 @@ def test_async_backpressure():
   producer_ft = FuncType([],[], async_ = True)
   fut = RacyBool(False)
   producer1_done = False
-  def producer1_core(thread, args):
+  def producer1_core(args):
     nonlocal producer1_done
-    canon_backpressure_inc(thread)
-    thread.wait_until(fut.is_set)
-    canon_backpressure_dec(thread)
-    canon_task_return(thread, [], producer_opts, [])
+    canon_backpressure_inc()
+    current_thread().wait_until(fut.is_set)
+    canon_backpressure_dec()
+    canon_task_return([], producer_opts, [])
     producer1_done = True
     return []
 
   producer2_done = False
-  def producer2_core(thread, args):
+  def producer2_core(args):
     nonlocal producer2_done
     assert(producer1_done == True)
-    canon_task_return(thread, [], producer_opts, [])
+    canon_task_return([], producer_opts, [])
     producer2_done = True
     return []
 
@@ -1125,22 +1127,22 @@ def producer2_core(thread, args):
   consumer_opts = mk_opts(MemInst(consumer_heap.memory, 'i32'), async_ = True)
 
   consumer_ft = FuncType([],[U8Type()], async_ = True)
-  def consumer(thread, args):
+  def consumer(args):
     assert(len(args) == 0)
 
-    [ret] = canon_lower(consumer_opts, producer_ft, producer1, thread, [])
+    [ret] = canon_lower(consumer_opts, producer_ft, producer1, [])
     state,subi1 = unpack_result(ret)
     assert(subi1 == 1)
     assert(state == Subtask.State.STARTED)
 
-    [ret] = canon_lower(consumer_opts, producer_ft, producer2, thread, [])
+    [ret] = canon_lower(consumer_opts, producer_ft, producer2, [])
     state,subi2 = unpack_result(ret)
     assert(subi2 == 2)
     assert(state == Subtask.State.STARTING)
 
-    [seti] = canon_waitable_set_new(thread)
-    [] = canon_waitable_join(thread, subi1, seti)
-    [] = canon_waitable_join(thread, subi2, seti)
+    [seti] = canon_waitable_set_new()
+    [] = canon_waitable_join(subi1, seti)
+    [] = canon_waitable_join(subi2, seti)
 
     fut.set()
     assert(producer1_done == False)
@@ -1149,19 +1151,19 @@ def consumer(thread, args):
     remain = [subi1, subi2]
     while remain:
       retp = 8
-      [event] = canon_waitable_set_wait(True, MemInst(consumer_heap.memory, 'i32'), thread, seti, retp)
+      [event] = canon_waitable_set_wait(True, MemInst(consumer_heap.memory, 'i32'), seti, retp)
       assert(event == EventCode.SUBTASK)
       assert(consumer_heap.memory[retp+4] == Subtask.State.RETURNED)
       subi = consumer_heap.memory[retp]
       remain.remove(subi)
-      canon_subtask_drop(thread, subi)
+      canon_subtask_drop(subi)
 
     assert(producer1_done == True)
     assert(producer2_done == True)
 
-    [] = canon_waitable_set_drop(thread, seti)
+    [] = canon_waitable_set_drop(seti)
 
-    canon_task_return(thread, [U8Type()], consumer_opts, [84])
+    canon_task_return([U8Type()], consumer_opts, [84])
     return []
 
   consumer_inst = ComponentInstance(store)
@@ -1183,9 +1185,9 @@ def test_sync_using_wait():
   hostcall_inst = ComponentInstance(store)
   ft = FuncType([], [], async_ = True)
 
-  def core_hostcall_pre(fut, thread, args):
-    thread.wait_until(fut.is_set)
-    [] = canon_task_return(thread, [], hostcall_opts, [])
+  def core_hostcall_pre(fut, args):
+    current_thread().wait_until(fut.is_set)
+    [] = canon_task_return([], hostcall_opts, [])
     return []
   fut1 = RacyBool(False)
   core_hostcall1 = partial(core_hostcall_pre, fut1)
@@ -1198,38 +1200,38 @@ def core_hostcall_pre(fut, thread, args):
   lower_opts = mk_opts(MemInst(lower_heap.memory, 'i32'))
   lower_opts.async_ = True
 
-  def core_func(thread, args):
-    [ret] = canon_lower(lower_opts, ft, hostcall1, thread, [])
+  def core_func(args):
+    [ret] = canon_lower(lower_opts, ft, hostcall1, [])
     state,subi1 = unpack_result(ret)
     assert(subi1 == 1)
     assert(state == Subtask.State.STARTED)
-    [ret] = canon_lower(lower_opts, ft, hostcall2, thread, [])
+    [ret] = canon_lower(lower_opts, ft, hostcall2, [])
     state,subi2 = unpack_result(ret)
     assert(subi2 == 2)
     assert(state == Subtask.State.STARTED)
 
-    [seti] = canon_waitable_set_new(thread)
-    [] = canon_waitable_join(thread, subi1, seti)
-    [] = canon_waitable_join(thread, subi2, seti)
+    [seti] = canon_waitable_set_new()
+    [] = canon_waitable_join(subi1, seti)
+    [] = canon_waitable_join(subi2, seti)
 
     fut1.set()
 
     retp = lower_heap.realloc(0,0,8,4)
-    [event] = canon_waitable_set_wait(True, MemInst(lower_heap.memory, 'i32'), thread, seti, retp)
+    [event] = canon_waitable_set_wait(True, MemInst(lower_heap.memory, 'i32'), seti, retp)
     assert(event == EventCode.SUBTASK)
     assert(lower_heap.memory[retp] == subi1)
     assert(lower_heap.memory[retp+4] == Subtask.State.RETURNED)
 
     fut2.set()
 
-    [event] = canon_waitable_set_wait(True, MemInst(lower_heap.memory, 'i32'), thread, seti, retp)
+    [event] = canon_waitable_set_wait(True, MemInst(lower_heap.memory, 'i32'), seti, retp)
     assert(event == EventCode.SUBTASK)
     assert(lower_heap.memory[retp] == subi2)
     assert(lower_heap.memory[retp+4] == Subtask.State.RETURNED)
 
-    canon_subtask_drop(thread, subi1)
-    canon_subtask_drop(thread, subi2)
-    canon_waitable_set_drop(thread, seti)
+    canon_subtask_drop(subi1)
+    canon_subtask_drop(subi2)
+    canon_waitable_set_drop(seti)
 
     return []
 
@@ -1429,49 +1431,49 @@ def on_resolve(results):
     nonlocal dst_stream
     dst_stream = HostSink(results[0], chunk=4)
 
-  def core_func(thread, args):
+  def core_func(args):
     assert(len(args) == 1)
     rsi1 = args[0]
     assert(rsi1 == 1)
-    [packed] = canon_stream_new(StreamType(U8Type()), thread)
+    [packed] = canon_stream_new(StreamType(U8Type()))
     rsi2,wsi2 = unpack_new_ends(packed)
-    [] = canon_task_return(thread, [StreamType(U8Type())], opts, [rsi2])
-    [ret] = canon_stream_read(StreamType(U8Type()), opts, thread, rsi1, 0, 4)
+    [] = canon_task_return([StreamType(U8Type())], opts, [rsi2])
+    [ret] = canon_stream_read(StreamType(U8Type()), opts, rsi1, 0, 4)
     result,n = unpack_result(ret)
     assert(n == 4 and result == CopyResult.COMPLETED)
     assert(mem[0:4] == b'\x01\x02\x03\x04')
-    [packed] = canon_stream_new(StreamType(U8Type()), thread)
+    [packed] = canon_stream_new(StreamType(U8Type()))
     rsi3,wsi3 = unpack_new_ends(packed)
     retp = 12
-    [ret] = canon_lower(opts, ft, host_import, thread, [rsi3, retp])
+    [ret] = canon_lower(opts, ft, host_import, [rsi3, retp])
     assert(ret == Subtask.State.RETURNED)
     rsi4 = mem[retp]
-    [ret] = canon_stream_write(StreamType(U8Type()), opts, thread, wsi3, 0, 4)
+    [ret] = canon_stream_write(StreamType(U8Type()), opts, wsi3, 0, 4)
     result,n = unpack_result(ret)
     assert(n == 4 and result == CopyResult.COMPLETED)
-    [ret] = canon_stream_read(StreamType(U8Type()), sync_opts, thread, rsi4, 0, 4)
+    [ret] = canon_stream_read(StreamType(U8Type()), sync_opts, rsi4, 0, 4)
     result,n = unpack_result(ret)
     assert(n == 4 and result == CopyResult.COMPLETED)
-    [ret] = canon_stream_write(StreamType(U8Type()), opts, thread, wsi2, 0, 4)
+    [ret] = canon_stream_write(StreamType(U8Type()), opts, wsi2, 0, 4)
     result,n = unpack_result(ret)
     assert(n == 4 and result == CopyResult.COMPLETED)
-    [ret] = canon_stream_read(StreamType(U8Type()), opts, thread, rsi1, 0, 4)
+    [ret] = canon_stream_read(StreamType(U8Type()), opts, rsi1, 0, 4)
     result,n = unpack_result(ret)
     assert(n == 4 and result == CopyResult.DROPPED)
     assert(mem[0:4] == b'\x05\x06\x07\x08')
-    [ret] = canon_stream_write(StreamType(U8Type()), sync_opts, thread, wsi3, 0, 4)
+    [ret] = canon_stream_write(StreamType(U8Type()), sync_opts, wsi3, 0, 4)
     result,n = unpack_result(ret)
     assert(n == 4 and result == CopyResult.COMPLETED)
-    [ret] = canon_stream_read(StreamType(U8Type()), sync_opts, thread, rsi4, 0, 4)
+    [ret] = canon_stream_read(StreamType(U8Type()), sync_opts, rsi4, 0, 4)
     result,n = unpack_result(ret)
     assert(n == 4 and result == CopyResult.COMPLETED)
-    [ret] = canon_stream_write(StreamType(U8Type()), sync_opts, thread, wsi2, 0, 4)
+    [ret] = canon_stream_write(StreamType(U8Type()), sync_opts, wsi2, 0, 4)
     result,n = unpack_result(ret)
     assert(n == 4 and result == CopyResult.COMPLETED)
-    [] = canon_stream_drop_readable(StreamType(U8Type()), thread, rsi1)
-    [] = canon_stream_drop_readable(StreamType(U8Type()), thread, rsi4)
-    [] = canon_stream_drop_writable(StreamType(U8Type()), thread, wsi2)
-    [] = canon_stream_drop_writable(StreamType(U8Type()), thread, wsi3)
+    [] = canon_stream_drop_readable(StreamType(U8Type()), rsi1)
+    [] = canon_stream_drop_readable(StreamType(U8Type()), rsi4)
+    [] = canon_stream_drop_writable(StreamType(U8Type()), wsi2)
+    [] = canon_stream_drop_writable(StreamType(U8Type()), wsi3)
     return []
 
   run_lift(opts, inst, ft, core_func, on_start, on_resolve)
@@ -1489,7 +1491,8 @@ def test_async_stream_ops():
   host_import_incoming = None
   host_import_outgoing = None
   def host_import(caller, on_start, on_resolve):
-    def thread_func(thread):
+    def thread_func():
+      thread = current_thread()
       nonlocal host_import_incoming, host_import_outgoing
       args = on_start()
       assert(len(args) == 1)
@@ -1526,79 +1529,79 @@ def on_resolve(results):
     nonlocal dst_stream
     dst_stream = HostSink(results[0], chunk=4, remain = 0)
 
-  def core_func(thread, args):
+  def core_func(args):
     [rsi1] = args
     assert(rsi1 == 1)
-    [packed] = canon_stream_new(StreamType(U8Type()), thread)
+    [packed] = canon_stream_new(StreamType(U8Type()),)
     rsi2,wsi2 = unpack_new_ends(packed)
-    [] = canon_task_return(thread, [StreamType(U8Type())], opts, [rsi2])
-    [ret] = canon_stream_read(StreamType(U8Type()), opts, thread, rsi1, 0, 4)
+    [] = canon_task_return([StreamType(U8Type())], opts, [rsi2])
+    [ret] = canon_stream_read(StreamType(U8Type()), opts, rsi1, 0, 4)
     assert(ret == definitions.BLOCKED)
     src_stream.write([1,2,3,4])
     retp = 16
-    [seti] = canon_waitable_set_new(thread)
-    [] = canon_waitable_join(thread, rsi1, seti)
+    [seti] = canon_waitable_set_new()
+    [] = canon_waitable_join(rsi1, seti)
     definitions.throw_it = True
-    [event] = canon_waitable_set_wait(True, MemInst(mem, 'i32'), thread, seti, retp) ##
+    [event] = canon_waitable_set_wait(True, MemInst(mem, 'i32'), seti, retp) ##
     assert(event == EventCode.STREAM_READ)
     assert(mem[retp+0] == rsi1)
     result,n = unpack_result(mem[retp+4])
     assert(n == 4 and result == CopyResult.COMPLETED)
     assert(mem[0:4] == b'\x01\x02\x03\x04')
-    [packed] = canon_stream_new(StreamType(U8Type()), thread)
+    [packed] = canon_stream_new(StreamType(U8Type()))
     rsi3,wsi3 = unpack_new_ends(packed)
-    [ret] = canon_lower(opts, ft, host_import, thread, [rsi3, retp])
+    [ret] = canon_lower(opts, ft, host_import, [rsi3, retp])
     assert(ret == Subtask.State.RETURNED)
     rsi4 = mem[16]
     assert(rsi4 == 4)
-    [ret] = canon_stream_write(StreamType(U8Type()), opts, thread, wsi3, 0, 4)
+    [ret] = canon_stream_write(StreamType(U8Type()), opts, wsi3, 0, 4)
     assert(ret == definitions.BLOCKED)
     host_import_incoming.set_remain(100)
-    [] = canon_waitable_join(thread, wsi3, seti)
-    [event] = canon_waitable_set_wait(True, MemInst(mem, 'i32'), thread, seti, retp)
+    [] = canon_waitable_join(wsi3, seti)
+    [event] = canon_waitable_set_wait(True, MemInst(mem, 'i32'), seti, retp)
     assert(event == EventCode.STREAM_WRITE)
     assert(mem[retp+0] == wsi3)
     result,n = unpack_result(mem[retp+4])
     assert(n == 4 and result == CopyResult.COMPLETED)
-    [ret] = canon_stream_read(StreamType(U8Type()), sync_opts, thread, rsi4, 0, 4)
+    [ret] = canon_stream_read(StreamType(U8Type()), sync_opts, rsi4, 0, 4)
     result,n = unpack_result(ret)
     assert(n == 4 and result == CopyResult.COMPLETED)
-    [ret] = canon_stream_write(StreamType(U8Type()), opts, thread, wsi2, 0, 4)
+    [ret] = canon_stream_write(StreamType(U8Type()), opts, wsi2, 0, 4)
     assert(ret == definitions.BLOCKED)
     dst_stream.set_remain(100)
-    [] = canon_waitable_join(thread, wsi2, seti)
-    [event] = canon_waitable_set_wait(True, MemInst(mem, 'i32'), thread, seti, retp)
+    [] = canon_waitable_join(wsi2, seti)
+    [event] = canon_waitable_set_wait(True, MemInst(mem, 'i32'), seti, retp)
     assert(event == EventCode.STREAM_WRITE)
     assert(mem[retp+0] == wsi2)
     result,n = unpack_result(mem[retp+4])
     assert(n == 4 and result == CopyResult.COMPLETED)
     src_stream.write([5,6,7,8])
     src_stream.destroy_once_empty()
-    [ret] = canon_stream_read(StreamType(U8Type()), opts, thread, rsi1, 0, 4)
+    [ret] = canon_stream_read(StreamType(U8Type()), opts, rsi1, 0, 4)
     result,n = unpack_result(ret)
     assert(n == 4 and result == CopyResult.DROPPED)
-    [] = canon_stream_drop_readable(StreamType(U8Type()), thread, rsi1)
+    [] = canon_stream_drop_readable(StreamType(U8Type()), rsi1)
     assert(mem[0:4] == b'\x05\x06\x07\x08')
-    [ret] = canon_stream_write(StreamType(U8Type()), opts, thread, wsi3, 0, 4)
+    [ret] = canon_stream_write(StreamType(U8Type()), opts, wsi3, 0, 4)
     result,n = unpack_result(ret)
     assert(n == 4 and result == CopyResult.COMPLETED)
-    [] = canon_stream_drop_writable(StreamType(U8Type()), thread, wsi3)
-    [ret] = canon_stream_read(StreamType(U8Type()), opts, thread, rsi4, 0, 4)
+    [] = canon_stream_drop_writable(StreamType(U8Type()), wsi3)
+    [ret] = canon_stream_read(StreamType(U8Type()), opts, rsi4, 0, 4)
     assert(ret == definitions.BLOCKED)
-    [] = canon_waitable_join(thread, rsi4, seti)
-    [event] = canon_waitable_set_wait(True, MemInst(mem, 'i32'), thread, seti, retp)
+    [] = canon_waitable_join(rsi4, seti)
+    [event] = canon_waitable_set_wait(True, MemInst(mem, 'i32'), seti, retp)
     assert(event == EventCode.STREAM_READ)
     assert(mem[retp+0] == rsi4)
     result,n = unpack_result(mem[retp+4])
     assert(n == 4 and result == CopyResult.COMPLETED)
-    [ret] = canon_stream_read(StreamType(U8Type()), sync_opts, thread, rsi4, 0, 4)
+    [ret] = canon_stream_read(StreamType(U8Type()), sync_opts, rsi4, 0, 4)
     assert(ret == CopyResult.DROPPED)
-    [] = canon_stream_drop_readable(StreamType(U8Type()), thread, rsi4)
-    [ret] = canon_stream_write(StreamType(U8Type()), opts, thread, wsi2, 0, 4)
+    [] = canon_stream_drop_readable(StreamType(U8Type()), rsi4)
+    [ret] = canon_stream_write(StreamType(U8Type()), opts, wsi2, 0, 4)
     result,n = unpack_result(ret)
     assert(n == 4 and result == CopyResult.COMPLETED)
-    [] = canon_stream_drop_writable(StreamType(U8Type()), thread, wsi2)
-    [] = canon_waitable_set_drop(thread, seti)
+    [] = canon_stream_drop_writable(StreamType(U8Type()), wsi2)
+    [] = canon_waitable_set_drop(seti)
     return []
 
   run_lift(opts, inst, ft, core_func, on_start, on_resolve)
@@ -1616,7 +1619,7 @@ def on_resolve(results):
     nonlocal dst_stream
     dst_stream = results[0]
 
-  def core_func(thread, args):
+  def core_func(args):
     assert(len(args) == 1)
     rsi1 = args[0]
     assert(rsi1 == 1)
@@ -1643,23 +1646,23 @@ def host_import(caller, on_start, on_resolve):
     on_resolve(args)
     return mk_done_task(caller)
 
-  def core_func(thread, args):
+  def core_func(args):
     assert(len(args) == 0)
-    [packed] = canon_stream_new(StreamType(U8Type()), thread)
+    [packed] = canon_stream_new(StreamType(U8Type()))
     rsi,wsi = unpack_new_ends(packed)
     assert(rsi == 1)
     assert(wsi == 2)
-    [ret] = canon_stream_write(StreamType(U8Type()), opts, thread, wsi, 0, 4)
+    [ret] = canon_stream_write(StreamType(U8Type()), opts, wsi, 0, 4)
     assert(ret == definitions.BLOCKED)
     retp = 8
-    [ret] = canon_lower(opts, host_ft, host_import, thread, [rsi, retp])
+    [ret] = canon_lower(opts, host_ft, host_import, [rsi, retp])
     assert(ret == Subtask.State.RETURNED)
     rsi2 = int.from_bytes(mem[retp : retp+4], 'little', signed=False)
     assert(rsi2 == 1)
-    [ret] = canon_stream_cancel_write(StreamType(U8Type()), False, thread, wsi)
+    [ret] = canon_stream_cancel_write(StreamType(U8Type()), False, wsi)
     result,n = unpack_result(ret)
     assert(result == CopyResult.CANCELLED and n == 0)
-    [] = canon_stream_drop_writable(StreamType(U8Type()), thread, wsi)
+    [] = canon_stream_drop_writable(StreamType(U8Type()), wsi)
     return []
 
   def on_start(): return []
@@ -1689,56 +1692,56 @@ def host_sink(caller, on_start, on_resolve):
     on_resolve([])
     return mk_done_task(caller)
 
-  def core_func(thread, args):
+  def core_func(args):
     assert(len(args) == 0)
     retp = 4
-    [ret] = canon_lower(opts, source_ft, host_source, thread, [retp])
+    [ret] = canon_lower(opts, source_ft, host_source, [retp])
     assert(ret == Subtask.State.RETURNED)
     rsi = mem[retp]
     assert(rsi == 1)
-    [ret] = canon_stream_read(StreamType(U8Type()), opts, thread, rsi, 0, 4)
+    [ret] = canon_stream_read(StreamType(U8Type()), opts, rsi, 0, 4)
     result,n = unpack_result(ret)
     assert(n == 2 and result == CopyResult.COMPLETED)
     assert(mem[0:2] == b'\x01\x02')
-    [ret] = canon_stream_read(StreamType(U8Type()), opts, thread, rsi, 0, 4)
+    [ret] = canon_stream_read(StreamType(U8Type()), opts, rsi, 0, 4)
     result,n = unpack_result(ret)
     assert(n == 2 and result == CopyResult.COMPLETED)
     assert(mem[0:2] == b'\x03\x04')
-    [ret] = canon_stream_read(StreamType(U8Type()), opts, thread, rsi, 0, 4)
+    [ret] = canon_stream_read(StreamType(U8Type()), opts, rsi, 0, 4)
     assert(ret == definitions.BLOCKED)
     src.write([5,6])
 
-    [seti] = canon_waitable_set_new(thread)
-    [] = canon_waitable_join(thread, rsi, seti)
-    [event] = canon_waitable_set_wait(True, MemInst(mem, 'i32'), thread, seti, retp)
+    [seti] = canon_waitable_set_new()
+    [] = canon_waitable_join(rsi, seti)
+    [event] = canon_waitable_set_wait(True, MemInst(mem, 'i32'), seti, retp)
     assert(event == EventCode.STREAM_READ)
     assert(mem[retp+0] == rsi)
     result,n = unpack_result(mem[retp+4])
     assert(n == 2 and result == CopyResult.COMPLETED)
-    [] = canon_stream_drop_readable(StreamType(U8Type()), thread, rsi)
+    [] = canon_stream_drop_readable(StreamType(U8Type()), rsi)
 
-    [packed] = canon_stream_new(StreamType(U8Type()), thread)
+    [packed] = canon_stream_new(StreamType(U8Type()))
     rsi,wsi = unpack_new_ends(packed)
     assert(rsi == 1)
     assert(wsi == 3)
-    [ret] = canon_lower(opts, sink_ft, host_sink, thread, [rsi])
+    [ret] = canon_lower(opts, sink_ft, host_sink, [rsi])
     assert(ret == Subtask.State.RETURNED)
     mem[0:6] = b'\x01\x02\x03\x04\x05\x06'
-    [ret] = canon_stream_write(StreamType(U8Type()), opts, thread, wsi, 0, 6)
+    [ret] = canon_stream_write(StreamType(U8Type()), opts, wsi, 0, 6)
     result,n = unpack_result(ret)
     assert(n == 2 and result == CopyResult.COMPLETED)
-    [ret] = canon_stream_write(StreamType(U8Type()), opts, thread, wsi, 2, 4)
+    [ret] = canon_stream_write(StreamType(U8Type()), opts, wsi, 2, 4)
     assert(ret == definitions.BLOCKED)
     dst.set_remain(4)
-    [] = canon_waitable_join(thread, wsi, seti)
-    [event] = canon_waitable_set_wait(True, MemInst(mem, 'i32'), thread, seti, retp)
+    [] = canon_waitable_join(wsi, seti)
+    [event] = canon_waitable_set_wait(True, MemInst(mem, 'i32'), seti, retp)
     assert(event == EventCode.STREAM_WRITE)
     assert(mem[retp+0] == wsi)
     result,n = unpack_result(mem[retp+4])
     assert(n == 4 and result == CopyResult.COMPLETED)
     assert(dst.received == [1,2,3,4,5,6])
-    [] = canon_stream_drop_writable(StreamType(U8Type()), thread, wsi)
-    [] = canon_waitable_set_drop(thread, seti)
+    [] = canon_stream_drop_writable(StreamType(U8Type()), wsi)
+    [] = canon_waitable_set_drop(seti)
     dst.set_remain(100)
     assert(dst.consume(100) is None)
     return []
@@ -1759,62 +1762,63 @@ def test_wasm_to_wasm_stream():
   mem1 = bytearray(24)
   opts1 = mk_opts(memory=MemInst(mem1, 'i32'), async_=True)
   ft1 = FuncType([], [StreamType(U8Type())])
-  def core_func1(thread, args):
+  def core_func1(args):
+    thread = current_thread()
     assert(not args)
-    [packed] = canon_stream_new(StreamType(U8Type()), thread)
+    [packed] = canon_stream_new(StreamType(U8Type()))
     rsi,wsi = unpack_new_ends(packed)
-    [] = canon_task_return(thread, [StreamType(U8Type())], opts1, [rsi])
+    [] = canon_task_return([StreamType(U8Type())], opts1, [rsi])
 
     thread.wait_until(fut1.is_set)
 
     mem1[0:4] = b'\x01\x02\x03\x04'
-    [ret] = canon_stream_write(StreamType(U8Type()), opts1, thread, wsi, 0, 4)
+    [ret] = canon_stream_write(StreamType(U8Type()), opts1, wsi, 0, 4)
     result,n = unpack_result(ret)
     assert(n == 4 and result == CopyResult.COMPLETED)
-    [ret] = canon_stream_write(StreamType(U8Type()), opts1, thread, wsi, 0, 4)
+    [ret] = canon_stream_write(StreamType(U8Type()), opts1, wsi, 0, 4)
     result,n = unpack_result(ret)
     assert(n == 4 and result == CopyResult.COMPLETED)
 
-    [ret] = canon_stream_write(StreamType(U8Type()), opts1, thread, wsi, 0, 0)
+    [ret] = canon_stream_write(StreamType(U8Type()), opts1, wsi, 0, 0)
     assert(ret == definitions.BLOCKED)
-    [ret] = canon_stream_cancel_write(StreamType(U8Type()), True, thread, wsi)
+    [ret] = canon_stream_cancel_write(StreamType(U8Type()), True, wsi)
     result,n = unpack_result(ret)
     assert(n == 0 and result == CopyResult.CANCELLED)
 
     thread.wait_until(fut2.is_set)
 
     mem1[0:8] = b'\x05\x06\x07\x08\x09\x0a\x0b\x0c'
-    [ret] = canon_stream_write(StreamType(U8Type()), opts1, thread, wsi, 0, 8)
+    [ret] = canon_stream_write(StreamType(U8Type()), opts1, wsi, 0, 8)
     assert(ret == definitions.BLOCKED)
 
     fut3.set()
 
     retp = 16
-    [seti] = canon_waitable_set_new(thread)
-    [] = canon_waitable_join(thread, wsi, seti)
-    [event] = canon_waitable_set_wait(True, MemInst(mem1, 'i32'), thread, seti, retp)
+    [seti] = canon_waitable_set_new()
+    [] = canon_waitable_join(wsi, seti)
+    [event] = canon_waitable_set_wait(True, MemInst(mem1, 'i32'), seti, retp)
     assert(event == EventCode.STREAM_WRITE)
     assert(mem1[retp+0] == wsi)
     result,n = unpack_result(mem1[retp+4])
     assert(n == 4 and result == CopyResult.COMPLETED)
 
-    [ret] = canon_stream_write(StreamType(U8Type()), opts1, thread, wsi, 12345, 0)
+    [ret] = canon_stream_write(StreamType(U8Type()), opts1, wsi, 12345, 0)
     assert(ret == definitions.BLOCKED)
 
     fut4.set()
 
-    [event] = canon_waitable_set_wait(True, MemInst(mem1, 'i32'), thread, seti, retp)
+    [event] = canon_waitable_set_wait(True, MemInst(mem1, 'i32'), seti, retp)
     assert(event == EventCode.STREAM_WRITE)
     assert(mem1[retp+0] == wsi)
     assert(mem1[retp+4] == 0)
 
-    [ret] = canon_stream_write(StreamType(U8Type()), opts1, thread, wsi, 12345, 0)
+    [ret] = canon_stream_write(StreamType(U8Type()), opts1, wsi, 12345, 0)
     assert(ret == 0)
 
-    [errctxi] = canon_error_context_new(opts1, thread, 0, 0)
-    [] = canon_stream_drop_writable(StreamType(U8Type()), thread, wsi)
-    [] = canon_waitable_set_drop(thread, seti)
-    [] = canon_error_context_drop(thread, errctxi)
+    [errctxi] = canon_error_context_new(opts1, 0, 0)
+    [] = canon_stream_drop_writable(StreamType(U8Type()), wsi)
+    [] = canon_waitable_set_drop(seti)
+    [] = canon_error_context_drop(errctxi)
     return []
 
   func1 = partial(canon_lift, opts1, inst1, ft1, core_func1)
@@ -1824,24 +1828,25 @@ def core_func1(thread, args):
   mem2 = heap2.memory
   opts2 = mk_opts(memory=MemInst(heap2.memory, 'i32'), realloc=heap2.realloc, async_=True)
   ft2 = FuncType([], [])
-  def core_func2(thread, args):
+  def core_func2(args):
+    thread = current_thread()
     assert(not args)
-    [] = canon_task_return(thread, [], opts2, [])
+    [] = canon_task_return([], opts2, [])
 
     retp = 16
-    [ret] = canon_lower(opts2, ft1, func1, thread, [retp])
+    [ret] = canon_lower(opts2, ft1, func1, [retp])
     assert(ret == Subtask.State.RETURNED)
     rsi = mem2[retp]
     assert(rsi == 1)
 
-    [ret] = canon_stream_read(StreamType(U8Type()), opts2, thread, rsi, 0, 8)
+    [ret] = canon_stream_read(StreamType(U8Type()), opts2, rsi, 0, 8)
     assert(ret == definitions.BLOCKED)
 
     fut1.set()
 
-    [seti] = canon_waitable_set_new(thread)
-    [] = canon_waitable_join(thread, rsi, seti)
-    [event] = canon_waitable_set_wait(True, MemInst(mem2, 'i32'), thread, seti, retp)
+    [seti] = canon_waitable_set_new()
+    [] = canon_waitable_join(rsi, seti)
+    [event] = canon_waitable_set_wait(True, MemInst(mem2, 'i32'), seti, retp)
     assert(event == EventCode.STREAM_READ)
     assert(mem2[retp+0] == rsi)
     result,n = unpack_result(mem2[retp+4])
@@ -1851,32 +1856,32 @@ def core_func2(thread, args):
     fut2.set()
     thread.wait_until(fut3.is_set)
 
-    [ret] = canon_stream_read(StreamType(U8Type()), opts2, thread, rsi, 12345, 0)
+    [ret] = canon_stream_read(StreamType(U8Type()), opts2, rsi, 12345, 0)
     assert(ret == 0)
 
     mem2[0:8] = bytes(8)
-    [ret] = canon_stream_read(StreamType(U8Type()), opts2, thread, rsi, 0, 2)
+    [ret] = canon_stream_read(StreamType(U8Type()), opts2, rsi, 0, 2)
     result,n = unpack_result(ret)
     assert(n == 2 and result == CopyResult.COMPLETED)
     assert(mem2[0:6] == b'\x05\x06\x00\x00\x00\x00')
-    [ret] = canon_stream_read(StreamType(U8Type()), opts2, thread, rsi, 2, 2)
+    [ret] = canon_stream_read(StreamType(U8Type()), opts2, rsi, 2, 2)
     result,n = unpack_result(ret)
     assert(n == 2 and result == CopyResult.COMPLETED)
     assert(mem2[0:6] == b'\x05\x06\x07\x08\x00\x00')
 
     thread.wait_until(fut4.is_set)
 
-    [ret] = canon_stream_read(StreamType(U8Type()), opts2, thread, rsi, 12345, 0)
+    [ret] = canon_stream_read(StreamType(U8Type()), opts2, rsi, 12345, 0)
     assert(ret == definitions.BLOCKED)
 
-    [event] = canon_waitable_set_wait(True, MemInst(mem2, 'i32'), thread, seti, retp)
+    [event] = canon_waitable_set_wait(True, MemInst(mem2, 'i32'), seti, retp)
     assert(event == EventCode.STREAM_READ)
     assert(mem2[retp+0] == rsi)
     p2 = int.from_bytes(mem2[retp+4 : retp+8], 'little', signed=False)
     assert(p2 == (CopyResult.DROPPED | 1))
 
-    [] = canon_stream_drop_readable(StreamType(U8Type()), thread, rsi)
-    [] = canon_waitable_set_drop(thread, seti)
+    [] = canon_stream_drop_readable(StreamType(U8Type()), rsi)
+    [] = canon_waitable_set_drop(seti)
     return []
 
   run_lift(opts2, inst2, ft2, core_func2, lambda:[], lambda _:())
@@ -1890,32 +1895,33 @@ def test_wasm_to_wasm_stream_empty():
   mem1 = bytearray(24)
   opts1 = mk_opts(memory=MemInst(mem1, 'i32'), async_=True)
   ft1 = FuncType([], [StreamType(None)])
-  def core_func1(thread, args):
+  def core_func1(args):
+    thread = current_thread()
     assert(not args)
-    [packed] = canon_stream_new(StreamType(None), thread)
+    [packed] = canon_stream_new(StreamType(None))
     rsi,wsi = unpack_new_ends(packed)
-    [] = canon_task_return(thread, [StreamType(None)], opts1, [rsi])
+    [] = canon_task_return([StreamType(None)], opts1, [rsi])
 
     thread.wait_until(fut1.is_set)
 
-    [ret] = canon_stream_write(StreamType(None), opts1, thread, wsi, 10000, 2)
+    [ret] = canon_stream_write(StreamType(None), opts1, wsi, 10000, 2)
     result,n = unpack_result(ret)
     assert(n == 2 and result == CopyResult.COMPLETED)
-    [ret] = canon_stream_write(StreamType(None), opts1, thread, wsi, 10000, 2)
+    [ret] = canon_stream_write(StreamType(None), opts1, wsi, 10000, 2)
     result,n = unpack_result(ret)
     assert(n == 2 and result == CopyResult.COMPLETED)
 
     thread.wait_until(fut2.is_set)
 
-    [ret] = canon_stream_write(StreamType(None), opts1, thread, wsi, 0, 8)
+    [ret] = canon_stream_write(StreamType(None), opts1, wsi, 0, 8)
     assert(ret == definitions.BLOCKED)
 
     fut3.set()
 
     retp = 16
-    [seti] = canon_waitable_set_new(thread)
-    [] = canon_waitable_join(thread, wsi, seti)
-    [event] = canon_waitable_set_wait(True, MemInst(mem1, 'i32'), thread, seti, retp)
+    [seti] = canon_waitable_set_new()
+    [] = canon_waitable_join(wsi, seti)
+    [event] = canon_waitable_set_wait(True, MemInst(mem1, 'i32'), seti, retp)
     assert(event == EventCode.STREAM_WRITE)
     assert(mem1[retp+0] == wsi)
     result,n = unpack_result(mem1[retp+4])
@@ -1923,9 +1929,9 @@ def core_func1(thread, args):
 
     fut4.set()
 
-    [errctxi] = canon_error_context_new(opts1, thread, 0, 0)
-    [] = canon_stream_drop_writable(StreamType(None), thread, wsi)
-    [] = canon_error_context_drop(thread, errctxi)
+    [errctxi] = canon_error_context_new(opts1, 0, 0)
+    [] = canon_stream_drop_writable(StreamType(None), wsi)
+    [] = canon_error_context_drop(errctxi)
     return []
 
   func1 = partial(canon_lift, opts1, inst1, ft1, core_func1)
@@ -1935,24 +1941,25 @@ def core_func1(thread, args):
   mem2 = heap2.memory
   opts2 = mk_opts(memory=MemInst(heap2.memory, 'i32'), realloc=heap2.realloc, async_=True)
   ft2 = FuncType([], [])
-  def core_func2(thread, args):
+  def core_func2(args):
+    thread = current_thread()
     assert(not args)
-    [] = canon_task_return(thread, [], opts2, [])
+    [] = canon_task_return([], opts2, [])
 
     retp = 0
-    [ret] = canon_lower(opts2, ft1, func1, thread, [retp])
+    [ret] = canon_lower(opts2, ft1, func1, [retp])
     assert(ret == Subtask.State.RETURNED)
     rsi = mem2[0]
     assert(rsi == 1)
 
-    [ret] = canon_stream_read(StreamType(None), opts2, thread, rsi, 0, 8)
+    [ret] = canon_stream_read(StreamType(None), opts2, rsi, 0, 8)
     assert(ret == definitions.BLOCKED)
 
     fut1.set()
 
-    [seti] = canon_waitable_set_new(thread)
-    [] = canon_waitable_join(thread, rsi, seti)
-    [event] = canon_waitable_set_wait(True, MemInst(mem2, 'i32'), thread, seti, retp)
+    [seti] = canon_waitable_set_new()
+    [] = canon_waitable_join(rsi, seti)
+    [event] = canon_waitable_set_wait(True, MemInst(mem2, 'i32'), seti, retp)
     assert(event == EventCode.STREAM_READ)
     assert(mem2[retp+0] == rsi)
     result,n = unpack_result(mem2[retp+4])
@@ -1961,19 +1968,19 @@ def core_func2(thread, args):
     fut2.set()
     thread.wait_until(fut3.is_set)
 
-    [ret] = canon_stream_read(StreamType(None), opts2, thread, rsi, 1000000, 2)
+    [ret] = canon_stream_read(StreamType(None), opts2, rsi, 1000000, 2)
     result,n = unpack_result(ret)
     assert(n == 2 and result == CopyResult.COMPLETED)
-    [ret] = canon_stream_read(StreamType(None), opts2, thread, rsi, 1000000, 2)
+    [ret] = canon_stream_read(StreamType(None), opts2, rsi, 1000000, 2)
     result,n = unpack_result(ret)
     assert(n == 2 and result == CopyResult.COMPLETED)
 
     thread.wait_until(fut4.is_set)
 
-    [ret] = canon_stream_read(StreamType(None), opts2, thread, rsi, 1000000, 2)
+    [ret] = canon_stream_read(StreamType(None), opts2, rsi, 1000000, 2)
     result,n = unpack_result(ret)
     assert(n == 0 and result == CopyResult.DROPPED)
-    [] = canon_stream_drop_readable(StreamType(None), thread, rsi)
+    [] = canon_stream_drop_readable(StreamType(None), rsi)
     return []
 
   run_lift(opts2, inst2, ft2, core_func2, lambda:[], lambda _:())
@@ -2004,79 +2011,79 @@ def host_func2(caller, on_start, on_resolve):
     return mk_done_task(caller)
 
   lift_opts = mk_opts()
-  def core_func(thread, args):
+  def core_func(args):
     assert(not args)
 
-    [packed] = canon_stream_new(StreamType(U8Type()), thread)
+    [packed] = canon_stream_new(StreamType(U8Type()))
     rsi,wsi = unpack_new_ends(packed)
-    [ret] = canon_lower(lower_opts, host_ft1, host_func1, thread, [rsi])
+    [ret] = canon_lower(lower_opts, host_ft1, host_func1, [rsi])
     assert(ret == Subtask.State.RETURNED)
     mem[0:4] = b'\x0a\x0b\x0c\x0d'
-    [ret] = canon_stream_write(StreamType(U8Type()), lower_opts, thread, wsi, 0, 4)
+    [ret] = canon_stream_write(StreamType(U8Type()), lower_opts, wsi, 0, 4)
     assert(ret == definitions.BLOCKED)
     host_sink.set_remain(2)
     got = host_sink.consume(2)
     assert(got == [0xa, 0xb])
-    [ret] = canon_stream_cancel_write(StreamType(U8Type()), False, thread, wsi)
+    [ret] = canon_stream_cancel_write(StreamType(U8Type()), False, wsi)
     result,n = unpack_result(ret)
     assert(n == 2 and result == CopyResult.COMPLETED)
-    [] = canon_stream_drop_writable(StreamType(U8Type()), thread, wsi)
+    [] = canon_stream_drop_writable(StreamType(U8Type()), wsi)
     host_sink.set_remain(100)
     assert(host_sink.consume(100) is None)
 
-    [packed] = canon_stream_new(StreamType(U8Type()), thread)
+    [packed] = canon_stream_new(StreamType(U8Type()))
     rsi,wsi = unpack_new_ends(packed)
-    [ret] = canon_lower(lower_opts, host_ft1, host_func1, thread, [rsi])
+    [ret] = canon_lower(lower_opts, host_ft1, host_func1, [rsi])
     assert(ret == Subtask.State.RETURNED)
     mem[0:4] = b'\x01\x02\x03\x04'
-    [ret] = canon_stream_write(StreamType(U8Type()), lower_opts, thread, wsi, 0, 4)
+    [ret] = canon_stream_write(StreamType(U8Type()), lower_opts, wsi, 0, 4)
     assert(ret == definitions.BLOCKED)
     host_sink.set_remain(2)
     got = host_sink.consume(2)
     assert(got == [1, 2])
-    [ret] = canon_stream_cancel_write(StreamType(U8Type()), True, thread, wsi)
+    [ret] = canon_stream_cancel_write(StreamType(U8Type()), True, wsi)
     result,n = unpack_result(ret)
     assert(n == 2 and result == CopyResult.COMPLETED)
-    [] = canon_stream_drop_writable(StreamType(U8Type()), thread, wsi)
+    [] = canon_stream_drop_writable(StreamType(U8Type()), wsi)
     host_sink.set_remain(100)
     assert(host_sink.consume(100) is None)
 
     retp = 16
-    [ret] = canon_lower(lower_opts, host_ft2, host_func2, thread, [retp])
+    [ret] = canon_lower(lower_opts, host_ft2, host_func2, [retp])
     assert(ret == Subtask.State.RETURNED)
     rsi = mem[retp]
-    [ret] = canon_stream_read(StreamType(U8Type()), lower_opts, thread, rsi, 0, 4)
+    [ret] = canon_stream_read(StreamType(U8Type()), lower_opts, rsi, 0, 4)
     assert(ret == definitions.BLOCKED)
-    [ret] = canon_stream_cancel_read(StreamType(U8Type()), False, thread, rsi)
+    [ret] = canon_stream_cancel_read(StreamType(U8Type()), False, rsi)
     result,n = unpack_result(ret)
     assert(n == 0 and result == CopyResult.CANCELLED)
-    [] = canon_stream_drop_readable(StreamType(U8Type()), thread, rsi)
+    [] = canon_stream_drop_readable(StreamType(U8Type()), rsi)
 
-    [ret] = canon_lower(lower_opts, host_ft2, host_func2, thread, [retp])
+    [ret] = canon_lower(lower_opts, host_ft2, host_func2, [retp])
     assert(ret == Subtask.State.RETURNED)
     rsi = mem[retp]
-    [ret] = canon_stream_read(StreamType(U8Type()), lower_opts, thread, rsi, 0, 4)
+    [ret] = canon_stream_read(StreamType(U8Type()), lower_opts, rsi, 0, 4)
     assert(ret == definitions.BLOCKED)
     host_source.block_cancel()
-    [ret] = canon_stream_cancel_read(StreamType(U8Type()), True, thread, rsi)
+    [ret] = canon_stream_cancel_read(StreamType(U8Type()), True, rsi)
     assert(ret == definitions.BLOCKED)
     try:
-      canon_stream_cancel_read(StreamType(U8Type()), True, thread, rsi)
+      canon_stream_cancel_read(StreamType(U8Type()), True, rsi)
       assert(False)
     except Trap:
       pass
     host_source.write([7,8])
     host_source.unblock_cancel()
-    [seti] = canon_waitable_set_new(thread)
-    [] = canon_waitable_join(thread, rsi, seti)
-    [event] = canon_waitable_set_wait(True, MemInst(mem, 'i32'), thread, seti, retp)
+    [seti] = canon_waitable_set_new()
+    [] = canon_waitable_join(rsi, seti)
+    [event] = canon_waitable_set_wait(True, MemInst(mem, 'i32'), seti, retp)
     assert(event == EventCode.STREAM_READ)
     assert(mem[retp+0] == rsi)
     result,n = unpack_result(mem[retp+4])
     assert(n == 2 and result == CopyResult.CANCELLED)
     assert(mem[0:2] == b'\x07\x08')
-    [] = canon_stream_drop_readable(StreamType(U8Type()), thread, rsi)
-    [] = canon_waitable_set_drop(thread, seti)
+    [] = canon_stream_drop_readable(StreamType(U8Type()), rsi)
+    [] = canon_waitable_set_drop(seti)
 
     return []
 
@@ -2142,7 +2149,8 @@ def test_futures():
 
   host_ft1 = FuncType([FutureType(U8Type())],[FutureType(U8Type())])
   def host_func(caller, on_start, on_resolve):
-    def thread_func(thread):
+    def thread_func():
+      thread = current_thread()
       [future] = on_start()
       outgoing = HostFutureSource(U8Type())
       thread.task.return_([outgoing])
@@ -2154,66 +2162,67 @@ def thread_func(thread):
     return mk_task(caller, on_resolve, thread_func)
 
   lift_opts = mk_opts()
-  def core_func(thread, args):
+  def core_func(args):
+    thread = current_thread()
     assert(not args)
-    [packed] = canon_future_new(FutureType(U8Type()), thread)
+    [packed] = canon_future_new(FutureType(U8Type()))
     rfi,wfi = unpack_new_ends(packed)
     retp = 16
-    [ret] = canon_lower(lower_opts, host_ft1, host_func, thread, [rfi, retp])
+    [ret] = canon_lower(lower_opts, host_ft1, host_func, [rfi, retp])
     assert(ret == Subtask.State.RETURNED)
     rfi = mem[retp]
 
     readp = 0
-    [ret] = canon_future_read(FutureType(U8Type()), lower_opts, thread, rfi, readp)
+    [ret] = canon_future_read(FutureType(U8Type()), lower_opts, rfi, readp)
     assert(ret == definitions.BLOCKED)
 
     writep = 8
     mem[writep] = 42
-    [ret] = canon_future_write(FutureType(U8Type()), lower_opts, thread, wfi, writep)
+    [ret] = canon_future_write(FutureType(U8Type()), lower_opts, wfi, writep)
     assert(ret == CopyResult.COMPLETED)
 
-    [seti] = canon_waitable_set_new(thread)
-    [] = canon_waitable_join(thread, rfi, seti)
-    [event] = canon_waitable_set_wait(True, MemInst(mem, 'i32'), thread, seti, retp)
+    [seti] = canon_waitable_set_new()
+    [] = canon_waitable_join(rfi, seti)
+    [event] = canon_waitable_set_wait(True, MemInst(mem, 'i32'), seti, retp)
     assert(event == EventCode.FUTURE_READ)
     assert(mem[retp+0] == rfi)
     assert(mem[retp+4] == CopyResult.COMPLETED)
     assert(mem[readp] == 43)
 
-    [] = canon_future_drop_writable(FutureType(U8Type()), thread, wfi)
-    [] = canon_future_drop_readable(FutureType(U8Type()), thread, rfi)
-    [] = canon_waitable_set_drop(thread, seti)
+    [] = canon_future_drop_writable(FutureType(U8Type()), wfi)
+    [] = canon_future_drop_readable(FutureType(U8Type()), rfi)
+    [] = canon_waitable_set_drop(seti)
 
-    [packed] = canon_future_new(FutureType(U8Type()), thread)
+    [packed] = canon_future_new(FutureType(U8Type()))
     rfi,wfi = unpack_new_ends(packed)
-    [ret] = canon_lower(lower_opts, host_ft1, host_func, thread, [rfi, retp])
+    [ret] = canon_lower(lower_opts, host_ft1, host_func, [rfi, retp])
     assert(ret == Subtask.State.RETURNED)
     rfi = mem[retp]
 
     readp = 0
-    [ret] = canon_future_read(FutureType(U8Type()), lower_opts, thread, rfi, readp)
+    [ret] = canon_future_read(FutureType(U8Type()), lower_opts, rfi, readp)
     assert(ret == definitions.BLOCKED)
 
     writep = 8
     mem[writep] = 42
-    [ret] = canon_future_write(FutureType(U8Type()), lower_opts, thread, wfi, writep)
+    [ret] = canon_future_write(FutureType(U8Type()), lower_opts, wfi, writep)
     assert(ret == CopyResult.COMPLETED)
 
     while not thread.task.inst.handles.get(rfi).has_pending_event():
-      canon_thread_yield(True, thread)
+      canon_thread_yield(True)
 
-    [ret] = canon_future_cancel_read(FutureType(U8Type()), False, thread, rfi)
+    [ret] = canon_future_cancel_read(FutureType(U8Type()), False, rfi)
     assert(ret == CopyResult.COMPLETED)
     assert(mem[readp] == 43)
 
-    [] = canon_future_drop_writable(FutureType(U8Type()), thread, wfi)
-    [] = canon_future_drop_readable(FutureType(U8Type()), thread, rfi)
+    [] = canon_future_drop_writable(FutureType(U8Type()), wfi)
+    [] = canon_future_drop_readable(FutureType(U8Type()), rfi)
 
-    [packed] = canon_future_new(FutureType(U8Type()), thread)
+    [packed] = canon_future_new(FutureType(U8Type()))
     rfi,wfi = unpack_new_ends(packed)
     trapped = False
     try:
-      canon_future_drop_writable(FutureType(U8Type()), thread, wfi)
+      canon_future_drop_writable(FutureType(U8Type()), wfi)
     except Trap:
       trapped = True
     assert(trapped)
@@ -2233,71 +2242,73 @@ def test_cancel_subtask():
   sync_callee_opts = mk_opts(MemInst(callee_heap.memory, 'i32'), async_ = False)
   callee_inst = ComponentInstance(store)
 
-  def core_callee1(thread, args):
+  def core_callee1(args):
     assert(False)
   callee1 = partial(canon_lift, callee_opts, callee_inst, ft, core_callee1)
 
-  def core_callee2(thread, args):
+  def core_callee2(args):
     [x] = args
-    [si] = canon_waitable_set_new(thread)
-    [ret] = canon_waitable_set_wait(True, MemInst(callee_heap.memory, 'i32'), thread, si, 0)
+    [si] = canon_waitable_set_new()
+    [ret] = canon_waitable_set_wait(True, MemInst(callee_heap.memory, 'i32'), si, 0)
     assert(ret == EventCode.TASK_CANCELLED)
     match x:
       case 1:
-        [] = canon_task_return(thread, [U8Type()], callee_opts, [42])
+        [] = canon_task_return([U8Type()], callee_opts, [42])
       case 2:
-        [] = canon_task_cancel(thread)
+        [] = canon_task_cancel()
       case 3:
-        [_] = canon_thread_yield(True, thread)
-        [] = canon_task_return(thread, [U8Type()], callee_opts, [43])
+        [_] = canon_thread_yield(True)
+        [] = canon_task_return([U8Type()], callee_opts, [43])
       case 4:
-        [_] = canon_thread_yield(True, thread)
-        [] = canon_task_cancel(thread)
+        [_] = canon_thread_yield(True)
+        [] = canon_task_cancel()
       case _:
         assert(False)
     return []
   callee2 = partial(canon_lift, callee_opts, callee_inst, ft, core_callee2)
 
-  def core_callee3(thread, args):
+  def core_callee3(args):
     [x] = args
-    [cancelled] = canon_thread_yield(True, thread)
+    [cancelled] = canon_thread_yield(True)
     if cancelled:
-      [] = canon_task_cancel(thread)
+      [] = canon_task_cancel()
     else:
-      [] = canon_task_return(thread, [U8Type()], callee_opts, [83])
+      [] = canon_task_return([U8Type()], callee_opts, [83])
     return []
   callee3 = partial(canon_lift, callee_opts, callee_inst, ft, core_callee3)
 
   host_fut4 = RacyBool(False)
   def host_import4(caller, on_start, on_resolve):
-    def thread_func(thread):
+    def thread_func():
+      thread = current_thread()
       args = on_start()
       assert(len(args) == 1)
       assert(args[0] == 42)
       thread.wait_until(host_fut4.is_set)
       thread.task.return_([43])
     return mk_task(caller, on_resolve, thread_func)
-  def core_callee4(thread, args):
+  def core_callee4(args):
     [x] = args
-    [result] = canon_lower(sync_callee_opts, ft, host_import4, thread, [42])
+    [result] = canon_lower(sync_callee_opts, ft, host_import4, [42])
     assert(result == 43)
     try:
-      [] = canon_task_cancel(thread)
+      [] = canon_task_cancel()
       assert(False)
     except Trap:
       pass
-    [seti] = canon_waitable_set_new(thread)
-    [result] = canon_waitable_set_wait(True, MemInst(callee_heap.memory, 'i32'), thread, seti, 0)
+    [seti] = canon_waitable_set_new()
+    [result] = canon_waitable_set_wait(True, MemInst(callee_heap.memory, 'i32'), seti, 0)
     assert(result == EventCode.TASK_CANCELLED)
-    [result] = canon_waitable_set_poll(True, MemInst(callee_heap.memory, 'i32'), thread, seti, 0)
+    [result] = canon_waitable_set_poll(True, MemInst(callee_heap.memory, 'i32'), seti, 0)
     assert(result == EventCode.NONE)
-    [] = canon_task_cancel(thread)
+    [] = canon_task_cancel()
     return []
   callee4 = partial(canon_lift, callee_opts, callee_inst, ft, core_callee4)
 
   host_fut5 = RacyBool(False)
   def host_import5(caller, on_start, on_resolve):
-    def thread_func(thread):
+    def thread_func():
+      thread = current_thread()
       args = on_start()
       assert(len(args) == 1)
       assert(args[0] == 42)
@@ -2306,30 +2317,30 @@ def thread_func(thread):
       thread.wait_until(host_fut5.is_set)
       thread.task.return_([43])
     return mk_task(caller, on_resolve, thread_func)
-  def core_callee5(thread, args):
+  def core_callee5(args):
     [x] = args
     assert(x == 13)
-    [ret] = canon_lower(callee_opts, ft, host_import5, thread, [42, 0])
+    [ret] = canon_lower(callee_opts, ft, host_import5, [42, 0])
     state,subi = unpack_result(ret)
     assert(state == Subtask.State.STARTED)
-    [ret] = canon_subtask_cancel(False, thread, subi)
+    [ret] = canon_subtask_cancel(False, subi)
     assert(ret == Subtask.State.RETURNED)
-    [] = canon_task_return(thread, [U8Type()], callee_opts, [44])
+    [] = canon_task_return([U8Type()], callee_opts, [44])
     return []
   callee5 = partial(canon_lift, callee_opts, callee_inst, ft, core_callee5)
 
   core_ftbl = Table()
   core_ft = CoreFuncType(['i32'], [])
-  def thread_func(cancellable, thread, args):
+  def thread_func(cancellable, args):
     [mainthreadi] = args
     if cancellable:
-      [ret] = canon_thread_switch_to(True, thread, mainthreadi)
+      [ret] = canon_thread_switch_to(True, mainthreadi)
       assert(ret == Cancelled.TRUE)
-      [ret] = canon_thread_switch_to(True, thread, mainthreadi)
+      [ret] = canon_thread_switch_to(True, mainthreadi)
       assert(ret == Cancelled.FALSE)
-      [] = canon_task_return(thread, [U8Type()], callee_opts, [45])
+      [] = canon_task_return([U8Type()], callee_opts, [45])
     else:
-      [ret] = canon_thread_switch_to(False, thread, mainthreadi)
+      [ret] = canon_thread_switch_to(False, mainthreadi)
       assert(ret == Cancelled.FALSE)
     return []
   cthread_func = partial(thread_func, True)
@@ -2337,30 +2348,30 @@ def thread_func(cancellable, thread, args):
   cfi = core_ftbl.add(CoreFuncRef(core_ft, cthread_func))
   ncfi = core_ftbl.add(CoreFuncRef(core_ft, ncthread_func))
 
-  def core_callee6(thread, args):
+  def core_callee6(args):
     [x] = args
     assert(x == 14)
 
-    [mainthreadi] = canon_thread_index(thread)
+    [mainthreadi] = canon_thread_index()
 
-    [threadi1] = canon_thread_new_indirect(core_ft, core_ftbl, thread, ncfi, mainthreadi)
-    [ret] = canon_thread_switch_to(True, thread, threadi1)
+    [threadi1] = canon_thread_new_indirect(core_ft, core_ftbl, ncfi, mainthreadi)
+    [ret] = canon_thread_switch_to(True, threadi1)
     assert(ret == Cancelled.FALSE)
 
-    [threadi2] = canon_thread_new_indirect(core_ft, core_ftbl, thread, cfi, mainthreadi)
-    [ret] = canon_thread_switch_to(True, thread, threadi2)
+    [threadi2] = canon_thread_new_indirect(core_ft, core_ftbl, cfi, mainthreadi)
+    [ret] = canon_thread_switch_to(True, threadi2)
     assert(ret == Cancelled.FALSE)
 
-    [threadi3] = canon_thread_new_indirect(core_ft, core_ftbl, thread, ncfi, mainthreadi)
-    [ret] = canon_thread_switch_to(True, thread, threadi3)
+    [threadi3] = canon_thread_new_indirect(core_ft, core_ftbl, ncfi, mainthreadi)
+    [ret] = canon_thread_switch_to(True, threadi3)
     assert(ret == Cancelled.FALSE)
 
-    [ret] = canon_thread_suspend(False, thread)
+    [ret] = canon_thread_suspend(False)
     assert(ret == Cancelled.FALSE)
 
-    [] = canon_thread_resume_later(thread, threadi1)
-    [] = canon_thread_resume_later(thread, threadi2)
-    [] = canon_thread_resume_later(thread, threadi3)
+    [] = canon_thread_resume_later(threadi1)
+    [] = canon_thread_resume_later(threadi2)
+    [] = canon_thread_resume_later(threadi3)
     return []
   callee6 = partial(canon_lift, callee_opts, callee_inst, ft, core_callee6)
 
@@ -2368,163 +2379,163 @@ def core_callee6(thread, args):
   caller_opts = mk_opts(MemInst(caller_heap.memory, 'i32'), async_ = True)
   caller_inst = ComponentInstance(store)
 
-  def core_caller(thread, args):
+  def core_caller(args):
     [x] = args
     assert(x == 1)
 
-    [seti] = canon_waitable_set_new(thread)
+    [seti] = canon_waitable_set_new()
 
     callee_inst.backpressure = True
-    [ret] = canon_lower(caller_opts, ft, callee1, thread, [13, 0])
+    [ret] = canon_lower(caller_opts, ft, callee1, [13, 0])
     state,subi1 = unpack_result(ret)
     assert(state == Subtask.State.STARTING)
-    [ret] = canon_lower(caller_opts, ft, callee1, thread, [13, 0])
+    [ret] = canon_lower(caller_opts, ft, callee1, [13, 0])
     state,subi2 = unpack_result(ret)
     assert(state == Subtask.State.STARTING)
-    [ret] = canon_subtask_cancel(False, thread, subi2)
+    [ret] = canon_subtask_cancel(False, subi2)
     assert(ret == Subtask.State.CANCELLED_BEFORE_STARTED)
-    [ret] = canon_subtask_cancel(True, thread, subi1)
+    [ret] = canon_subtask_cancel(True, subi1)
     assert(ret == Subtask.State.CANCELLED_BEFORE_STARTED)
     callee_inst.backpressure = False
 
-    [ret] = canon_lower(caller_opts, ft, callee2, thread, [1, 0])
+    [ret] = canon_lower(caller_opts, ft, callee2, [1, 0])
     state,subi1 = unpack_result(ret)
     assert(state == Subtask.State.STARTED)
-    [ret] = canon_lower(caller_opts, ft, callee2, thread, [2, 0])
+    [ret] = canon_lower(caller_opts, ft, callee2, [2, 0])
     state,subi2 = unpack_result(ret)
     assert(state == Subtask.State.STARTED)
-    [ret] = canon_lower(caller_opts, ft, callee2, thread, [3, 0])
+    [ret] = canon_lower(caller_opts, ft, callee2, [3, 0])
     state,subi3 = unpack_result(ret)
     assert(state == Subtask.State.STARTED)
-    [ret] = canon_lower(caller_opts, ft, callee2, thread, [3, 0])
+    [ret] = canon_lower(caller_opts, ft, callee2, [3, 0])
     state,subi3_2 = unpack_result(ret)
     assert(state == Subtask.State.STARTED)
-    [ret] = canon_lower(caller_opts, ft, callee2, thread, [4, 0])
+    [ret] = canon_lower(caller_opts, ft, callee2, [4, 0])
     state,subi4 = unpack_result(ret)
     assert(state == Subtask.State.STARTED)
-    [ret] = canon_lower(caller_opts, ft, callee2, thread, [4, 0])
+    [ret] = canon_lower(caller_opts, ft, callee2, [4, 0])
     state,subi4_2 = unpack_result(ret)
     assert(state == Subtask.State.STARTED)
 
     caller_heap.memory[0] = 13
-    [ret] = canon_subtask_cancel(False, thread, subi1)
+    [ret] = canon_subtask_cancel(False, subi1)
     assert(ret == Subtask.State.RETURNED)
     assert(caller_heap.memory[0] == 42)
-    [] = canon_subtask_drop(thread, subi1)
+    [] = canon_subtask_drop(subi1)
 
     caller_heap.memory[0] = 13
-    [ret] = canon_subtask_cancel(True, thread, subi2)
+    [ret] = canon_subtask_cancel(True, subi2)
     assert(ret == Subtask.State.CANCELLED_BEFORE_RETURNED)
     assert(caller_heap.memory[0] == 13)
-    [] = canon_subtask_drop(thread, subi2)
+    [] = canon_subtask_drop(subi2)
 
     caller_heap.memory[0] = 13
-    [ret] = canon_subtask_cancel(True, thread, subi3)
+    [ret] = canon_subtask_cancel(True, subi3)
     assert(ret == definitions.BLOCKED)
     assert(caller_heap.memory[0] == 13)
-    [] = canon_waitable_join(thread, subi3, seti)
+    [] = canon_waitable_join(subi3, seti)
     retp = 8
-    [ret] = canon_waitable_set_wait(True, MemInst(caller_heap.memory, 'i32'), thread, seti, retp)
+    [ret] = canon_waitable_set_wait(True, MemInst(caller_heap.memory, 'i32'), seti, retp)
     assert(ret == EventCode.SUBTASK)
     assert(caller_heap.memory[retp+0] == subi3)
     assert(caller_heap.memory[retp+4] == Subtask.State.RETURNED)
     assert(caller_heap.memory[0] == 43)
-    [] = canon_subtask_drop(thread, subi3)
+    [] = canon_subtask_drop(subi3)
 
     caller_heap.memory[0] = 13
-    [ret] = canon_subtask_cancel(False, thread, subi3_2)
+    [ret] = canon_subtask_cancel(False, subi3_2)
     assert(ret == Subtask.State.RETURNED)
     assert(caller_heap.memory[0] == 43)
-    [] = canon_subtask_drop(thread, subi3_2)
+    [] = canon_subtask_drop(subi3_2)
 
     caller_heap.memory[0] = 13
-    [ret] = canon_subtask_cancel(True, thread, subi4)
+    [ret] = canon_subtask_cancel(True, subi4)
     assert(ret == definitions.BLOCKED)
     assert(caller_heap.memory[0] == 13)
-    [] = canon_waitable_join(thread, subi4, seti)
+    [] = canon_waitable_join(subi4, seti)
     retp = 8
-    [ret] = canon_waitable_set_wait(True, MemInst(caller_heap.memory, 'i32'), thread, seti, retp)
+    [ret] = canon_waitable_set_wait(True, MemInst(caller_heap.memory, 'i32'), seti, retp)
     assert(ret == EventCode.SUBTASK)
     assert(caller_heap.memory[retp+0] == subi4)
     assert(caller_heap.memory[retp+4] == Subtask.State.CANCELLED_BEFORE_RETURNED)
-    [] = canon_subtask_drop(thread, subi4)
+    [] = canon_subtask_drop(subi4)
 
     caller_heap.memory[0] = 13
-    [ret] = canon_subtask_cancel(False, thread, subi4_2)
+    [ret] = canon_subtask_cancel(False, subi4_2)
     assert(ret == Subtask.State.CANCELLED_BEFORE_RETURNED)
     assert(caller_heap.memory[0] == 13)
-    [] = canon_subtask_drop(thread, subi4_2)
+    [] = canon_subtask_drop(subi4_2)
 
     caller_heap.memory[0] = 13
-    [ret] = canon_lower(caller_opts, ft, callee3, thread, [0, 0])
+    [ret] = canon_lower(caller_opts, ft, callee3, [0, 0])
     state,subi = unpack_result(ret)
     assert(state == Subtask.State.STARTED)
     while caller_inst.handles.get(subi).state == Subtask.State.STARTED:
-      [_] = canon_thread_yield(True, thread)
-    [ret] = canon_subtask_cancel(True, thread, subi)
+      [_] = canon_thread_yield(True)
+    [ret] = canon_subtask_cancel(True, subi)
     assert(ret == Subtask.State.RETURNED)
     assert(caller_heap.memory[0] == 83)
-    [] = canon_subtask_drop(thread, subi)
+    [] = canon_subtask_drop(subi)
 
     caller_heap.memory[0] = 13
-    [ret] = canon_lower(caller_opts, ft, callee3, thread, [0, 0])
+    [ret] = canon_lower(caller_opts, ft, callee3, [0, 0])
     state,subi = unpack_result(ret)
     assert(state == Subtask.State.STARTED)
-    [ret] = canon_subtask_cancel(True, thread, subi)
+    [ret] = canon_subtask_cancel(True, subi)
     assert(ret == Subtask.State.CANCELLED_BEFORE_RETURNED)
     assert(caller_heap.memory[0] == 13)
-    [] = canon_subtask_drop(thread, subi)
+    [] = canon_subtask_drop(subi)
 
     caller_heap.memory[0] = 13
-    [ret] = canon_lower(caller_opts, ft, callee4, thread, [0, 0])
+    [ret] = canon_lower(caller_opts, ft, callee4, [0, 0])
     state,subi = unpack_result(ret)
     assert(state == Subtask.State.STARTED)
-    [ret] = canon_subtask_cancel(True, thread, subi)
+    [ret] = canon_subtask_cancel(True, subi)
     assert(ret == definitions.BLOCKED)
     assert(caller_heap.memory[0] == 13)
     host_fut4.set()
-    [] = canon_waitable_join(thread, subi, seti)
+    [] = canon_waitable_join(subi, seti)
     waitretp = 4
-    [event] = canon_waitable_set_wait(True, MemInst(caller_heap.memory, 'i32'), thread, seti, waitretp)
+    [event] = canon_waitable_set_wait(True, MemInst(caller_heap.memory, 'i32'), seti, waitretp)
     assert(event == EventCode.SUBTASK)
     assert(caller_heap.memory[waitretp] == subi)
     assert(caller_heap.memory[waitretp+4] == Subtask.State.CANCELLED_BEFORE_RETURNED)
     assert(caller_heap.memory[0] == 13)
-    [] = canon_subtask_drop(thread, subi)
+    [] = canon_subtask_drop(subi)
 
-    [ret] = canon_lower(caller_opts, ft, callee5, thread, [13, 0])
+    [ret] = canon_lower(caller_opts, ft, callee5, [13, 0])
     state,subi = unpack_result(ret)
     assert(state == Subtask.State.STARTED)
-    [ret] = canon_subtask_cancel(True, thread, subi)
+    [ret] = canon_subtask_cancel(True, subi)
     assert(ret == definitions.BLOCKED)
     assert(caller_heap.memory[0] == 13)
     host_fut5.set()
-    [] = canon_waitable_join(thread, subi, seti)
+    [] = canon_waitable_join(subi, seti)
     waitretp = 4
-    [event] = canon_waitable_set_wait(True, MemInst(caller_heap.memory, 'i32'), thread, seti, waitretp)
+    [event] = canon_waitable_set_wait(True, MemInst(caller_heap.memory, 'i32'), seti, waitretp)
     assert(event == EventCode.SUBTASK)
     assert(caller_heap.memory[waitretp] == subi)
     assert(caller_heap.memory[waitretp+4] == Subtask.State.RETURNED)
     assert(caller_heap.memory[0] == 44)
-    [] = canon_subtask_drop(thread, subi)
+    [] = canon_subtask_drop(subi)
 
-    [ret] = canon_lower(caller_opts, ft, callee6, thread, [14, 0])
+    [ret] = canon_lower(caller_opts, ft, callee6, [14, 0])
     state,subi = unpack_result(ret)
     assert(state == Subtask.State.STARTED)
 
-    [ret] = canon_subtask_cancel(True, thread, subi)
+    [ret] = canon_subtask_cancel(True, subi)
     assert(ret == definitions.BLOCKED)
 
-    [] = canon_waitable_join(thread, subi, seti)
-    [event] = canon_waitable_set_wait(True, MemInst(caller_heap.memory, 'i32'), thread, seti, 4)
+    [] = canon_waitable_join(subi, seti)
+    [event] = canon_waitable_set_wait(True, MemInst(caller_heap.memory, 'i32'), seti, 4)
     assert(event == EventCode.SUBTASK)
     assert(caller_heap.memory[0] == 45)
     assert(caller_heap.memory[4] == subi)
     assert(caller_heap.memory[8] == Subtask.State.RETURNED)
-    [] = canon_subtask_drop(thread, subi)
+    [] = canon_subtask_drop(subi)
 
-    [] = canon_waitable_set_drop(thread, seti)
-    [] = canon_task_return(thread, [U8Type()], caller_opts, [42])
+    [] = canon_waitable_set_drop(seti)
+    [] = canon_task_return([U8Type()], caller_opts, [42])
     return []
 
   def on_start():
@@ -2549,49 +2560,49 @@ def test_self_copy(elemt):
   async_opts = mk_opts(memory=MemInst(mem, 'i32'), async_=True)
 
   ft = FuncType([], [], async_ = True)
-  def core_func(thread, args):
-    [seti] = canon_waitable_set_new(thread)
+  def core_func(args):
+    [seti] = canon_waitable_set_new()
 
-    [packed] = canon_future_new(FutureType(elemt), thread)
+    [packed] = canon_future_new(FutureType(elemt))
     rfi,wfi = unpack_new_ends(packed)
 
-    [ret] = canon_future_write(FutureType(elemt), async_opts, thread, wfi, 0)
+    [ret] = canon_future_write(FutureType(elemt), async_opts, wfi, 0)
     assert(ret == definitions.BLOCKED)
 
-    [ret] = canon_future_read(FutureType(elemt), async_opts, thread, rfi, 0)
+    [ret] = canon_future_read(FutureType(elemt), async_opts, rfi, 0)
     assert(ret == CopyResult.COMPLETED)
-    [] = canon_future_drop_readable(FutureType(elemt), thread, rfi)
+    [] = canon_future_drop_readable(FutureType(elemt), rfi)
 
-    [] = canon_waitable_join(thread, wfi, seti)
-    [event] = canon_waitable_set_wait(True, MemInst(mem, 'i32'), thread, seti, 0)
+    [] = canon_waitable_join(wfi, seti)
+    [event] = canon_waitable_set_wait(True, MemInst(mem, 'i32'), seti, 0)
     assert(event == EventCode.FUTURE_WRITE)
     assert(mem[0] == wfi)
     assert(mem[4] == CopyResult.COMPLETED)
-    [] = canon_future_drop_writable(FutureType(elemt), thread, wfi)
+    [] = canon_future_drop_writable(FutureType(elemt), wfi)
 
-    [packed] = canon_stream_new(StreamType(elemt), thread)
+    [packed] = canon_stream_new(StreamType(elemt))
     rsi,wsi = unpack_new_ends(packed)
-    [ret] = canon_stream_write(StreamType(elemt), async_opts, thread, wsi, 0, 3)
+    [ret] = canon_stream_write(StreamType(elemt), async_opts, wsi, 0, 3)
     assert(ret == definitions.BLOCKED)
 
-    [ret] = canon_stream_read(StreamType(elemt), async_opts, thread, rsi, 0, 1)
+    [ret] = canon_stream_read(StreamType(elemt), async_opts, rsi, 0, 1)
     result,n = unpack_result(ret)
     assert(n == 1 and result == CopyResult.COMPLETED)
-    [ret] = canon_stream_read(StreamType(elemt), async_opts, thread, rsi, 0, 4)
+    [ret] = canon_stream_read(StreamType(elemt), async_opts, rsi, 0, 4)
     result,n = unpack_result(ret)
     assert(n == 2 and result == CopyResult.COMPLETED)
-    [] = canon_stream_drop_readable(StreamType(elemt), thread, rsi)
+    [] = canon_stream_drop_readable(StreamType(elemt), rsi)
 
-    [] = canon_waitable_join(thread, wsi, seti)
-    [event] = canon_waitable_set_wait(True, MemInst(mem, 'i32'), thread, seti, 0)
+    [] = canon_waitable_join(wsi, seti)
+    [event] = canon_waitable_set_wait(True, MemInst(mem, 'i32'), seti, 0)
     assert(event == EventCode.STREAM_WRITE)
     assert(mem[0] == wsi)
     result,n = unpack_result(mem[4])
     assert(result == CopyResult.DROPPED)
     assert(n == 3)
-    [] = canon_stream_drop_writable(StreamType(elemt), thread, wsi)
+    [] = canon_stream_drop_writable(StreamType(elemt), wsi)
 
-    [] = canon_waitable_set_drop(thread, seti)
+    [] = canon_waitable_set_drop(seti)
     return []
 
   run_lift(sync_opts, inst, ft, core_func, lambda:[], lambda _:())
@@ -2629,18 +2640,18 @@ def f3(caller, on_start, on_resolve):
     on_resolve([])
     return mk_done_task(caller)
 
-  def core_func(thread, args):
-    [ret] = canon_lower(opts, ft1, f1, thread, [1.1, 2.2, 3, 4])
+  def core_func(args):
+    [ret] = canon_lower(opts, ft1, f1, [1.1, 2.2, 3, 4])
     assert(ret == Subtask.State.RETURNED)
 
-    [ret] = canon_lower(opts, ft2, f2, thread, [1,2,3,4])
+    [ret] = canon_lower(opts, ft2, f2, [1,2,3,4])
     assert(ret == Subtask.State.RETURNED)
 
     heap.memory[12:20] = b'\x01\x00\x00\x00\x02\x03\x04\x05'
-    [ret] = canon_lower(opts, ft3, f3, thread, [12])
+    [ret] = canon_lower(opts, ft3, f3, [12])
     assert(ret == Subtask.State.RETURNED)
 
-    canon_task_return(thread, [], opts, [])
+    canon_task_return([], opts, [])
     return []
 
   inst = ComponentInstance(store)
@@ -2655,60 +2666,60 @@ def test_threads():
   ftbl = Table()
   ft = CoreFuncType(['i32'],[])
 
-  def thread_func1(thread, args):
+  def thread_func1(args):
     assert(args == [13])
     return []
   fi1 = ftbl.add(CoreFuncRef(ft, thread_func1))
 
-  def thread_func2(thread, args):
+  def thread_func2(args):
     [mainthreadi] = args
-    [ret] = canon_thread_yield_to(True, thread, mainthreadi)
+    [ret] = canon_thread_yield_to(True, mainthreadi)
     assert(ret == Cancelled.FALSE)
     return []
   fi2 = ftbl.add(CoreFuncRef(ft, thread_func2))
 
-  def thread_func3(thread, args):
+  def thread_func3(args):
     [mainthreadi] = args
-    [] = canon_thread_resume_later(thread, mainthreadi)
+    [] = canon_thread_resume_later(mainthreadi)
     return []
   fi3 = ftbl.add(CoreFuncRef(ft, thread_func3))
 
-  def thread_func4(thread, args):
+  def thread_func4(args):
     [ptr] = args
-    [ret] = canon_thread_yield(False, thread)
+    [ret] = canon_thread_yield(False)
     assert(ret == Cancelled.FALSE)
     mem[ptr] = mem[ptr] + 1
-    [ret] = canon_thread_yield(False, thread)
+    [ret] = canon_thread_yield(False)
     assert(ret == Cancelled.FALSE)
     mem[ptr] = mem[ptr] + 1
     return []
   fi4 = ftbl.add(CoreFuncRef(ft, thread_func4))
 
-  def core_func(thread, args):
+  def core_func(args):
     assert(not args)
 
-    [mainthreadi] = canon_thread_index(thread)
+    [mainthreadi] = canon_thread_index()
 
-    [threadi] = canon_thread_new_indirect(ft, ftbl, thread, fi1, 13)
-    [ret] = canon_thread_yield_to(True, thread, threadi)
+    [threadi] = canon_thread_new_indirect(ft, ftbl, fi1, 13)
+    [ret] = canon_thread_yield_to(True, threadi)
     assert(ret == Cancelled.FALSE)
 
-    [threadi] = canon_thread_new_indirect(ft, ftbl, thread, fi2, mainthreadi)
-    [ret] = canon_thread_switch_to(True, thread, threadi)
+    [threadi] = canon_thread_new_indirect(ft, ftbl, fi2, mainthreadi)
+    [ret] = canon_thread_switch_to(True, threadi)
     assert(ret == Cancelled.FALSE)
 
-    [threadi] = canon_thread_new_indirect(ft, ftbl, thread, fi3, mainthreadi)
-    [] = canon_thread_resume_later(thread, threadi)
-    [ret] = canon_thread_suspend(True, thread)
+    [threadi] = canon_thread_new_indirect(ft, ftbl, fi3, mainthreadi)
+    [] = canon_thread_resume_later(threadi)
+    [ret] = canon_thread_suspend(True)
     assert(ret == Cancelled.FALSE)
 
     ptr = 4
     mem[ptr] = 0
     for i in range(5):
-      [threadi] = canon_thread_new_indirect(ft, ftbl, thread, fi4, ptr)
-      [] = canon_thread_resume_later(thread, threadi)
+      [threadi] = canon_thread_new_indirect(ft, ftbl, fi4, ptr)
+      [] = canon_thread_resume_later(threadi)
     while mem[ptr] != 10:
-      canon_thread_yield(False, thread)
+      canon_thread_yield(False)
 
     return [42]
 
@@ -2727,25 +2738,25 @@ def test_thread_cancel_callback():
   producer_ft = FuncType([], [U32Type()], async_ = True)
 
   producer_opts1 = mk_opts(async_ = True)
-  def core_producer1(thread, args):
+  def core_producer1(args):
     assert(not args)
     return [CallbackCode.YIELD]
-  def core_producer_callback1(thread, args):
+  def core_producer_callback1(args):
     [event,payload1,payload2] = args
     assert(event == EventCode.NONE and payload1 == 0 and payload2 == 0)
-    [] = canon_task_return(thread, [U32Type()], producer_opts1, [42])
+    [] = canon_task_return([U32Type()], producer_opts1, [42])
     return [CallbackCode.EXIT]
   producer_opts1.callback = core_producer_callback1
   producer_callee1 = partial(canon_lift, producer_opts1, producer_inst, producer_ft, core_producer1)
 
   producer_opts2 = mk_opts(async_ = True)
-  def core_producer2(thread, args):
+  def core_producer2(args):
     assert(not args)
-    [ret] = canon_thread_yield(False, thread)
+    [ret] = canon_thread_yield(False)
     assert(ret == Cancelled.FALSE)
-    [] = canon_task_return(thread, [U32Type()], producer_opts2, [43])
+    [] = canon_task_return([U32Type()], producer_opts2, [43])
     return [CallbackCode.EXIT]
-  def core_producer_callback2(thread, args):
+  def core_producer_callback2(args):
     assert(False)
   producer_opts2.callback = core_producer_callback2
   producer_callee2 = partial(canon_lift, producer_opts2, producer_inst, producer_ft, core_producer2)
@@ -2755,33 +2766,33 @@ def core_producer_callback2(thread, args):
   consumer_mem = bytearray(24)
   consumer_opts = mk_opts(MemInst(consumer_mem, 'i32'), async_ = True)
 
-  def core_consumer(thread, args):
+  def core_consumer(args):
     assert(len(args) == 0)
 
     retp1 = 8
-    [ret] = canon_lower(consumer_opts, producer_ft, producer_callee1, thread, [retp1])
+    [ret] = canon_lower(consumer_opts, producer_ft, producer_callee1, [retp1])
     state,subi1 = unpack_result(ret)
     assert(state == Subtask.State.STARTED)
 
     retp2 = 12
-    [ret] = canon_lower(consumer_opts, producer_ft, producer_callee2, thread, [retp2])
+    [ret] = canon_lower(consumer_opts, producer_ft, producer_callee2, [retp2])
     state,subi2 = unpack_result(ret)
     assert(state == Subtask.State.STARTED)
 
-    [ret] = canon_subtask_cancel(True, thread, subi1)
+    [ret] = canon_subtask_cancel(True, subi1)
     assert(ret == definitions.BLOCKED)
 
     retp3 = 16
-    [seti] = canon_waitable_set_new(thread)
-    [] = canon_waitable_join(thread, subi1, seti)
-    [event] = canon_waitable_set_wait(True, MemInst(consumer_mem, 'i32'), thread, seti, retp3)
+    [seti] = canon_waitable_set_new()
+    [] = canon_waitable_join(subi1, seti)
+    [event] = canon_waitable_set_wait(True, MemInst(consumer_mem, 'i32'), seti, retp3)
     assert(event == EventCode.SUBTASK)
     assert(consumer_mem[retp3] == subi1)
     assert(consumer_mem[retp3+4] == Subtask.State.RETURNED)
     assert(consumer_mem[retp1] == 42)
 
-    [] = canon_waitable_join(thread, subi2, seti)
-    [event] = canon_waitable_set_wait(True, MemInst(consumer_mem, 'i32'), thread, seti, retp3)
+    [] = canon_waitable_join(subi2, seti)
+    [event] = canon_waitable_set_wait(True, MemInst(consumer_mem, 'i32'), seti, retp3)
     assert(event == EventCode.SUBTASK)
     assert(consumer_mem[retp3] == subi2)
     assert(consumer_mem[retp3+4] == Subtask.State.RETURNED)
