cl-async

Implementation notes

I tried other non-blocking libraries, but they either require a large number of dependencies, aren’t portable, or are too specialized to one task. Cl-async uses Libevent2 as the async backend, which is a fast, stable, portable library for asynchronous IO (see my notes on choosing Libevent).

Libevent

Libevent was chosen for a few reasons:

The bindings for libevent are auto-generated. I’m not proud of the bindings themselves, but because I planned to completely wrap them all along, didn’t put too much work into making them pretty and useful. They will most likely stay as-is (and undocumented).

HTTP

Libevent has some limited/simple support for HTTP client/server implementations that cl-async wraps. However, the HTTP portion of cl-async is now deprecated for a variety of reasons. The functions/classes wrapped will be available for the known future, but should really only be used for quick prototyping.

Internals

cl-async tracks anonymous callbacks and libevent objects using what are called data pointers. A data pointer is just a CFFI pointer that can be passed around to libevent callbacks, and can also be used to pull data out of a hash table. So while CFFI callbacks cannot be anonymous, we can fake it by creating a data pointer, assigning the app-supplied anonymous callbacks to the data pointer in a hash table lookup (pointer => callbacks), and sending the pointer (in what would be a void* argument in C) to the libevent callback. Once the generic CFFI callback is fired, it can pull out the anonymous callbacks (as well as any assigned libevent objects) using the data pointer and do what it needs to with them. Data pointers (and the data attached to them in the function/data hash tables) are freed once no longer needed. This is managed completely by cl-async.