SkillAgentSearch skills...

Libgoc

A Go-style CSP concurrency runtime for C: threadpools, stackful coroutines, channels, select, async I/O, and garbage collection in one coherent API.

Install / Use

/learn @divs1210/Libgoc
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

CI CD

libgoc

libgoc

A Go-style CSP concurrency runtime for C: threadpools, stackful coroutines, channels, select, async I/O, and garbage collection in one coherent API.

libgoc is a runtime library for C programs that want Go-style CSP concurrency backed by a managed memory environment. It is written in plain C for maximum reach and portability.

The library provides stackful coroutines ("fibers"), channels, a select primitive (goc_alts), timeout channels, a managed thread pool, and optional runtime telemetry (goc_stats). Boehm GC is a required dependency and is linked automatically.

libgoc is built for:

  • C developers who want a Go-style concurrency runtime without switching to Go.
  • Language implementors targeting C/C++ as a compilation target, or writing multithreaded interpreters.

Dependencies:

| | | |---|---| | minicoro | fiber suspend/resume (cross-platform; POSIX and Windows) | | libuv | event loop, threads, timers, cross-thread wakeup | | Boehm GC | garbage collection | | picohttpparser | HTTP/1.1 request parser (vendored MIT); used by goc_http; compiled in by default; disable with -DLIBGOC_SERVER=OFF |

Pre-built static libraries are available on the Releases page:

  • Linux (x86-64)
  • macOS (arm64)
  • Windows (x86-64)

Helper libraries:

Also see:


Table of Contents


Examples

1. Ping-pong

Two fibers exchange a message back and forth over a pair of unbuffered channels. This is the canonical CSP "ping-pong" pattern — each fiber blocks on a take, then immediately puts to wake the other side.

#include "goc.h"
#include <stdio.h>

#define N_ROUNDS 5

typedef struct {
    goc_chan* recv;   /* channel this fiber reads from  */
    goc_chan* send;   /* channel this fiber writes to   */
    const char* name;
} player_args_t;

static void player_fiber(void* arg) {
    player_args_t* a = arg;

    goc_val_t* v;
    while ((v = goc_take(a->recv))->ok == GOC_OK) {
        int count = goc_unbox_int(v->val);
        printf("%s %d\n", a->name, count);
        if (count >= N_ROUNDS) {
            goc_close(a->send);
            return;
        }
        goc_put(a->send, goc_box_int(count + 1));
    }
}

static void main_fiber(void* _) {
    goc_chan* a_to_b = goc_chan_make(0);
    goc_chan* b_to_a = goc_chan_make(0);

    player_args_t ping_args = { .recv = b_to_a, .send = a_to_b, .name = "ping" };
    player_args_t pong_args = { .recv = a_to_b, .send = b_to_a, .name = "pong" };

    goc_chan* done_ping = goc_go(player_fiber, &ping_args);
    goc_chan* done_pong = goc_go(player_fiber, &pong_args);

    /* Kick off the exchange with the first message. */
    goc_put(a_to_b, goc_box_int(1));

    /* Wait for both fibers to finish. */
    goc_take(done_ping);
    goc_take(done_pong);
}

int main(void) {
    goc_init();
    goc_go(main_fiber, NULL);
    goc_shutdown();
    return 0;
}

What this example demonstrates:

  • goc_chan_make(0) — unbuffered channels enforce a synchronous rendezvous: each goc_put blocks until the other fiber calls goc_take, and vice versa.
  • goc_go — spawns both player fibers on the current pool (or default pool when called outside fiber context) and returns a join channel that is closed automatically when the fiber returns.
  • goc_close — when the round limit is reached the active fiber closes the forward channel, causing the partner's next goc_take to return GOC_CLOSED and exit its loop cleanly.

2. Custom thread pool with goc_go_on

Use goc_pool_make when you need workloads isolated from the default pool — for example, CPU-bound tasks that should not starve I/O fibers.

This example fans out several independent jobs onto a dedicated pool, then collects all results from main using goc_take_sync.

#include "goc.h"
#include <stdio.h>
#include <stdlib.h>

/* -------------------------------------------------------------------------
 * Worker fiber: sum integers in [lo, hi) and send the result back.
 * In a real program this would be image processing, compression, etc.
 * ------------------------------------------------------------------------- */
typedef struct {
    long      lo, hi;
    goc_chan* result_ch;
} worker_args_t;

static void sum_range(void* arg) {
    worker_args_t* a = arg;
    long acc = 0;
    for (long i = a->lo; i < a->hi; i++)
        acc += i;
    goc_put(a->result_ch, goc_box_int(acc));
}

/* =========================================================================
 * main
 * ========================================================================= */
#define N_WORKERS 4
#define RANGE     1000000L

int main(void) {
    goc_init();

    /*
     * A dedicated pool for CPU-bound work. The default pool (started by
     * goc_init) is left free for I/O fibers and other goc_go calls.
     */
    goc_pool* cpu_pool = goc_pool_make(N_WORKERS);

    goc_chan*     result_ch = goc_chan_make(0);
    worker_args_t workers[N_WORKERS];
    long          chunk = RANGE / N_WORKERS;

    /* Fan out: spawn each worker on the CPU pool with goc_go_on. */
    for (int i = 0; i < N_WORKERS; i++) {
        workers[i].lo        = i * chunk;
        workers[i].hi        = (i == N_WORKERS - 1) ? RANGE : (i + 1) * chunk;
        workers[i].result_ch = result_ch;
        goc_go_on(cpu_pool, sum_range, &workers[i]);
    }

    /* Fan in: collect results from the main thread with goc_take_sync. */
    long total = 0;
    for (int i = 0; i < N_WORKERS; i++) {
        goc_val_t* v = goc_take_sync(result_ch);
        if (v->ok == GOC_OK) total += (long)goc_unbox_int(v->val);
    }

    printf("sum [0, %ld) = %ld\n", RANGE, total);

    /*
     * Destroy the CPU pool. 
     * Optional, shown here for completeness.
     * All undestroyed pools are automatically cleaned up by goc_shutdown().
     */
    goc_pool_destroy(cpu_pool);

    goc_shutdown();
    return 0;
}

What this example demonstrates:

  • goc_pool_make / goc_pool_destroy — creates and tears down a dedicated pool, isolated from the default pool started by goc_init.
  • goc_go_on — pins each worker fiber to the CPU pool.
  • Fan-out / fan-in over a shared result channel — no explicit synchronisation primitives beyond channels.
  • goc_pool_destroy blocks till all the fibers running on the pool finish, then frees resources. goc_shutdown tears down the rest of the runtime.

3. Using goc_malloc

goc_malloc allocates memory on the Boehm GC heap. Allocations are collected automatically when no longer reachable — no free is needed. This is the intended allocator for long-lived program objects: nodes, buffers, application data, and so on.

#include "goc.h"
#include <stdio.h>

typedef struct node_t {
    int           value;
    struct node_t* next;
} node_t;

static node_t* build_list(int n) {
    node_t* head = NULL;
    for (int i = n - 1; i >= 0; i--) {
        node_t* node = goc_malloc(sizeof(node_t));
        node->value = i;
        node->next  = head;
        head        = node;
    }
    return head;
}

int main(void) {
    goc_init();

    node_t* node = build_list(10);
    while (node != NULL) {
        printf("%d ", node->value);
        node = node->next;
    }
    printf("\n");

    goc_shutdown();
    return 0;
}

A few things to keep in mind:

  • goc_malloc is a thin wrapper around GC_malloc. Memory is zero-initialised.

Public API

Initialisation and shutdown

| Function | Signature | Description | |---|---|---| | goc_init | void goc_init(void) | Initialise the runtime. Must be called exactly once as the first call in main(), from the process main thread. | | goc_shutdown | void goc_shutdown(void) | Shut down the runtime. Blocks until all in-flight fibers finish, then tears down all pools, channels, and the event loop. Must be called from the process main thread. |


Memory allocation

| Function | Signature | Description | |---|---|---| | goc_malloc | void* goc_malloc(size_t n) | Allocate n bytes

View on GitHub
GitHub Stars23
CategoryDevelopment
Updated1d ago
Forks0

Languages

C

Security Score

90/100

Audited on Apr 7, 2026

No findings