FIBER_EVENT_KERNEL vs FIBER_EVENT_POLL vs FIBER_EVENT_SELECT in libfiber
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. 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:
#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, c/src/event/event_kqueue.c, and 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 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 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 to track the desired backend. The factory function event_create() (lines 29–50) instantiates the appropriate implementation:
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:
#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:
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:
#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 |
Defines the three event constants. | L258-L262 |
c/src/event.c |
Factory implementation with event_set() and event_create(). |
L29-L50 |
c/src/event/event_epoll.c |
Linux epoll implementation. |
event_epoll_create() |
c/src/event/event_kqueue.c |
BSD kqueue implementation. |
event_kqueue_create() |
c/src/event/event_iocp.c |
Windows IOCP implementation. |
event_iocp_create() |
c/src/event/event_poll.c |
POSIX poll() implementation. |
event_poll_create() |
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 ofselect, though it incurs O(N) scan overhead. - FIBER_EVENT_SELECT relies on legacy
select()with fixed-sizefd_setbitmaps 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, 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, 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, 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.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →