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

> Effortlessly migrate your libco or libmill code to libfiber. Discover the simple steps to replace functions and run your scheduler for a seamless transition. Follow our complete porting guide.

- Repository: [iQIYI/libfiber](https://github.com/iqiyi/libfiber)
- Tags: migration-guide
- Published: 2026-03-04

---

**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`](https://github.com/iqiyi/libfiber/blob/main/c/src/fiber.c), the `acl_fiber_create` function constructs a new `ACL_FIBER` and places it on the ready queue.

**libco:**

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

```

**libmill:**

```c
go(readwrite_routine(task));

```

**libfiber:**

```c
acl_fiber_create(readwrite_routine, task, 128000);

```

*Source reference:* [`c/src/fiber.c`](https://github.com/iqiyi/libfiber/blob/main/c/src/fiber.c) lines 22-30.

### Update Yield Operations

The yield primitive is defined in [`c/src/fiber.c`](https://github.com/iqiyi/libfiber/blob/main/c/src/fiber.c) at lines 545-551.

**libco:**

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

```

**libmill:**

```c
fiber_yield();

```

**libfiber:**

```c
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:

```c
acl_fiber_schedule();               // or acl_fiber_schedule_with(event_mode)

```

Internally, this invokes `fiber_hook_api(1)` (found in [`c/src/fiber.c`](https://github.com/iqiyi/libfiber/blob/main/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`](https://github.com/iqiyi/libfiber/blob/main/c/src/event.h) lines 30-36.

```c
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`](https://github.com/iqiyi/libfiber/blob/main/benchmark/libco/libco_server.cpp)):

```c
/* 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`](https://github.com/iqiyi/libfiber/blob/main/samples/c/server/main.c)):

```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`](https://github.com/iqiyi/libfiber/blob/main/benchmark/libmill/libmill_server.c)):

```c
static coroutine void doit(tcpsock as) { … }
int main() {
    …
    while (1) {
        tcpsock as = tcpaccept(ls, -1);
        if (as) go(doit(as));
    }
}

```

**libfiber equivalent**:

```c
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`](https://github.com/iqiyi/libfiber/blob/main/c/src/fiber.c) | Core implementation of `acl_fiber_create`, `acl_fiber_yield`, and the scheduler. |
| [`c/src/fiber.h`](https://github.com/iqiyi/libfiber/blob/main/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`](https://github.com/iqiyi/libfiber/blob/main/c/src/event.h) | Event mode constants (`FIBER_EVENT_KERNEL`, `FIBER_EVENT_SELECT`, `FIBER_EVENT_POLL`). |
| [`samples/c/server/main.c`](https://github.com/iqiyi/libfiber/blob/main/samples/c/server/main.c) | Reference echo server implementation. |
| [`benchmark/libco/libco_server.cpp`](https://github.com/iqiyi/libfiber/blob/main/benchmark/libco/libco_server.cpp) | Original libco benchmark for comparison. |
| [`benchmark/libmill/libmill_server.c`](https://github.com/iqiyi/libfiber/blob/main/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`](https://github.com/iqiyi/libfiber/blob/main/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.