Skip to content

sirhcm/libbraid

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

libbraid -- C99 library for coroutines.

WHAT

  libbraid is heavily inspired by higan's libco and Russ Cox's libtask.

  The goal is to provide the non-blocking IO features of libtask, while keeping
  the simplicity of libco. Additionally, libbraid should be highly portable,
  just by writing a bit of assembly for saving and restoring contexts (and some
  glue in `ctxcreate`).

  Non-blocking IO is supported using one of the following backends: kqueue,
  io_uring (via liburing), and poll (requires timerfd).

INSTALLATION

  First run `./configure`. Options are available using the `--help` flag.
  Subsequently, `make` can be run, along with `make install`. After
  installation, use pkg-config to determine dependencies.

TERMINOLOGY

 * braid: a set of cooperatively scheduled cords.
 * cord: a coroutine.
 * context: a set of registers and stack that can be switched to.

USAGE

  libbraid provides two APIs: the low-level context switching API and a
  higher-level cooperative scheduling API. The low-level API can be accessed
  through `braid/ctx.h`, which provides the following functions:

    ctx_t   ctxempty(void);

            Creates a new, empty context.

    ctx_t   ctxcreate(void (*f)(usize), usize stacksize, usize arg);

            Creates a new context with entry point `f` and stack size
            `stacksize`. When switching to this context, `arg` will be passed.

    void    ctxswap(ctx_t *old, ctx_t new);

            Swaps to the context `new`. The current context is saved in `old`.
            Use `ctxempty` to create the first `old` context.

    void    ctxdel(ctx_t c);

            Cleans up the memory associated with a context (including unmapping
            the stack if it exists).

    void   *ctxstack(ctx_t c);

            Returns a pointer to the context's stack. It is assumed that stacks
            grow downward, so this will be a pointer to the guard page.

  The higher-level API is available through `braid.h`, which provides the
  following functions:

    braid_t braidinit(void);

            Creates a new braid.

    cord_t  braidadd(braid_t b, void (*f)(), usize stacksize, const char *name,
                     uchar flags, int nargs, ...);
    cord_t  braidvadd(braid_t b, void (*f)(), usize stacksize, const char *name,
                      uchar flags, int nargs, va_list args)

            Adds a new cord to the braid with entry point `f` and stack size
            `stacksize`. The cord will be scheduled cooperatively with other
            cords in the braid. `nargs` variadic arguments will be passed to
            `f`, unless `nargs` is 0, in which case just `b` will be passed.
            The `name` argument is nullable. The `flags` arguement sets the
            type of cord that is being created:

              * CORD_NORMAL: a normal cord.
              * CORD_SYSTEM: ignored when deciding the braid size.

            Returns the newly created cord.

    void    braidstart(braid_t b);

            Launches the braid scheduler, which continues executing until all
            cords have exited.

    void    braidyield(braid_t b);

            Called by a cord, yielding control back to the braid scheduler.

    usize   braidblock(braid_t b);

            Called by a cord to block itself. The cord will not be scheduled
            again until it is added back to the braid with `braidunblock`.
            The return value is determined by the how the cord was added back
            to the braid (see `braiunblock`).

    int     braidunblock(braid_t b, cord_t c, usize val);

            Adds an existing cord `c` to the braid `b`. This function is mostly
            used for plumbing blocking operations (see `braidblock`). If this
            cord was stopped by `braidblock`, `val` is the value that will be
            returned from the `braidblock` call. If the cord is not found in the
            blocked list, returns -1, otherwise returns 0.

    void    braidstop(braid_t b);

            Shuts down a braid, causing braidstart to return, and the braid to
            be cleaned up.

    void    braidexit(braid_t b);

            Called by a cord to exit the braid. The cord will be removed from the
            braid and will not be scheduled again. If the braid size is now 0,
            will cause braidlaunch to return.

    cord_t  braidcurr(braid_t b);

            Returns the currently scheduled cord in the braid `b`.

    uint    braidcnt(braid_t b);

            Returns the number of (non-system) scheduled cords in the braid `b`.

    uint    braidsys(braid_t b);

            Returns the number of scheduled system cords in the braid `b`.

    uint    braidblk(const braid_t b)

            Returns the number of blocked cords in the braid `b`

    void  **braiddata(braid_t b, uchar key);

            Returns a pointer to braid-shared storage for the given key. If the
            key is not already present, a new pointer will be allocated.

            NB: the channel API stores data under the key 0xFC, the non-blocking
            IO API stores data under the key 0xFD, and the clock API stores
            data under the key 0xFE. As such, if you use any of these APIs, do
            not use their keys for your own data.

    void    braidinfo(braid_t b);

            Prints the state of all the cords in the braid `b` to stdout.

    void    cordhalt(braid_t b, cord_t c);

            Halts the cord `c` in the braid `b`. The cord cannot be rescheduled.

    void   *cordmalloc(braid_t b, cord_t c, size_t sz);
    void   *cordzalloc(braid_t b, cord_t c, size_t sz);
    void   *cordrealloc(braid_t b, cord_t c, void *p, size_t sz);
    void    cordfree(braid_t b, cord_t c, void *p);
    void   *braidmalloc(braid_t b, size_t sz);
    void   *braidzalloc(braid_t b, size_t sz);
    void   *braidrealloc(braid_t b, void *p, size_t sz);
    void    braidfree(braid_t b, void *p);

            Arena allocators, scoped to currently running cord or overall braid
            lifetimes.

  Non-blocking IO routines below all rely on the following system cord in
  `braid/io.h`.

    void    iovisor(braid_t b);

            This function must be started as a system cord in order for the
            following non-blocking IO routines to function.

  File descriptor manipulation is available through `braid/fd.h`.

    void    fdwait(braid_t b, int fd, char rw);

            Yields the current cord until the file descriptor is ready for
            reading or writing, as specified by `rw` ('r' for read, 'w' for
            write).

    int     fdread(braid_t b, int fd, void *buf, size_t count);

            Yields the current cord until `poll(2)` indicates that the file
            descriptor `fd` is ready for reading, then performs a `read(2)`.

    int     fdwrite(braid_t b, int fd, const void *buf, size_t count);

            Yields the current cord until `poll(2)` indicates that the file
            descriptor `fd` is ready for writing, then performs a `write(2)`.

  TCP/IP non-blocking IO is available through `braid/tcp.h`.

    int     tcpdial(braid_t b, int fd, const char *host, int port);

            Connects to the specified host and port. The `host` argument should
            be specified like "127.0.0.1" or "example.com". The `fd` argument can
            be used to specify a preexisting socket, but if negative, `tcpdial`
            will create a new socket. Returns the connected socket.

    int     tcplisten(const char *host, int port);

            Helper function to create a TCP/IP listening socket. The `host`
            argument can be NULL or "localhost" to listen on all interfaces,
            or a specific IP address in the same format as `tcpdial`. Returns
            the listening socket.

    int     tcpaccept(braid_t b, int fd);

            Non-blocking waits for a connection on the listening socket `fd`.
            Returns the accepted socket.

  Go-style (unbuffered) channels are available through `braid/ch.h`.

    ch_t    chopen(braid_t b);

            Creates a new channel.

    void    chclose(braid_t b, ch_t ch);

            Closes the channel `ch`. No new sends will be accepted. Memory will
            be freed as soon as the channel is drained.

    int     chsend(braid_t b, ch_t ch, usize data);

            Sends `data` to the channel `ch` in the braid `b`, blocking until
            the data is received. Returns 0 on success, or -1 if the channel was
            closed.

    usize   chrecv(braid_t b, ch_t ch, char *ok);

            Receives data from the channel `ch` in the braid `b`, blocking until
            data is available. Returns the received data, or 0 if the channel
            closed. `ok` is nullable, but if non-null will distinguish between
            validly returning 0 or error.

  Clock routines are available through `braid/ck.h`.

    void    cknsleep(braid_t b, ulong ns);
    void    ckusleep(braid_t b, ulong us);
    void    ckmsleep(braid_t b, ulong ms);
    void    cksleep(braid_t b, ulong s);

            Sleeps for the specified amount of time, yielding the current cord
            until the time has passed.

    usize   ckntimeout(braid_t b, usize (*f)(), usize sz, ulong ns, int nargs, ...);
    usize   ckutimeout(braid_t b, usize (*f)(), usize sz, ulong us, int nargs, ...);
    usize   ckmtimeout(braid_t b, usize (*f)(), usize sz, ulong ms, int nargs, ...);
    usize   cktimeout(braid_t b, usize (*f)(), usize sz, ulong s, int nargs, ...);
    usize   ckntimeoutv(braid_t b, usize (*f)(), usize sz, ulong ns, int nargs, va_list args);
    usize   ckutimeoutv(braid_t b, usize (*f)(), usize sz, ulong us, int nargs, va_list args);
    usize   ckmtimeoutv(braid_t b, usize (*f)(), usize sz, ulong ms, int nargs, va_list args);
    usize   cktimeoutv(braid_t b, usize (*f)(), usize sz, ulong s, int nargs, va_list args);

            Runs the function f, and returns the value returned by f, or if the timer expires, 0.

About

simple C99 coroutines

Resources

License

Stars

Watchers

Forks

Languages