Libevfibers

Small C fiber library

Download .zip Download .tar.gz View on GitHub

About libevfibers

A small C fiber library that uses libev based event loop and libcoro based coroutine context switching. As libcoro alone is barely enough to do something useful, this project aims at building a complete fiber API around it while leveraging high performance and flexibility of libev.

The API documentation for current master along with general description is located here.

What fibers are you speaking about?

A fiber is an independent execution context of your program. Unlike classic approach, when you have your `main' being the only execution context, you can have as many of them as you like and switch between them at will. Wait, but that sounds like threads! Well, yes, it really does. Fibers are really similar to threads, but are executed completely in the userspace without any kernel-level scheduling and context switching mechanisms. The main difference between fibers and threads is the scheduling model. Fibers use cooperative multitasking while threads exercise preemptive multitasking. The fibers model requires some cooperation between fibers: if one takes all of the CPU time running in a busy loop (or blocks on e.g. read()) others will not have a chance to run. On the other hand it has some advantages:

  • user space context switching is cheap (just save/restore all registers and adjust a couple of variables in the scheduler)
  • less synchronization is required --- as you have a guarantee that no other fiber is executed concurrently, you don't have to bother about inter-fiber synchronization (except the cases where you want to guard some code block explicitly, i.e. writing to a shared socket).

Fibers also may be considered as an enhancement of event-loop driven applications as they enable you to put all your business logic in one function instead of splitting it in several callbacks (without an extra processing overhead). For example, if you implement a network protocol, you can just create a fiber per each client connection and use fbr_read_all/fbr_write_all to fully read and write protocol messages. Also you don't have to bother about low-level details of how much data was processed during syscalls and how much left. Your fiber will proceed when the required amount of data is read (or an error occurred). This greatly simplifies the protocol processing logic.

But you can't make the whole world non-blocking...

That is true. For the purpose of wrapping something naturally blocking (like a read from the disk) libeio support has been added in v0.4. It enables you to have threads blocking on various operations while the event loop keeps running. Wrappers for blocking POSIX API have been added along with wrapper for eio_custom, which allows one to make any 3rdparty code non-blocking.

Basic usage example

An example is modeled after this one and can be found in the source tree.

This example is a simple TCP server that listens on the 12345 port for incoming connections and writes the current time to the client socket if a connection was established. There are fibers of two types: acceptors and handlers. The former accept all incoming connections and create connection handler fibers, the latter process all the communication with connected clients. Additionally, to see more concurrency, a useless ticker fiber runs simultaneously and outputs the current time to the stderr every 5 seconds.

Recent changes

13 March 2014

I've just released libevfibers 0.4.0!

The most important about this release is libeio support. Now you have access to full POSIX API in fiber-friendly fashion. As eio_custom is also wrapped, you can plug in any 3rdparty library and make it non-blocking. Another change is replacement of libvrb with in-house implementation, which is cleaner and does not leak memory. Other minor changes have been made, please see the full changelog. Documentation for this release can be found here. The list of changes follows:

  • Added no-reclaim mode for a fiber
  • Got rid of 128 bit integers in favour of packed struct
  • Added fbr_ev_wait with timeout (fbr_ev_wait_to)
  • Assert for a yield in the root fiber
  • Added fbr_connect function and test case for it (thanks, Mikhail)
  • Added fbr_buffer_reset method
  • Implemented libeio support (configurable at build time)
  • Debian package includes libeio support by default
  • In-place download&build support for libeio (if it's missing on the system)
  • Replaced libvrb with in-house implementation
  • Fixed unit tests run inside of pbuilder (/dev/shm workaround)
  • Support for cmake-level embedding into other cmake projects
  • Default build type set to RelWithDebInfo
  • Support for building with lto support
  • Added fbr_system, fbr_popenN and fbr_waitpid calls
  • Added fbr_send and fbr_recv wrappers
  • Marked fiber memory pool APIs as deprecated
  • Dropped usage of fiber memory pools in tests
  • Valgrind is now an optional dependency
  • Added travis-ci support
  • Added missing build script (used in test coverage script)
  • Changing license to Apache License, Version 2.0
  • Improved fbr_ev_wait_one performance

12 May 2013

I've just released libevfibers 0.3.0!

This release contains important bugfixes and some useful features. Documentation for this release can be found here. The list of changes follows:

  • Fiber names are now gettable/settable
  • Also a name is now an array, embedded into a fiber structure to simplify memory management.
  • Support for fiber-local storage
  • fbr_reclaim fixes. Now when a fiber is reclaimed, it's filtered out from the stack to make sure that fbr_yield will not ield to a reclaimed fiber. Also fbr_reclaim of self will now never return.
  • Switched fbr_need_log and fbr_set_log_level to static inline
  • Now passing real fiber stack size to coro_create
  • Added ``active'' flag to destructor. To support this I've also added static and dynamic initializers for a destructor structure.
  • Moved fbr_mutex, fbr_cond_var, fbr_buffer into public API to allow stack allocation of these structures which should result in some speedup.
  • Protected libev watchers with desctructors. Now when doing fbr_read a destructor is set before fbr_ev_wait that will stop the watcher in case fiber is reclaimed while we were waiting.
  • ev_now() timestamp in default logger
  • Fixed fbr_ev_wait_one behaviour to match documentation
  • Added missing call of destructors upon fiber reclaim
  • Added stack-allocatable destructor API
  • More rethinking and reworking of the event facility (see 701e067)

9 December 2012

I've just released libevfibers 0.2.0!

API has changed quite a lot from the previous release. Hopefully this API will be more stable and will not be changed that much in the future. Documentation for this release can be found here. The list of changes follows:

  • Dropped "call" and "multicall" interfaces alltogether
  • Added fbr_ev_wait and corresponding machinery
  • Added transactional ring buffer
  • Implemented error subsystem with f_errno
  • Fixed ABA problem with reclaimed fibers
  • Added logging system
  • Added conditional variables
  • Minor utility additions to API
  • Unit tests