EMILUA(7)
π EMILUA(7) β the Emilua manual, bestie edition β¨
okay so this is the Emilua page β it's the "Lua execution engine" one, and honestly it's the embedding page. If you're out here trying to shove Emilua inside your own Boost.Asio program, this is your girl. Let's go π«Ά
πΈ NAME πΈ
Emilua β Lua execution engine. That's the whole vibe. Periodt.
β¨ DESCRIPTION β¨
So you wanna embed Emilua in your own Boost.Asio-based programs? Iconic. Here's literally the exact list of steps you gotta do, no skips:
- Compile and link against Emilua (use Meson or pkg-config so the appropriate compiler flags get filled in automatically β don't do it by hand, that's not the move π ).
#include <emilua/state.hpp>- Instantiate
emilua::app_context. This object needs to stay alive for as long as at least one Lua VM is alive. If you want to be sure, just make sure it outlivesboost::asio::io_contextand you're good to go π - Make sure all
asio::io_contextobjects store an option (asio::config) forscheduler/concurrency_hint. Alternatively you may useemilua::properties_serviceon older ASIO versions. - Call
emilua::make_vm()(seesrc/main.yppfor an example). - Call
emilua::vm_context::fiber_resume()inside the strand returned byemilua::vm_context::strand()to start the Lua VM you created in the previous step (seesrc/main.yppfor an example). - Optionally synchronize against other threads before you exit the application. If you're going to spawn actors in foreign
boost::asio::io_contextobjects in your Lua programs, then yeah, it's a good idea to include this step. See below π
β οΈ Warning β Emilua is not designed to work properly withboost::asio::io_context::stop(). Many cleanup steps will be missed if you call this function. If you need to use it, then spawn Emilua programs in their ownboost::asio::io_contextinstances. Like, for real for real. π
π¦ emilua::app_context π¦
This type stores all the process-wide info that's shared among all Lua VMs β think process arguments, environment, module paths, module caches, the default logger, which VM is the master VM, that whole situation.
If you want to embed the Lua programs in your binary as well, you can pre-populate the module cache here with the contents of all the Lua files you intend to ship in the binary. modules_cache_registry is the member you're looking for π. Do this before you start the first Lua VM. But ngl, there's a better way (see the next section, bestie).
π BUILTIN MODULES π
Just define some or all of the following 3 functions and your module name resolution will be favoured over filesystem queries:
emilua::get_builtin_moduleemilua::get_builtin_rdf_ecemilua::get_builtin_native_module
Using this method instead of pre-filling the module cache allows actors spawned in subprocesses to import these modules as well. It may also lead to better start-up times and a smaller memory footprint β you could even use gperf to have the best module search performance π₯
π MASTER VM π
If you want to allow your Lua programs to change process state that's shared among all program threads (current working directory, signal handlers, ...), then you need to elect one Lua VM to be the master VM.
The one-liner snippet that follows is enough to finish this setup. This step must be done before you call fiber_resume().
appctx.master_vm = vm_ctx;
π CLEANUP AT EXIT π
First things first: make sure emilua::app_context outlives boost::asio::io_context. Non-negotiable.
After boost::asio::io_context::run() returns, you can use the following snippet to synchronize against extra threads and boost::asio::io_context objects your Lua scripts created[1].
{
std::unique_lock<std::mutex> lk{appctx.extra_threads_count_mtx};
while (appctx.extra_threads_count > 0)
appctx.extra_threads_count_empty_cond.wait(lk);
}
π§ ACTORS SPAWNED IN DIFFERENT PROCESSES π§
Emilua has the ability to spawn Lua VMs in their own processes for isolation or sandboxing purposes. To enable this feature, a supervisor process must be created while the program is still single-threaded. (Yes, while. Timing matters π€)
For communication with the supervisor process, Emilua uses an UNIX socket. The file descriptor for this process is stored in app_context::ipc_actor_service_sockfd. See src/main.ypp for an example on how to initialize this variable.
On Linux, you also need to initialize emilua::clone_stack_address.
If you don't intend to have Lua VMs tied to their own processes triggered by Lua programs, then you can skip this step entirely. No pressure π
π INHERITED LOW-NUMBERED FILE DESCRIPTORS π
Okay so here's the situation: after file descriptors are allocated in the process table by some library, it's already too late to detect whether some arbitrary file descriptor was inherited from the parent process. π¬
If you're planning to allow Lua programs to take control over these file descriptors (i.e. system.get_lowfd()), you must add some logic at program startup (before any new file descriptor is allocated) to check which file descriptors were inherited and are allowed to be manipulated from Lua programs, and store them in the Emilua registry:
std::array<bool, 7> lowfds;
lowfds.fill(false);
for (int fd = 3 ; fd <= 9 ; ++fd) {
if (fcntl(fd, F_GETFD) != -1 || errno != EBADF) {
lowfds[fd - 3] = true;
}
}
// ...
emilua::app_context appctx;
appctx.lowfds = lowfds;
π RT SIGNALS π
Emilua reserves a RT signal for internal uses β specifically for cancelling IO operations which have poor system APIs. This signal can be configured at build time:
meson configure -Deintr_rtsigno=RTSIGNO
If you choose the value 0, this support is disabled altogether and Emilua won't reserve any RT signal by itself. If this support is enabled, you must add some code similar to the following one in main():
struct sigaction sa;
std::memset(&sa, 0, sizeof(struct sigaction));
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask, EMILUA_CONFIG_EINTR_RTSIGNO);
sigprocmask(SIG_BLOCK, &sa.sa_mask, /*oldset=*/NULL);
sa.sa_sigaction = emilua::longjmp_on_rtsigno;
sa.sa_flags = SA_RESTART | SA_SIGINFO;
sigaction(EMILUA_CONFIG_EINTR_RTSIGNO, /*act=*/&sa, /*oldact=*/NULL);
β LIBEMILUA-MAIN β
If you aren't trying to embed Emilua into an existing application and just want to create a single-binary application embedding the Lua sources, it'll be easier to just use libemilua-main. It's a ready-to-roll implementation for the function main() with some hooks you may use to customize simple behavior. Lowkey the move π
π NOTES π
[1] Emilua only instantiates new threads and boost::asio::io_context objects if your Lua programs explicitly ask for that when it calls spawn_vm(). You can also disable this feature altogether at build time.
okay that's the whole page, bestie, you're officially an embedding expert πβ¨ this was Emilua 0.12.1-nixpkgs1, dated 1980-01-01 (yes, the most mysterious date ever, we don't question it π) β go forth and slay your build, mwah π
π¦ where this page comes from
- nixos 26.05: emilua 0.12.1
- nixpkgs unstable: emilua 0.12.1
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms