An event loop library in C with cross-platform support and efficient event handling. This library provides a unified interface for handling I/O events, timers, and signals across different operating systems.
-
Cross-Platform Support
- Linux: epoll with signalfd integration
- BSD/MacOS: kqueue with EVFILT_SIGNAL
- Windows: select with IOCP integration
-
High-Performance Timer System
- O(1) complexity timer wheel algorithm
- Microsecond resolution
- Support for one-shot and periodic timers
-
Robust Signal Handling
- Unified API across all platforms
- Platform-optimized implementations
- Support for graceful shutdown patterns
-
Thread Pool Integration
- Configurable worker count
- Automatic task distribution
- CPU-intensive task offloading
# Build the library
gcc -I./include -c src/*.c
# Build examples
gcc -I./include -o echo_server examples/echo_server.c src/*.c
gcc -I./include -o thread_example examples/thread_example.c src/*.c -pthread// Create and destroy event loop
eventloop_t* eventloop_create(void);
eventloop_t* eventloop_create_with_threads(int num_threads);
void eventloop_destroy(eventloop_t* loop);
// Run the event loop
int eventloop_run(eventloop_t* loop);
void eventloop_stop(eventloop_t* loop);// Add/remove/modify events
int eventloop_add_event(eventloop_t* loop, event_t* ev);
int eventloop_remove_event(eventloop_t* loop, event_t* ev);
int eventloop_modify_event(eventloop_t* loop, event_t* ev, uint32_t events);// Add one-shot or periodic timer
int eventloop_add_timer(eventloop_t* loop, event_t* ev, uint64_t timeout_ms);
int eventloop_add_periodic_timer(eventloop_t* loop, event_t* ev, uint64_t interval_ms);// Add/remove signal handlers
int eventloop_add_signal(eventloop_t* loop, int signo, signal_callback_fn cb, void* arg);
int eventloop_remove_signal(eventloop_t* loop, int signo);
// Signal callback function type
typedef void (*signal_callback_fn)(eventloop_t* loop, int signo, void* arg);#include "eventloop.h"
#include "network.h"
void on_client_data(eventloop_t* loop, socket_t* sock, void* data, size_t len, void* arg) {
// Echo data back to client
socket_write(sock, data, len);
}
int main() {
eventloop_t* loop = eventloop_create();
socket_t* server = socket_create(loop);
socket_set_read_cb(server, on_client_data, NULL);
socket_bind(server, NULL, 5000);
socket_listen(server, SOMAXCONN);
eventloop_run(loop);
return 0;
}#include "eventloop.h"
void heavy_computation(void* arg) {
// CPU-intensive work here
}
int main() {
eventloop_t* loop = eventloop_create_with_threads(4);
// Submit task to thread pool
eventloop_submit_task(loop, heavy_computation, arg);
eventloop_run(loop);
return 0;
}#include "eventloop.h"
#include <signal.h>
void handle_sigint(eventloop_t* loop, int signo, void* arg) {
printf("Received SIGINT (Ctrl+C)\n");
eventloop_stop(loop);
}
int main() {
eventloop_t* loop = eventloop_create();
// Add SIGINT handler
eventloop_add_signal(loop, SIGINT, handle_sigint, NULL);
eventloop_run(loop);
return 0;
}#include "eventloop.h"
#include <signal.h>
#include <unistd.h>
void handle_signal(eventloop_t* loop, int signo, void* arg) {
printf("Received signal %d\n", signo);
if (signo == SIGTERM) {
eventloop_stop(loop);
}
}
void handle_io(eventloop_t* loop, event_t* ev, void* arg) {
char buffer[1024];
ssize_t n = read(ev->fd, buffer, sizeof(buffer));
if (n > 0) {
printf("Read %zd bytes\n", n);
}
}
int main() {
eventloop_t* loop = eventloop_create();
// Add signal handlers
eventloop_add_signal(loop, SIGTERM, handle_signal, NULL);
eventloop_add_signal(loop, SIGUSR1, handle_signal, NULL);
// Add I/O event
event_t ev;
event_init(&ev, STDIN_FILENO, EV_READ, handle_io, NULL);
eventloop_add_event(loop, &ev);
eventloop_run(loop);
eventloop_destroy(loop);
return 0;
}- Uses
epoll()for I/O multiplexing - Integrates with
signalfd()for signal handling - Supports all POSIX signals
// Linux-specific signal handling is efficient and reliable
// signalfd provides a file descriptor interface for signals
eventloop_add_signal(loop, SIGUSR1, handle_signal, NULL);
eventloop_add_signal(loop, SIGUSR2, handle_signal, NULL);- Uses
kqueue()withEVFILT_READ/WRITE EVFILT_SIGNALfor signal handling- Supports all POSIX signals
// BSD systems use kqueue for signal handling
// Supports all standard POSIX signals
eventloop_add_signal(loop, SIGHUP, handle_signal, NULL);
eventloop_add_signal(loop, SIGTERM, handle_signal, NULL);- Uses
select()for I/O multiplexing - Limited to console control signals
- IOCP integration for better performance
// Windows has limited signal support
// Only console control handlers are supported
eventloop_add_signal(loop, SIGINT, handle_ctrl_c, NULL); // Ctrl+C
eventloop_add_signal(loop, SIGBREAK, handle_break, NULL); // Ctrl+Break-
Signal Handling
- Keep signal handlers minimal
- Use the event loop to defer complex processing
- Clean up signal handlers before destroying the loop
-
Resource Management
- Always check return values from API calls
- Use provided cleanup functions
- Remove events before closing file descriptors
-
Thread Safety
- Don't access event loop from multiple threads
- Use thread pool for CPU-intensive tasks
- Keep signal handlers thread-safe
-
Event Loop Hangs
- Check for unclosed file descriptors
- Verify all events are properly removed
- Ensure signal handlers are properly registered
-
Memory Leaks
- Use valgrind for leak detection
- Implement proper cleanup in signal handlers
- Check for resource leaks in callbacks
-
Performance Issues
- Monitor thread pool utilization
- Use appropriate timer intervals
- Check for blocking operations in callbacks
- Fork the repository
- Create a feature branch
- Make your changes
- Run the test suite
- Submit a pull request
This project is licensed under the MIT License - see the LICENSE file for details.
- Inspired by libuv and libev
- Built with modern C programming practices
- Designed for production use
-
Windows Platform
- Limited to console control signals only
- No support for POSIX-style signals
- Cannot handle application-specific signals
-
Signal Queueing
- Signal coalescing may occur under heavy load
- Not all signals may be delivered if sent rapidly
- Platform-specific queueing behavior varies
-
Thread Safety
- Signal handlers should be thread-safe
- Avoid complex operations in signal handlers
- Use the event loop to defer processing to the main thread