lev — Lilush API

←index

Overview

Asynchronous I/O runtime for Lilush, built on epoll. Provides coroutine-based concurrency with timers, UDP/TCP sockets, cancellation tokens, and structured task combinators.

Functions

NameSignature
set_loggerset_logger(logger)
ownown(resource) -> resource
deferdefer(fn)
disowndisown(resource) -> resource
spawnspawn(fn, opts) -> task
awaitawait(task) -> result, err
sleepsleep(seconds, cancel) -> true, err
cancel_tokencancel_token() -> token
racerace(fns) -> results, err
allall(fns) -> results, err
on_signalon_signal(signum, handler) -> true, err
stopstop()
udpudp() -> socket, err
tls_identitytls_identity(cert_path, key_path, hostname) -> identity, err
tcptcp() -> socket, err
connectconnect(addr, port, opts) -> socket, err
listenlisten(addr, port, opts) -> listener, err
execexec(path, args, opts) -> proc, err
task_nametask_name(name) -> name
statsstats() -> stats
task_treetask_tree() -> tasks
task_dumptask_dump() -> tasks
enable_dump_signalenable_dump_signal(format)
runrun(fn) -> result, err

set_logger(logger)

Inject a structured logger for LEV error reporting

Sets a std.logger instance to receive LEV error messages (cleanup errors, detached task errors). The logger persists across run() calls. Pass nil to revert to stderr.

own(resource) -> resource

Register a resource for auto-close when the current task completes

Registers resource for automatic cleanup when the current coroutine finishes (success or error). The resource must have a close() method. Returns the resource for chaining: local sock = lev.own(lev.connect(...)). No-op outside lev.run() or from non-spawned coroutines.

defer(fn)

Register an arbitrary cleanup function for the current task

Registers fn to run when the current coroutine finishes (success or error). Cleanups run in LIFO order, like Go's defer. Errors in cleanup functions are logged via log_error and don't prevent other cleanups from running. No-op outside lev.run() or from non-spawned coroutines.

disown(resource) -> resource

Remove a resource from auto-close tracking

Removes resource from the current task's cleanup list (identity comparison). Use for ownership transfer between coroutines. Returns the resource for chaining. No-op if the resource is not tracked.

spawn(fn, opts) -> task

Spawn a new concurrent task

Spawns fn as a new coroutine that runs concurrently within the current lev.run() event loop. Returns a task handle that can be passed to await().

Options:

await(task) -> result, err

Wait for a spawned task to complete

Yields the current coroutine until the given task finishes. Returns the task's return values on success, or nil, err on error.

sleep(seconds, cancel) -> true, err

Suspend the current coroutine for a duration

Yields the current coroutine and resumes it after the given number of seconds (fractional seconds supported). Returns true on normal completion, or nil, "cancelled" if a cancel token fires before the timer expires.

cancel_token() -> token

Create a cancellation token

Returns a cancel token that can be passed to sleep(), connect(), accept(), and other blocking operations to enable cooperative cancellation. Call token:cancel() to interrupt all registered waiters.

race(fns) -> results, err

Run functions concurrently, return first result

Spawns each function in the list. When the first one finishes (success or error), cancels the rest and returns that result. Each function receives a cancel token as its first argument.

all(fns) -> results, err

Run functions concurrently, wait for all to complete

Spawns each function and waits for all to complete. Returns a list of result tables (each {values...}). If any task errors, cancels the rest and returns nil, err immediately.

on_signal(signum, handler) -> true, err

Register a signal handler

Registers a callback to be invoked when the specified signal is delivered. Must be called within lev.run(). The handler receives the signal number as its argument.

stop()

Force-stop the event loop

Sets the loop's running flag to false, causing lev.run() to exit on the next iteration regardless of active coroutines. Intended for use in signal handlers to implement graceful shutdown with a deadline. Must be called within lev.run().

udp() -> socket, err

Create a non-blocking UDP socket

Returns a wrapped UDP socket that integrates with the event loop. The recvfrom method yields the current coroutine until data arrives, with optional timeout and cancellation support.

tls_identity(cert_path, key_path, hostname) -> identity, err

Parse a cert+key PEM pair into a reusable TLS identity

Parses the PEM certificate chain and private key from files into a Lua userdata that can be passed to starttls() via the identity config field. The identity is parsed once and reused across connections, avoiding per-connection file I/O and PEM parsing overhead.

The optional hostname is stored for SNI matching.

tcp() -> socket, err

Create a non-blocking TCP socket

Returns a wrapped TCP socket that integrates with the event loop. Provides send, recv, recv_exactly, recv_until methods with async I/O via wait_readable/wait_writable. Supports TLS upgrade via starttls.

connect(addr, port, opts) -> socket, err

Connect to a remote TCP server

Creates a TCP socket, connects to addr:port, and optionally performs a TLS handshake. Returns a wrapped TCP socket on success.

Options:

listen(addr, port, opts) -> listener, err

Create a bound listening TCP socket

Creates and binds a TCP listener socket. Returns a listener object with an accept method that yields until a client connects. accept takes a timeout (number) or an options table { timeout = N, cancel = token }.

Options:

exec(path, args, opts) -> proc, err

Execute an external command asynchronously

Spawns an external command as a child process with non-blocking pipe I/O and pidfd-based exit notification. Returns a process handle with async send, recv, recv_stderr, read_all, wait, and kill methods.

Options:

task_name(name) -> name

Set or get the current task's name

With an argument, sets the name of the current task and returns it. Without an argument, returns the current task's name (or nil). No-op outside lev.run().

stats() -> stats

Get fast loop stats snapshot

Returns a table with loop counters: active_coros, registered_fds, timer_count, ready_count (from C core), and task_count (Lua-side count of entries in the task registry). Returns nil outside lev.run().

task_tree() -> tasks

Get structured task list

Returns a sorted array of task info tables, each with fields: id, name, status, detached, parent_id, co_status. Sorted by id for stable output. Returns nil outside lev.run().

task_dump() -> tasks

Get detailed task dump with tracebacks

Like task_tree() but adds a traceback field for suspended coroutines, showing the exact yield point. Has meaningful overhead -- use for debugging, not monitoring. Returns nil outside lev.run().

enable_dump_signal(format)

Enable SIGUSR1 task dump handler

Registers a SIGUSR1 handler that calls task_dump() and outputs the result. Supports "text" (default) and "json" formats. Uses the injected logger if set, otherwise stderr. Safe because signal handlers in LEV run via signalfd dispatch (not async-signal context).

run(fn) -> result, err

Run the event loop with a main function

Creates an event loop, spawns fn as the main coroutine, and runs the loop until all tasks complete. Returns the main task's results or nil, err if the main task errored.

Nested lev.run() calls are not allowed.