Skip to content
libcatPublic

About

The coroutine version of libuv, mainly used by Swow for now

Topics

Resources

Stars

92 stars

Watchers

8 watching

Forks

Repository files navigation

English | 中文

CAT

Cooperative Async Technology

Synchronous code. High-performance async I/O.

license tests codecov platform architecture

libcat is the C coroutine and I/O core behind Swow. Write with functions, loops and local variables; stackful coroutines and libuv handle asynchronous concurrency.

Performance in the execution path

Switch coroutines in user space. The default context implementation saves and restores registers and stack state in assembly. Switching coroutines does not require waking another system thread.

Wait on one connection. Serve the others. The event loop schedules multiple connections without thread count growing in step with connection count. A socket call can suspend its coroutine before returning while other coroutines keep running.

Move less data. Reuse allocations. Plaintext reads fill caller-provided buffers directly. Vector writes can submit existing buffers together. Sockets reuse write requests when no other write occupies them.

Implementation references

Specific paths depend on the platform, socket type and encryption state. Default scheduling is cooperative: long CPU work or external blocking calls occupy the scheduler thread. Blocking tasks can use the worker interface.

One C core for application I/O

Coroutines and concurrency

Capability What you can do
Stackful coroutines Create, resume and suspend; preserve nested calls and local variables; configure stack size and inspect state, switch counts and elapsed time
Channels Buffered and unbuffered communication, producers and consumers, timed waits, multi-channel selection, closure and data cleanup
Wait groups Join coroutine tasks, wait for a group of concurrent operations and set a wait timeout
Time and waiting Coroutine sleep and delay, timed waits, high-resolution time queries and cached time reads

Networking and protocols

Capability What you can do
Sockets TCP, UDP, IPv4/IPv6, Unix local streams and datagrams; connect, accept, read, write, vector writes and handle passing; operation-specific timeouts
TLS / SSL OpenSSL integration, handshakes and encrypted transport, certificate-chain and peer-name verification, SNI, ALPN and protocol configuration
HTTP and multipart Incremental request and response parsing; URL, status, header and body events; chunked messages and multipart data
WebSocket Frame headers, opcodes, payload lengths, masking-key configuration, masking and unmasking utilities
DNS Wait for name resolution in a coroutine, query addresses and IPs, and configure lookup timeouts
poll / select Bring readable and writable waits for existing descriptors into coroutines while retaining poll/select-style calls

Clients and system I/O

Capability What you can do
cURL Keep CURL * handles, options and callbacks; wait for transfers and organize concurrent requests through coroutine-aware easy and multi interfaces
PostgreSQL / libpq Coroutine connections and queries, parameterized queries, prepared statements and execution; retain libpq connection and result objects
Files and directories Read, write, seek, sync, inspect metadata, rename, traverse directories and lock files; use the sendfile interface
Processes Start child processes, wait for exit in a coroutine and inspect exit status; use fork/waitpid interfaces on supported platforms
Signals Wait for signals in a coroutine, configure wait timeouts and send signals to processes
Workers and asynchronous notifications Execute blocking tasks on workers while the caller waits; bring thread notifications into the event loop and arrange completion cleanup

Data and diagnostics

Capability What you can do
Dynamic buffers Grow, append, format and export data; manage capacity and actual length
Debug logs and watchdog Module logs, coroutine state and switch statistics, scheduler deadlock diagnostics and blocking detection; build with ASan support

OpenSSL, cURL and PostgreSQL are enabled through build configuration. System-specific capabilities depend on the platform.

What the code looks like

TCP: one connection, one coroutine

Each connection has its own stack and buffer. Read, send and close in order; other connections keep working while this one waits for data.

static cat_data_t *echo_connection(cat_data_t *data)
{
    cat_socket_t *connection = (cat_socket_t *) data;
    char buffer[4096];
    ssize_t length;

    while ((length = cat_socket_recv(connection, buffer, sizeof(buffer))) > 0) {
        if (!cat_socket_send(connection, buffer, (size_t) length)) {
            break;
        }
    }

    cat_socket_close(connection);
    return NULL;
}
Starting the connection coroutine

Include cat_api.h and initialize the runtime and scheduler. Pass an accepted connection created with cat_socket_create(NULL, ...):

if (cat_coroutine_run(NULL, echo_connection, connection) == NULL) {
    cat_socket_close(connection);
}

The coroutine closes the connection after a successful start; the caller closes it if starting fails. Peer closure or a read/write error ends processing. Use cat_get_last_error_message() for error diagnostics. See the HTTP echo example for a complete listener entry point.

HTTP: read, parse, respond

For a GET request that receives Hello, CAT!, the core flow after parser initialization is below. The coroutine handles an accepted connection; a request arriving in fragments continues through the same loop.

while (!cat_http_parser_is_completed(&parser)) {
    ssize_t length = cat_socket_recv(connection, buffer, sizeof(buffer));
    if (length <= 0 || !cat_http_parser_execute(&parser, buffer, (size_t) length)) {
        goto _close;
    }
}

if (!cat_socket_send(connection, response, strlen(response))) {
    fprintf(stderr, "%s\n", cat_get_last_error_message());
}
Full handler: GET responds with Hello, CAT!

Include cat_api.h and cat_http.h. Ownership follows the TCP example; use http_connection as the entry point. The example closes each connection after one request; invalid requests or an early disconnect end processing.

static cat_data_t *http_connection(cat_data_t *data)
{
    cat_socket_t *connection = (cat_socket_t *) data;
    cat_http_parser_t parser;
    char buffer[4096];

    cat_http_parser_init(&parser);
    if (!cat_http_parser_set_type(&parser, CAT_HTTP_PARSER_TYPE_REQUEST)) {
        goto _close;
    }
    cat_http_parser_set_events(&parser, CAT_HTTP_PARSER_EVENT_MESSAGE_COMPLETE);

    while (!cat_http_parser_is_completed(&parser)) {
        ssize_t length = cat_socket_recv(connection, buffer, sizeof(buffer));
        if (length <= 0 || !cat_http_parser_execute(&parser, buffer, (size_t) length)) {
            goto _close;
        }
    }

    const char response[] =
        "HTTP/1.1 200 OK\r\n"
        "Content-Type: text/plain; charset=utf-8\r\n"
        "Content-Length: 12\r\n"
        "Connection: close\r\n\r\n"
        "Hello, CAT!\n";

    if (!cat_socket_send(connection, response, strlen(response))) {
        fprintf(stderr, "%s\n", cat_get_last_error_message());
    }

    _close:
    cat_socket_close(connection);
    return NULL;
}

Build

Use CMake 3.18+, a C11 compiler and the assembler toolchain required by the default context implementation.

git clone https://github.com/libcat/libcat.git
cd libcat
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DLIBCAT_BUILD_TESTS=OFF
cmake --build build --config Release --target cat --parallel

The target is the static library cat. Check CMake output for enabled OpenSSL, libcurl and libpq modules. For include paths, compile definitions and link dependencies when embedding the library, see the CMake configuration and Swow integration.

Explore further

The HTTP benchmark entry point provides workload settings. Report measurements with hardware, build configuration, throughput, latency and resource usage. With GoogleTest and a C++20 toolchain installed, run tests with ./run-tests.sh; see the CI configuration for services and platform setup.

Send minimal reproductions through Issues, and contribute code, tests or documentation through Pull Requests. libcat uses the Apache License 2.0; third-party licenses are in LICENSE.full.

About

The coroutine version of libuv, mainly used by Swow for now

Topics

Resources

Stars

92 stars

Watchers

8 watching

Forks

Releases

Packages

Contributors

Languages