ljuv is a module built on libuv and LuaJIT 2.1 (HEAD).
A main event loop combined with coroutines can be a way to nicely parallelize and synchronize various operations; one of the computing problems.
Not only a binding to libuv, the module aims to expose different levels of abstraction and address problems like multi-threading.
Project Status
This project is no longer in development.
It is obsolete in my methodology. It was used to build the server of slayers-online-re and I was considering its use client side for a game framework with asynchronicity at its core.
But, it is not pragmatic for multiple reasons:
- There is friction with the way graphics APIs work.
- Creating a game framework is a lot of work considering that I will probably be the only user.
- I no longer value event loops as I did at the time; I prefer to have full control over the flow of execution by default and only use specialized event loops when needed (e.g. elscheduler).
I don't plan to create game servers with an event loop like libuv anymore, I will directly use LÖVE and build something custom. In the case of slayers-online-re, where the server needs are higher, I built something platform-specific with luaposix instead.
Also, the choices made with this project resulted in something very complicated, especially about the interaction between the libuv design (callbacks) and the FFI. It is probably one of the worst FFI bindings I could do (and was my first).
Install
See src/
, rockspecs/
or
luarocks.
Documentation
Note: When the documentation is very succinct because it is just a layer above the libuv API, refer to the libuv documentation.
Levels of abstraction
If the regular API is too high level, e.g. poor performance or some features are missing, the ljuv submodules may be directly used:
ljuv.libuv
: The FFI binding and library namespace of libuv (not exhaustive).ljuv.wrapper
: The ljuv C API, which is used to implement additional features like thread and channel.
Error handling
Callbacks
Errors from loop callbacks are properly handled; they will be deferred, queued and propagated by loop:run as soon as possible (with a call to loop:stop). After an error, the state of the loop is still valid and loop:run can be called again to propagate the next error or to continue execution.
Note: In most cases catching errors from loop:run, the root of the application, is not meaningful and it should probably be done at the callback level instead.
Resources management
Loop and handles
Handles are created and owned by a loop, they are not garbage collected on their own. To release an handle and its resources, handle:close must be called. A loop will close its handles when garbage collected.
Warning: Due to the design of ljuv, if an handle callback is an anchor for the associated loop, e.g. it has a reference to it directly or indirectly by its upvalues, the loop will not be garbage collected until the VM is closed. For most applications it will not matter because they will use the default global loop as their main event loop.
Although ljuv uses cdata objects, care is taken to preserve their identity in the API. When an handle is passed to a callback it will be the same cdata object created by the loop. That cdata object identifies the handle and can be used as a key.
Shared objects
Objects like channels or shared flags are properly managed from multiple threads by the use of reference counting (if correctly exported/imported).
An async handle exported to another thread will act more as a weak reference: the validity of the handle is checked before attempting an operation.
String buffers
ljuv uses LuaJIT's string buffers to pass data between threads. Look at the LuaJIT documentation to know about what kind of data is supported.