# FIBER_EVENT_KERNEL vs FIBER_EVENT_POLL vs FIBER_EVENT_SELECT in libfiber

> Understand differences between FIBER_EVENT_KERNEL, FIBER_EVENT_POLL, and FIBER_EVENT_SELECT in libfiber. Choose the best event multiplexer for your concurrency needs.

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

---

**FIBER_EVENT_KERNEL leverages OS-specific high-performance multiplexers (epoll, kqueue, IOCP) for massive concurrency, FIBER_EVENT_POLL uses the POSIX poll() system call for moderate portability, and FIBER_EVENT_SELECT relies on the legacy select() call limited by FD_SETSIZE (typically 1024 descriptors).**

The iqiyi/libfiber library abstracts I/O multiplexing behind three distinct event types defined in [`c/include/fiber/fiber_base.h`](https://github.com/iqiyi/libfiber/blob/main/c/include/fiber/fiber_base.h). Understanding the differences between **FIBER_EVENT_KERNEL**, **FIBER_EVENT_POLL**, and **FIBER_EVENT_SELECT** is critical for optimizing fiber-based network applications across different platforms and connection volumes.

## What Are the libfiber Event Types?

The three constants represent different underlying system calls for multiplexing I/O. They are defined at lines 258–262 of [`c/include/fiber/fiber_base.h`](https://github.com/iqiyi/libfiber/blob/main/c/include/fiber/fiber_base.h):

```c
#define FIBER_EVENT_KERNEL   0   /* epoll/kqueue/iocp */
#define FIBER_EVENT_POLL     1   /* poll */
#define FIBER_EVENT_SELECT   2   /* select */

```

### FIBER_EVENT_KERNEL (Kernel Events)

**FIBER_EVENT_KERNEL** (`0`) is the default on Linux, BSD, and Windows. It maps to the most efficient OS-provided multiplexer available on the platform:

- **Linux**: `epoll` (edge-triggered capable)
- **BSD/macOS**: `kqueue`
- **Windows**: `IOCP` (I/O Completion Ports)

This mode offers **O(1)** notification complexity and minimal per-file-descriptor overhead, scaling efficiently to hundreds of thousands of concurrent connections. The implementation resides in [`c/src/event/event_epoll.c`](https://github.com/iqiyi/libfiber/blob/main/c/src/event/event_epoll.c), [`c/src/event/event_kqueue.c`](https://github.com/iqiyi/libfiber/blob/main/c/src/event/event_kqueue.c), and [`c/src/event/event_iocp.c`](https://github.com/iqiyi/libfiber/blob/main/c/src/event/event_iocp.c), instantiated via `event_epoll_create()`, `event_kqueue_create()`, or `event_iocp_create()`.

### FIBER_EVENT_POLL

**FIBER_EVENT_POLL** (`1`) utilizes the POSIX `poll()` system call. It is available on all POSIX-compatible operating systems and provides a straightforward dynamic array of `struct pollfd` structures.

While more flexible than `select()`, **poll** still requires **O(N)** scans of the descriptor array to identify ready sockets. Performance degrades linearly as connection counts grow, making it suitable for moderate concurrency (hundreds to a few thousand connections) but not extreme high-load scenarios. The implementation is found in [`c/src/event/event_poll.c`](https://github.com/iqiyi/libfiber/blob/main/c/src/event/event_poll.c) via `event_poll_create()`.

### FIBER_EVENT_SELECT

**FIBER_EVENT_SELECT** (`2`) relies on the classic `select()` system call. It uses fixed-size **fd_set** bitmaps, which limits the maximum file descriptor to `FD_SETSIZE` (typically 1024 on most systems).

Like `poll()`, `select()` performs **O(N)** scans and requires copying fd_sets to and from kernel space on every call. It is the **least scalable** option but offers the widest historical compatibility. The implementation resides in [`c/src/event/event_select.c`](https://github.com/iqiyi/libfiber/blob/main/c/src/event/event_select.c) via `event_select_create()`.

## How libfiber Selects the Event Engine

The library uses a thread-local variable `__event_mode` stored in [`c/src/event.c`](https://github.com/iqiyi/libfiber/blob/main/c/src/event.c) to track the desired backend. The factory function `event_create()` (lines 29–50) instantiates the appropriate implementation:

```c
static __thread int __event_mode = FIBER_EVENT_KERNEL;

void event_set(int event_mode) { __event_mode = event_mode; }

EVENT *event_create(int size) {
    switch (__event_mode) {
        case FIBER_EVENT_POLL:   ev = event_poll_create(size);   break;
        case FIBER_EVENT_SELECT: ev = event_select_create(size); break;
        default: /* kernel */    ev = event_epoll_create(size); /* or kqueue/iocp */
    }
    ...
}

```

The **kernel** branch defaults to `event_epoll_create` on Linux, `event_kqueue_create` on BSD, or `event_iocp_create` on Windows, selected via conditional compilation (`#if defined(HAS_EPOLL) …`).

## Performance and Scalability Comparison

| Aspect | FIBER_EVENT_KERNEL | FIBER_EVENT_POLL | FIBER_EVENT_SELECT |
|--------|-------------------|------------------|-------------------|
| **Scalability** | Excellent – O(1) notification, handles >10⁵ fds | Good – O(N) scan, no hard limits | Poor – limited by `FD_SETSIZE` (~1024) |
| **Latency** | Edge-triggered, tunable for low latency | Level-triggered, may wake frequently | Level-triggered, extra kernel copies |
| **Portability** | Platform-specific (Linux/BSD/Windows) | Portable POSIX | Most portable, but constrained on Windows |
| **Memory Usage** | Kernel handle + per-fd structures | Array of `struct pollfd` | Fixed bitmap (`fd_set`) |
| **Best For** | Production servers, high concurrency | Moderate loads, POSIX compatibility | Legacy code, <1024 connections |

## Code Examples

### Selecting the Event Engine Globally

Use `acl_fiber_schedule_with()` or `acl_fiber_schedule_set_event()` before starting the scheduler:

```c
#include "fiber/fiber_base.h"

int main(void)
{
    /* Kernel-based engine (default) - uses epoll/kqueue/IOCP */
    acl_fiber_schedule_with(FIBER_EVENT_KERNEL);
    
    /* Or explicitly use poll */
    // acl_fiber_schedule_set_event(FIBER_EVENT_POLL);
    
    /* Or use select for compatibility */
    // acl_fiber_schedule_set_event(FIBER_EVENT_SELECT);

    acl_fiber_schedule();   /* start the fiber scheduler */
    return 0;
}

```

### Creating a Server with Different Engines

The application logic remains identical; only the initialization changes:

```c
void echo_fiber(ACL_FIBER *fib, void *arg)
{
    ACL_SOCKET *sock = (ACL_SOCKET *)arg;
    char buf[1024];
    int n;

    while ((n = acl_socket_recv(sock, buf, sizeof(buf), 0)) > 0) {
        acl_socket_send(sock, buf, n, 0);
    }
    acl_socket_close(sock);
}

/* Kernel mode (epoll/kqueue/IOCP) */
void start_server_kernel(void)
{
    acl_fiber_schedule_set_event(FIBER_EVENT_KERNEL);
    /* ... setup listen socket ... */
    /* acl_fiber_create(echo_fiber, client_sock, 64 * 1024); */
}

/* Poll mode */
void start_server_poll(void)
{
    acl_fiber_schedule_set_event(FIBER_EVENT_POLL);
    /* same accept loop */
}

/* Select mode */
void start_server_select(void)
{
    acl_fiber_schedule_set_event(FIBER_EVENT_SELECT);
    /* same accept loop */
}

```

### Switching Engine at Runtime (Advanced)

For advanced use cases, set the mode before creating the first fiber:

```c
#include "c/src/event.h"  /* internal header */

/* Set thread-local event mode */
event_set(FIBER_EVENT_POLL);

/* Then schedule with that mode */
acl_fiber_schedule_with(FIBER_EVENT_POLL);

```

> **Important**: The event type must be set **before** the first call to `event_create()`. Once the scheduler is running, the engine cannot be changed without restarting the process.

## Key Source Files

| File | Purpose | Key Lines |
|------|---------|-----------|
| [`c/include/fiber/fiber_base.h`](https://github.com/iqiyi/libfiber/blob/main/c/include/fiber/fiber_base.h) | Defines the three event constants. | [L258-L262](https://github.com/iqiyi/libfiber/blob/master/c/include/fiber/fiber_base.h#L258-L262) |
| [`c/src/event.c`](https://github.com/iqiyi/libfiber/blob/main/c/src/event.c) | Factory implementation with `event_set()` and `event_create()`. | [L29-L50](https://github.com/iqiyi/libfiber/blob/master/c/src/event.c#L29-L50) |
| [`c/src/event/event_epoll.c`](https://github.com/iqiyi/libfiber/blob/main/c/src/event/event_epoll.c) | Linux `epoll` implementation. | `event_epoll_create()` |
| [`c/src/event/event_kqueue.c`](https://github.com/iqiyi/libfiber/blob/main/c/src/event/event_kqueue.c) | BSD `kqueue` implementation. | `event_kqueue_create()` |
| [`c/src/event/event_iocp.c`](https://github.com/iqiyi/libfiber/blob/main/c/src/event/event_iocp.c) | Windows `IOCP` implementation. | `event_iocp_create()` |
| [`c/src/event/event_poll.c`](https://github.com/iqiyi/libfiber/blob/main/c/src/event/event_poll.c) | POSIX `poll()` implementation. | `event_poll_create()` |
| [`c/src/event/event_select.c`](https://github.com/iqiyi/libfiber/blob/main/c/src/event/event_select.c) | POSIX `select()` implementation. | `event_select_create()` |

## Summary

- **FIBER_EVENT_KERNEL** (default) provides the highest performance using OS-native multiplexers (`epoll`, `kqueue`, `IOCP`), scaling to hundreds of thousands of connections with O(1) complexity.
- **FIBER_EVENT_POLL** offers portable POSIX compatibility using `poll()`, suitable for moderate concurrency (thousands of connections) without the descriptor limits of `select`, though it incurs O(N) scan overhead.
- **FIBER_EVENT_SELECT** relies on legacy `select()` with fixed-size `fd_set` bitmaps limited to 1024 descriptors, making it suitable only for legacy code or very small-scale applications.

Choose **FIBER_EVENT_KERNEL** for production servers, **FIBER_EVENT_POLL** for portable moderate loads, and **FIBER_EVENT_SELECT** only when constrained by legacy requirements or descriptor limits.

## Frequently Asked Questions

### Which event type should I use for a high-concurrency production server?

Use **FIBER_EVENT_KERNEL** (the default). According to the libfiber source code in [`c/src/event.c`](https://github.com/iqiyi/libfiber/blob/main/c/src/event.c), this mode automatically selects `epoll` on Linux, `kqueue` on BSD/macOS, or `IOCP` on Windows. These kernel mechanisms provide O(1) notification complexity and minimal per-descriptor overhead, scaling efficiently to hundreds of thousands of concurrent connections without the linear scan penalties of poll or the descriptor limits of select.

### Can I switch between FIBER_EVENT_POLL and FIBER_EVENT_KERNEL at runtime?

No, you cannot switch engines once the scheduler is running. As implemented in [`c/src/event.c`](https://github.com/iqiyi/libfiber/blob/main/c/src/event.c), the event type is stored in a thread-local variable `__event_mode` that is read once during `event_create()`. You must call `acl_fiber_schedule_set_event()` or `event_set()` before the first call to `acl_fiber_schedule()` or `event_create()`. Changing the mode after initialization requires restarting the process.

### Why does FIBER_EVENT_SELECT have a 1024 connection limit?

**FIBER_EVENT_SELECT** uses the POSIX `select()` system call, which relies on fixed-size bitmap structures called `fd_set`. The maximum file descriptor number is constrained by the `FD_SETSIZE` macro, which is typically defined as 1024 on most systems. While you can recompile with a larger value, the O(N) scan performance and bitmap copying overhead make select unsuitable for high concurrency compared to the kernel event types that use dynamic structures and O(1) notification.

### Is FIBER_EVENT_POLL portable across all platforms?

Yes, **FIBER_EVENT_POLL** is the most portable POSIX-compliant option. According to the libfiber implementation in [`c/src/event/event_poll.c`](https://github.com/iqiyi/libfiber/blob/main/c/src/event/event_poll.c), this mode uses the standard `poll()` system call available on Linux, macOS, BSD, and other POSIX systems. Unlike kernel events which require platform-specific code paths (epoll vs kqueue vs IOCP), poll provides a uniform interface across operating systems, making it ideal for applications prioritizing portability over maximum scalability.