How to Migrate libco or libmill Code to libfiber: A Complete Porting Guide

Replace co_create with acl_fiber_create, swap co_yield or fiber_yield for acl_fiber_yield, remove explicit co_resume calls, and start the scheduler with acl_fiber_schedule() to successfully migrate libco or libmill code to libfiber.

Migrating from legacy coroutine libraries to the modern libfiber (iqiyi/libfiber) requires updating only a handful of API calls while preserving your existing network and business logic. Both libco and libmill expose coroutine primitives that map directly to libfiber's C API, allowing you to port applications with minimal structural changes. This guide provides a mechanical translation of coroutine primitives, references the actual source files in the libfiber repository, and includes complete before-and-after code examples to ensure a smooth migration.

API Mapping: libco vs libmill vs libfiber

The three libraries share the same conceptual model—create, yield, and resume—but use different function signatures and conventions.

Feature libco libmill libfiber
Create coroutine co_create(&co, NULL, fn, arg) go(fn(arg)) (C macro) acl_fiber_create(fn, arg, stack_size)
Yield co_yield(co) or co_yield_ct() fiber_yield() acl_fiber_yield()
Resume co_resume(co) Implicit (runtime handles it) Automatic (scheduler resumes ready fibers)
System-call hooking Optional (co_enable_hook_sys()) Built-in Enabled automatically when scheduler starts
Scheduler start co_eventloop() Implicit behind go acl_fiber_schedule() or acl_fiber_schedule_with(event_mode)

Because libfiber’s scheduler always runs in a single thread (or one thread per scheduler instance), you only need to replace the creation and yield primitives. The rest of your code—network logic, DNS resolution, timeouts, etc.—remains unchanged because libfiber hooks the same POSIX APIs that libco and libmill do.

Step-by-Step Migration Guide

Replace Coroutine Creation

In c/src/fiber.c, the acl_fiber_create function constructs a new ACL_FIBER and places it on the ready queue.

libco:

stCoRoutine_t *co;
co_create(&co, NULL, readwrite_routine, task);

libmill:

go(readwrite_routine(task));

libfiber:

acl_fiber_create(readwrite_routine, task, 128000);

Source reference: c/src/fiber.c lines 22-30.

Update Yield Operations

The yield primitive is defined in c/src/fiber.c at lines 545-551.

libco:

co_yield(co);          // or co_yield_ct() for the current routine

libmill:

fiber_yield();

libfiber:

acl_fiber_yield();

Remove Explicit Resume Calls

Libco resumes a specific coroutine via co_resume(co). In libfiber, you never call a resume function; the fiber becomes runnable simply by returning from the function that called acl_fiber_yield (or by calling an API that marks the fiber ready, e.g., acl_fiber_ready). The scheduler picks it up automatically.

Example: The echo client in the libco benchmark calls co_resume(co->co) after attaching a socket. In libfiber, the same logic lives in fiber_accept where a new fiber is created and immediately runnable.

Enable System Call Hooking

Both libco and libmill enable "hooked" system calls (non-blocking read, write, connect, etc.) by calling a single function. In libfiber, the scheduler turns on hooking automatically when it starts:

acl_fiber_schedule();               // or acl_fiber_schedule_with(event_mode)

Internally, this invokes fiber_hook_api(1) (found in c/src/fiber.c lines 804-808), which sets the thread-local flag var_hook_sys_api. All hooked implementations in c/src/hook/*.c check this flag before deciding whether to use the libfiber-aware version.

Thus, you can drop the explicit call to co_enable_hook_sys or any libmill-specific macro.

Choose an Event Backend

Both libco and libmill expose a kernel (epoll/kqueue/iocp) and select/poll mode. libfiber mirrors this through acl_fiber_schedule_with(event_mode). The event_mode constants are defined in c/src/event.h lines 30-36.

int mode = FIBER_EVENT_KERNEL;   // or FIBER_EVENT_SELECT / FIBER_EVENT_POLL
acl_fiber_schedule_with(mode);

Update Build Configuration

  • Headers: Replace #include "libco/co_routine.h" or #include "libmill.h" with #include "fiber/lib_fiber.h".
  • Linker: Link against -lfiber (the compiled static/shared lib).
  • Makefile: The samples under samples/c/ demonstrate correct build recipes.

Complete Migration Examples

Converting a libco Echo Server

Original libco version (benchmark/libco/libco_server.cpp):

/* create a pool of coroutines */
for (int i = 0; i < 1024; i++) {
    task_t *task = (task_t *) calloc(1, sizeof(task_t));
    task->fd = -1;
    co_create(&(task->co), NULL, readwrite_routine, task);
    co_resume(task->co);
}

/* enable hooking */
co_enable_hook_sys();

/* start the event loop */
co_eventloop(co_get_epoll_ct(), 0, 0);

libfiber equivalent (samples/c/server/main.c):

/* create a pool of fibers */
for (int i = 0; i < 1024; i++) {
    task_t *task = (task_t *) calloc(1, sizeof(task_t));
    task->fd = -1;
    acl_fiber_create(readwrite_routine, task, __stack_size);
}

/* no explicit hook call – start scheduler which turns on hooking */
acl_fiber_schedule_with(FIBER_EVENT_KERNEL);

Converting libmill Code

libmill uses the go macro and a tiny runtime. The migration is a macro-to-function substitution.

libmill version (benchmark/libmill/libmill_server.c):

static coroutine void doit(tcpsock as) { … }
int main() {

    while (1) {
        tcpsock as = tcpaccept(ls, -1);
        if (as) go(doit(as));
    }
}

libfiber equivalent:

static void echo_fiber(ACL_FIBER *fb, void *ctx) {
    tcpsock as = (tcpsock) ctx;
 // same read/write loop
}
int main() {

    while (1) {
        tcpsock as = tcpaccept(ls, -1);
        if (as) acl_fiber_create(echo_fiber, as, __stack_size);
    }
    acl_fiber_schedule_with(FIBER_EVENT_KERNEL);
}

The only changes are the removal of go and the addition of acl_fiber_create. All other libmill APIs (tcpsock, tcprecvuntil, tcpsend, etc.) are provided by libfiber’s patch layer.

Key Source Files in libfiber

File Purpose
c/src/fiber.c Core implementation of acl_fiber_create, acl_fiber_yield, and the scheduler.
c/src/fiber.h Public API declarations for all acl_fiber_* functions.
c/src/hook/*.c Hooked versions of read, write, connect, select, etc.
c/src/event.h Event mode constants (FIBER_EVENT_KERNEL, FIBER_EVENT_SELECT, FIBER_EVENT_POLL).
samples/c/server/main.c Reference echo server implementation.
benchmark/libco/libco_server.cpp Original libco benchmark for comparison.
benchmark/libmill/libmill_server.c Original libmill benchmark for comparison.

Summary

  • Replace creation primitives: Swap co_create or the go macro with acl_fiber_create, passing the function pointer, argument, and stack size.
  • Update yield calls: Change co_yield or fiber_yield to acl_fiber_yield.
  • Remove resume logic: Delete explicit co_resume calls; libfiber’s scheduler handles resumption automatically when fibers become ready.
  • Let the scheduler enable hooks: Remove calls to co_enable_hook_sys; acl_fiber_schedule() automatically invokes fiber_hook_api(1) to enable non-blocking I/O.
  • Choose your event backend: Use acl_fiber_schedule_with() and specify FIBER_EVENT_KERNEL, FIBER_EVENT_SELECT, or FIBER_EVENT_POLL to match your previous setup.

Frequently Asked Questions

Do I need to modify my network I/O logic when migrating to libfiber?

No. libfiber hooks the same POSIX APIs (read, write, connect, select, etc.) as libco and libmill. Your existing socket code remains unchanged because the hooked implementations in c/src/hook/*.c automatically yield the fiber when an operation would block, then resume it when the descriptor becomes ready.

What happens to explicit resume calls like co_resume in libfiber?

You must remove them. libfiber does not expose an explicit resume primitive. When a fiber calls acl_fiber_yield() or returns from a hooked I/O call, the scheduler automatically marks it as ready and resumes it later. The fiber becomes runnable by returning from the yield function or by calling acl_fiber_ready.

How does libfiber handle system call hooking compared to libco?

libco requires an explicit call to co_enable_hook_sys() to intercept blocking calls, while libmill enables hooking automatically. libfiber follows the libmill approach: calling acl_fiber_schedule() or acl_fiber_schedule_with() internally invokes fiber_hook_api(1) (found in c/src/fiber.c lines 804-808), which sets the thread-local flag var_hook_sys_api and enables the hooked system calls.

Can I mix libfiber with existing libco or libmill code in the same project?

No, you should not mix these libraries in the same thread or process. Each library implements its own scheduler and system call hooking mechanism, which will conflict at runtime. Complete the migration to libfiber for a given component before linking it with other parts of your application, ensuring only one coroutine runtime manages the thread's execution flow.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →