.. _fiber-h:

=======
fiber.h
=======

`Fibers <https://en.wikipedia.org/wiki/Fiber_(computer_science)>`_ are light,
co-operative threads of execution. The `fiber.h
<https://github.com/emscripten-core/emscripten/blob/main/system/include/emscripten/fiber.h>`_
header defines a low-level API for manipulating Fibers in Emscripten. Fibers are
implemented with :ref:`asyncify section`, so you must link your program with
:ref:`ASYNCIFY` if you intend to use them.

Fibers are intended as a building block for asynchronous control flow
constructs, such as coroutines. They supersede the legacy coroutine API that was
available in the fastcomp backend. This API is similar to, but distinct from,
POSIX `ucontext <https://en.wikipedia.org/wiki/Setcontext>`_.

.. contents:: Table of Contents
    :local:
    :depth: 3

API Reference
=============

Types
-----

.. c:type:: emscripten_fiber_t

  This structure represents a Fiber context `continuation
  <https://en.wikipedia.org/wiki/Continuation>`_. The runtime does not keep
  references to these objects, they only contain information needed to perform
  the context switch. The switch operation updates some of the contents,
  however.

  .. c:member:: void *stack_base

    Upper limit of the C stack region. The stack grows down, so this is the
    initial position of the stack pointer. Must be at least 16-byte aligned.

  .. c:member:: void *stack_limit

    Lower limit of the C stack region. Must be below
    :c:member:`emscripten_fiber_t.stack_base`.

  .. c:member:: void *stack_ptr

    Current position of the stack pointer. Must be between
    :c:member:`emscripten_fiber_t.stack_base` and
    :c:member:`emscripten_fiber_t.stack_limit`.

  .. c:member:: em_arg_callback_func entry

    Entry point. If not NULL, this function will be called when the fiber is
    switched into. Otherwise, :c:member:`emscripten_fiber_t.asyncify_data` is
    used to rewind the call stack.

  .. c:member:: void *user_data

    Opaque pointer, passed as-is to :c:member:`emscripten_fiber_t.entry`.

  .. c:member:: asyncify_data_t asyncify_data

    Asyncify data structure. Used to unwind and rewind the call stack when switching fibers.

.. c:type:: asyncify_data_t

  .. c:member:: void *stack_ptr

    Current position of the Asyncify stack pointer.

    The Asyncify stack is distinct from the C stack. It contains the call stack
    as well as the state of WASM locals. Unlike the C stack, it grows up.

  .. c:member:: void *stack_limit

    Upper limit of the Asyncify stack region.

  .. c:member:: int rewind_id

    Opaque handle to the function that needs to be called in order to rewind the
    call stack. This value is only meaningful to the runtime.

    .. warning:: Rewind IDs are currently thread-specific. This makes it
       impossible to resume a fiber that has been started from a different thread.


Functions
---------

.. c:function:: void emscripten_fiber_init(emscripten_fiber_t *fiber, em_arg_callback_func entry_func, void *entry_func_arg, void *c_stack, size_t c_stack_size, void *asyncify_stack, size_t asyncify_stack_size)

  Initializes a fiber context. It can then be entered by calling :c:func:`emscripten_fiber_swap`.

  :param emscripten_fiber_t* fiber: Pointer to the fiber structure.
  :param em_arg_callback_func entry_func: Entry point function, called when the fiber is entered for the first time.
  :param void* entry_func_arg: Opaque pointer passed to `entry_func`.
  :param void* c_stack: Pointer to memory region to use for the C stack. Must be at least 16-byte aligned. This points to the lower bound of the stack, regardless of growth direction.
  :param size_t c_stack_size: Size of the C stack memory region, in bytes.
  :param void* asyncify_stack: Pointer to memory region to use for the Asyncify stack. No special alignment requirements.
  :param size_t asyncify_stack_size: Size of the Asyncify stack memory region, in bytes.

  .. note:: If `entry_func` returns, the entire program will end, as if `main` had returned. To avoid this, you can use :c:func:`emscripten_fiber_swap` to jump to another fiber.

.. c:function:: void emscripten_fiber_init_from_current_context(emscripten_fiber_t *fiber, void *asyncify_stack, size_t asyncify_stack_size)

  Partially initializes a fiber based on the currently active context. This is
  needed in order to switch back from a fiber into the thread's original
  context.

  This function sets up :c:member:`emscripten_fiber_t.stack_base` and
  :c:member:`emscripten_fiber_t.stack_limit` to refer to the current stack
  boundaries, sets :c:member:`emscripten_fiber_t.entry` to `NULL`, and makes
  :c:member:`emscripten_fiber_t.asyncify_data` refer to the provided Asyncify
  stack memory. Other fields are not changed.

  Fibers initialized by this function are not complete. They are only suitable
  to pass as the first argument to :c:func:`emscripten_fiber_swap`. Doing that
  completes the continuation, making it possible to switch back to the original
  context with another :c:func:`emscripten_fiber_swap`, as with a normal fiber.

  :param emscripten_fiber_t* fiber: Pointer to the fiber structure.
  :param void* asyncify_stack: Pointer to memory region to use for the Asyncify
                               stack. No special alignment requirements.
  :param size_t asyncify_stack_size: Size of the Asyncify stack memory region, in bytes.

.. c:function:: void emscripten_fiber_swap(emscripten_fiber_t *old_fiber, emscripten_fiber_t *new_fiber)

  Performs a fiber context switch.

  :param emscripten_fiber_t* old_fiber: Fiber representing the current context.
                                        It will be partially updated, such as
                                        that switching back into it via another
                                        call to :c:func:`emscripten_fiber_swap`
                                        would appear to return from the original call.
  :param emscripten_fiber_t* new_fiber: Fiber representing the target context.
                                        If the fiber has an entry point, it will
                                        be called in the new context and set
                                        to `NULL`. Otherwise,
                                        :c:member:`emscripten_fiber_t.asyncify_data`
                                        is used to rewind the call stack. If the
                                        fiber is invalid or incomplete, the
                                        behavior is undefined.
