µEv (libuEv)

repository·master·Indexed 42 days ago

https://github.com/troglobit/libuev

A minimal, lightweight event loop library for Linux that provides a simplified wrapper around the epoll() family of APIs, including epoll, eventfd, signalfd, and timerfd. It serves as a small-footprint alternative to larger libraries like libevent or libev, supporting I/O, timer, cron, and signal watchers.

Tokens
3.3K
Snippets
12
Records
16
Agent score
54%

What's inside µEv

  1. Introduction to µEv

    master

    µEv (libuEv) is a lightweight event loop library for Linux that wraps the epoll() family of APIs. It is designed to be small in size and feature set, making it a minimal alternative to larger libraries like libevent or libev.

    Supported Linux APIs:

    • epoll(2)
    • eventfd(2)
    • signalfd(2)
    • timerfd(2)
  2. Handle watcher errors and HUP events

    master

    When using µEv, callbacks must handle specific error and status conditions.

    • UEV_ERROR: This event indicates a problem with the watcher. libuEv stops the watcher before the callback runs, so the callback is responsible for deciding whether to attempt a restart or perform other error recovery.
    • UEV_HUP: For I/O watchers, you must check for this event to detect hang-ups.
  3. How libuEv works: The event loop lifecycle

    master

    libuEv follows a standard event-driven lifecycle consisting of four main stages:

    1. Initialize Context: Create a thread-local event context using uev_init().
    2. Register Watchers: For every event type (I/O, timers, signals, or generic events), register a watcher (uev_t) with the context using the appropriate _init() function. Each watcher requires a callback function.
    3. Run the Loop: Start the event loop with uev_run(). This function blocks and dispatches events to your registered callbacks.
    4. Exit: Terminate the loop using uev_exit(), which can be called from within a callback.

    Critical Requirements:

    • Non-blocking I/O: All file descriptors used with I/O watchers MUST be opened in non-blocking mode. Using blocking streams is a common source of bugs in event-driven applications.
    • Error Handling: Callbacks must always check the events mask for UEV_ERROR. If an error occurs, the watcher is stopped by libuEv and may need to be restarted by the application.
    // 1. Setup context
    uev_ctx_t ctx;
    uev_init(&ctx);
    
    // 2. Register watcher (e.g., signal)
    uev_t sig_watcher;
    uev_signal_init(&ctx, &sig_watcher, my_callback, NULL, SIGTERM);
    
    // 3. Run loop
    uev_run(&ctx, UEV_NONE);
    
    // 4. Exit (usually via uev_exit inside a callback)
  4. Build and install libuEv from a release tarball

    master

    libuEv uses the GNU configure and build system. To build and install the library, run the following commands:

    1. Run ./configure (use --enable-examples to include bundled examples).
    2. Compile using make.
    3. Run make check to execute the unit test suite.
    4. Install the library using sudo make install-strip.
    5. Update the shared library cache with sudo ldconfig.
    ./configure
    make -j5
    make check
    sudo make install-strip
    sudo ldconfig
  5. Integrate libuEv into a C project

    master

    libuEv is a standard C library. You can include the single header file and use pkg-config to manage compilation flags and linking.

    Include Header:

    #include <uev/uev.h>

    Compilation with pkg-config:

    gcc `pkg-config --libs --static --cflags libuev` -o my_app my_app.c

    GNU Autotools Integration: In your configure.ac:

    PKG_CHECK_MODULES([uev], [libuev >= 1.4.0])

    In your Makefile.am:

    proggy_CFLAGS = $(uev_CFLAGS)
    proggy_LDADD  = $(uev_LIBS)
  6. Build libuEv from GIT sources

    master

    To build from a Git clone, you must have automake, autoconf, and libtool installed on your system. Run the autogen.sh script to generate the configuration files before proceeding with the standard build process.

    ./autogen.sh
  7. Access libuEv documentation on Debian

    master

    If you have installed the development package (libuev-dev) on a Debian-based system, the project's documentation is available locally in the /usr/share/doc/ directory. You can view the README and API documentation using less:

    • README: /usr/share/doc/libuev-dev/README.md
    • API Documentation: /usr/share/doc/libuev-dev/API.md
    less /usr/share/doc/libuev-dev/README.md
    less /usr/share/doc/libuev-dev/API.md
  8. Install libuEv on Debian-based systems

    master

    On Debian-based operating systems (such as Debian, Ubuntu, or Linux Mint), libuEv is available via pre-packaged .deb files. The distribution is split into two packages:

    1. Runtime package: Contains only the .so shared library.
    2. Development package (libuev-dev): Contains the header files and the .a static library required for compiling applications that link against libuEv.

    To develop with libuEv, ensure you install the -dev package.

  9. Example: Monitoring Joystick Input

    master

    This example demonstrates using an I/O watcher to monitor a joystick device (/dev/input/js0). It uses O_NONBLOCK to open the file descriptor and handles UEV_ERROR and UEV_HUP to manage device connectivity.

    Key Logic:

    • Open the device with O_NONBLOCK.
    • Initialize an I/O watcher with uev_io_init using UEV_READ.
    • In the callback, if UEV_ERROR is detected, attempt to restart the watcher with uev_io_start(w).
    • If UEV_HUP or a zero-byte read is detected, the joystick has been disconnected.
    #include <uev/uev.h>
    // ... other includes
    
    static void joystick_cb(uev_t *w, void *arg, int events) {
        if (UEV_ERROR == events) {
            warnx("Spurious problem with the joystick watcher, restarting.");
            uev_io_start(w);
            return;
        }
    
        // ... read logic ...
    
        if (cnt == 0 || UEV_HUP == events) {
            warn("Joystick disconnected");
            return;
        }
        // ... process event ...
    }
    
    int main(void) {
        uev_ctx_t ctx;
        uev_t js;
        int fd = open("/dev/input/js0", O_RDONLY | O_NONBLOCK);
        
        uev_init(&ctx);
        uev_io_init(&ctx, &js, joystick_cb, NULL, fd, UEV_READ);
        return uev_run(&ctx, 0);
    }
  10. Example: Set up a periodic timer watcher

    master

    This example demonstrates how to initialize an event context, create a timer watcher that triggers every 2 seconds, and handle the UEV_ERROR condition within the callback.

    /* Set up a timer watcher to call cb() every other second */
    #include <stdio.h>
    #include <uev/uev.h>
    
    static void cb(uev_t *w, void *arg, int events)
    {
            if (UEV_ERROR == events) {
                puts("Problem with timer, attempting to restart.");
                uev_timer_start(w);
                return;
            }
    
            puts("Every other second");
    }
    
    int main(void)
    {
            uev_ctx_t ctx;
            uev_t timer;
    
            uev_init(&ctx);
            uev_timer_init(&ctx, &timer, cb, NULL, 2 * 1000, 2 * 1000);
    
            return uev_run(&ctx, 0);
    }
  11. Create an event context

    master

    To monitor events, you must first create an event context (uev_ctx_t). This context manages the event loop and the registered watchers. It is typically intended to be thread-local.

    Use uev_init() to initialize the context.

    uev_ctx_t ctx;
    int result = uev_init(&ctx);
  12. Register a Cron watcher (Absolute Timers)

    master

    Use uev_cron_init() to schedule absolute timers based on a specific time_t timestamp.

    Note on Wall Clock Changes: If the system wall clock changes (e.g., via NTP), absolute timers will receive a UEV_HUP error event in their callback. It is generally recommended to use relative timers (uev_timer_init) whenever possible to avoid this complexity.

    /* Schedule an absolute timer */
    int uev_cron_init(uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, time_t when, time_t period);
    
    /* Change when or period */
    int uev_cron_set(uev_t *w, int when, time_t period);
    
    /* Restart a stopped cron */
    int uev_cron_start(uev_t *w);
    
    /* Stop a cron */
    int uev_cron_stop(uev_t *w);