sig_runtime.h

include/sig/runtime/sig_runtime.h

sig_runtime

typedef struct sig_runtime sig_runtime;

A sig_runtime owns one running system: the loaded project, the runtime modules registered with it (management, and later IPC, security, ...), and the lifecycle that starts and stops them. States: CREATED -> STARTING -> RUNNING -> STOPPING -> CREATED. A stopped runtime may be started again.

sig_runtime_module

typedef struct {
    const char *name; /* required; unique within a runtime */
    sig_status_t (*create)(sig_runtime *rt, const char *config, void **out_state);
    sig_status_t (*start)(void *state);
    sig_status_t (*stop)(void *state);
    void (*destroy)(void *state);
} sig_runtime_module;

A runtime module is an optional feature of the runtime: a named vtable of hooks around an opaque state created from a config string, like sig_persistence_module. Every hook except `name` is nullable. Hooks run on the thread that calls sig_runtime_start() / sig_runtime_stop(). `create` receives the runtime handle and may use the public runtime API (including sig_runtime_project() and sig_runtime_module_state() for modules registered before it).

sig_runtime_config

typedef struct {
    sig_persistence *store;               /* borrowed; must outlive the runtime */
    const sig_capsule_registry *capsules; /* borrowed; must outlive the runtime */
} sig_runtime_config;

`store` holds the project (see sig_project_load()): it is read at every sig_runtime_start() and written by project edits.

sig_runtime_module_fn

typedef sig_status_t (*sig_runtime_module_fn)(const char *name, void *ctx);

sig_runtime_create

sig_status_t sig_runtime_create(sig_runtime ** out, const sig_runtime_config * cfg)
ParameterType
outsig_runtime **
cfgconst sig_runtime_config *

sig_runtime_destroy

void sig_runtime_destroy(sig_runtime * rt)
ParameterType
rtsig_runtime *

Stops the runtime if it is running, then frees everything. Must not race with any other call on the same runtime. NULL is a no-op.

sig_runtime_register_module

sig_status_t sig_runtime_register_module(sig_runtime * rt, const sig_runtime_module * module, const char * config)
ParameterType
rtsig_runtime *
moduleconst sig_runtime_module *
configconst char *

Registers `module` (borrowed; must outlive the runtime) with an optional config string (copied). Only valid in CREATED (SIG_ERR_STATE otherwise). A duplicate name is SIG_ERR_EXISTS. Modules are created and started in registration order.

sig_runtime_start

sig_status_t sig_runtime_start(sig_runtime * rt)
ParameterType
rtsig_runtime *

CREATED -> RUNNING. Loads the project, then calls every module's create in registration order, then every module's start in registration order (two phases: a create may use the state of earlier-registered modules, and none has started yet). If anything fails, the started modules are stopped, then the created ones destroyed, each in reverse order; the project is destroyed, the runtime returns to CREATED, and the failure status is returned. Finally the project's auto_start services are started; a failure there is logged but does not fail the call. SIG_ERR_STATE unless CREATED. Clears any pending shutdown request.

sig_runtime_stop

sig_status_t sig_runtime_stop(sig_runtime * rt)
ParameterType
rtsig_runtime *

RUNNING -> CREATED. Stops all services, then stops every module and then destroys every module, each in reverse order, then destroys the project. A no-op SIG_OK in CREATED; SIG_ERR_STATE while STARTING or STOPPING.

sig_runtime_request_shutdown

void sig_runtime_request_shutdown(sig_runtime * rt)
ParameterType
rtsig_runtime *

Thread-safe: any thread (a dedicated sigwait() thread, a management request, a module) may ask the host to shut down. This only records the request; it does not stop anything itself. It briefly takes an internal mutex and signals a condition variable, so it is NOT async-signal-safe: never call it from a signal handler. Block SIGINT/SIGTERM in all threads and call it from a thread that sigwait()s for them.

sig_runtime_wait

sig_status_t sig_runtime_wait(sig_runtime * rt)
ParameterType
rtsig_runtime *

Blocks until sig_runtime_request_shutdown() has been called since the last sig_runtime_start() (returns immediately if it already was). The host then calls sig_runtime_stop().

sig_runtime_set_loop

sig_status_t sig_runtime_set_loop(sig_runtime * rt, sig_loop * loop)
ParameterType
rtsig_runtime *
loopsig_loop *

Single-threaded operation. Giving the runtime an event loop (only valid in CREATED, else SIG_ERR_STATE; NULL returns to threaded operation) makes every service driven rather than given a worker thread: the host runs the loop, whose step hook must call sig_runtime_step(), to deliver signals and run jobs. The loop is borrowed and must outlive the runtime's use of it (through sig_runtime_stop()). Runtime modules find it with sig_runtime_loop() and may use it for their own fds and timers; they must remove those in `stop`. sig_runtime_step() steps every running service once and reports when it next needs to run, as sig_service_step() does (a deadline of "now" means more is queued). It holds the service-control lock while it runs, so it is serialized with sig_runtime_service_*(). SIG_ERR_STATE unless RUNNING. Work arriving from other threads and shutdown requests wake the loop, so it steps again promptly. sig_runtime_shutdown_requested() is the non-blocking form of sig_runtime_wait().

sig_runtime_loop

sig_loop * sig_runtime_loop(sig_runtime * rt)
ParameterType
rtsig_runtime *

shared with sig_runtime_set_loop

Single-threaded operation. Giving the runtime an event loop (only valid in CREATED, else SIG_ERR_STATE; NULL returns to threaded operation) makes every service driven rather than given a worker thread: the host runs the loop, whose step hook must call sig_runtime_step(), to deliver signals and run jobs. The loop is borrowed and must outlive the runtime's use of it (through sig_runtime_stop()). Runtime modules find it with sig_runtime_loop() and may use it for their own fds and timers; they must remove those in `stop`. sig_runtime_step() steps every running service once and reports when it next needs to run, as sig_service_step() does (a deadline of "now" means more is queued). It holds the service-control lock while it runs, so it is serialized with sig_runtime_service_*(). SIG_ERR_STATE unless RUNNING. Work arriving from other threads and shutdown requests wake the loop, so it steps again promptly. sig_runtime_shutdown_requested() is the non-blocking form of sig_runtime_wait().

sig_runtime_step

sig_status_t sig_runtime_step(sig_runtime * rt, bool * out_has_deadline, struct timespec * out_deadline)
ParameterType
rtsig_runtime *
out_has_deadlinebool *
out_deadlinestruct timespec *

shared with sig_runtime_set_loop

Single-threaded operation. Giving the runtime an event loop (only valid in CREATED, else SIG_ERR_STATE; NULL returns to threaded operation) makes every service driven rather than given a worker thread: the host runs the loop, whose step hook must call sig_runtime_step(), to deliver signals and run jobs. The loop is borrowed and must outlive the runtime's use of it (through sig_runtime_stop()). Runtime modules find it with sig_runtime_loop() and may use it for their own fds and timers; they must remove those in `stop`. sig_runtime_step() steps every running service once and reports when it next needs to run, as sig_service_step() does (a deadline of "now" means more is queued). It holds the service-control lock while it runs, so it is serialized with sig_runtime_service_*(). SIG_ERR_STATE unless RUNNING. Work arriving from other threads and shutdown requests wake the loop, so it steps again promptly. sig_runtime_shutdown_requested() is the non-blocking form of sig_runtime_wait().

sig_runtime_shutdown_requested

bool sig_runtime_shutdown_requested(sig_runtime * rt)
ParameterType
rtsig_runtime *

shared with sig_runtime_set_loop

Single-threaded operation. Giving the runtime an event loop (only valid in CREATED, else SIG_ERR_STATE; NULL returns to threaded operation) makes every service driven rather than given a worker thread: the host runs the loop, whose step hook must call sig_runtime_step(), to deliver signals and run jobs. The loop is borrowed and must outlive the runtime's use of it (through sig_runtime_stop()). Runtime modules find it with sig_runtime_loop() and may use it for their own fds and timers; they must remove those in `stop`. sig_runtime_step() steps every running service once and reports when it next needs to run, as sig_service_step() does (a deadline of "now" means more is queued). It holds the service-control lock while it runs, so it is serialized with sig_runtime_service_*(). SIG_ERR_STATE unless RUNNING. Work arriving from other threads and shutdown requests wake the loop, so it steps again promptly. sig_runtime_shutdown_requested() is the non-blocking form of sig_runtime_wait().

sig_runtime_project

sig_project * sig_runtime_project(sig_runtime * rt)
ParameterType
rtsig_runtime *

Borrowed. Valid from the first module's create() until after the last module's stop(); NULL when the runtime is not running.

sig_runtime_edit_fn

typedef sig_status_t (*sig_runtime_edit_fn)(sig_project *project, void *ctx);

Runs fn(project, ctx) under the service-control lock, so it is serialized with sig_runtime_service_start/stop/restart and with sig_runtime_dump_info. This is the ONLY safe way to call the sig_project_put_/patch_/delete_ editing functions while the runtime is running. Returns fn's status; SIG_ERR_STATE if the runtime is not RUNNING (fn is not called); SIG_ERR_INVALID_ARG for NULL rt/fn. fn must not call back into sig_runtime_service_*(), sig_runtime_edit() or sig_runtime_dump_info(). It does NOT exclude sig_mgmt readers. While sig_mgmt is in use, an edit run directly through this function can free a service or configuration that a mgmt read is walking: use the sig_mgmt_put_/patch_/delete_ operations.

sig_runtime_edit

sig_status_t sig_runtime_edit(sig_runtime * rt, sig_runtime_edit_fn fn, void * ctx)
ParameterType
rtsig_runtime *
fnsig_runtime_edit_fn
ctxvoid *

shared with sig_runtime_edit_fn

Runs fn(project, ctx) under the service-control lock, so it is serialized with sig_runtime_service_start/stop/restart and with sig_runtime_dump_info. This is the ONLY safe way to call the sig_project_put_/patch_/delete_ editing functions while the runtime is running. Returns fn's status; SIG_ERR_STATE if the runtime is not RUNNING (fn is not called); SIG_ERR_INVALID_ARG for NULL rt/fn. fn must not call back into sig_runtime_service_*(), sig_runtime_edit() or sig_runtime_dump_info(). It does NOT exclude sig_mgmt readers. While sig_mgmt is in use, an edit run directly through this function can free a service or configuration that a mgmt read is walking: use the sig_mgmt_put_/patch_/delete_ operations.

sig_runtime_capsules

const sig_capsule_registry * sig_runtime_capsules(const sig_runtime * rt)
ParameterType
rtconst sig_runtime *

The registry of capsules the runtime was created with.

sig_runtime_module_state

sig_status_t sig_runtime_module_state(sig_runtime * rt, const char * name, void ** out)
ParameterType
rtsig_runtime *
nameconst char *
outvoid **

Returns the state a registered module's create() produced. SIG_ERR_NOT_FOUND for an unregistered name; SIG_ERR_STATE if the module is registered but not yet created.

sig_runtime_walk_modules

sig_status_t sig_runtime_walk_modules(sig_runtime * rt, sig_runtime_module_fn fn, void * ctx)
ParameterType
rtsig_runtime *
fnsig_runtime_module_fn
ctxvoid *

Visits registered module names in registration order. A non-SIG_OK return from fn stops the walk and becomes the result.

sig_runtime_service_start

sig_status_t sig_runtime_service_start(sig_runtime * rt, const char * name)
ParameterType
rtsig_runtime *
nameconst char *

Service control. The only supported way for modules and transports to start/stop a project service at runtime: calls are serialized by one mutex and rejected with SIG_ERR_STATE unless the runtime is RUNNING, so they cannot race sig_runtime_stop(). SIG_ERR_NOT_FOUND for an unknown service; start on an already-started service is SIG_ERR_EXISTS; stop on a stopped service is SIG_OK; restart is stop-then-start under one hold of the mutex. Each blocks until the service's hooks have run (see sig_service.h). start and restart first rebuild a stopped service whose definition (or a capsule configuration it uses) was edited since it was built; see sig_project_refresh_service(). Do NOT call sig_runtime_service_* (or sig_runtime_uptime_seconds) from a service or capsule thread: the control mutex is held across service start/stop joins, so a call from a thread being joined deadlocks. Call them from module, transport or host threads only. start and restart may rebuild (and free) a service. While sig_mgmt is in use, calling them directly can free a service that a mgmt reader is walking: go through sig_mgmt_start_service()/_restart_service().

sig_runtime_service_stop

sig_status_t sig_runtime_service_stop(sig_runtime * rt, const char * name)
ParameterType
rtsig_runtime *
nameconst char *

shared with sig_runtime_service_start

Service control. The only supported way for modules and transports to start/stop a project service at runtime: calls are serialized by one mutex and rejected with SIG_ERR_STATE unless the runtime is RUNNING, so they cannot race sig_runtime_stop(). SIG_ERR_NOT_FOUND for an unknown service; start on an already-started service is SIG_ERR_EXISTS; stop on a stopped service is SIG_OK; restart is stop-then-start under one hold of the mutex. Each blocks until the service's hooks have run (see sig_service.h). start and restart first rebuild a stopped service whose definition (or a capsule configuration it uses) was edited since it was built; see sig_project_refresh_service(). Do NOT call sig_runtime_service_* (or sig_runtime_uptime_seconds) from a service or capsule thread: the control mutex is held across service start/stop joins, so a call from a thread being joined deadlocks. Call them from module, transport or host threads only. start and restart may rebuild (and free) a service. While sig_mgmt is in use, calling them directly can free a service that a mgmt reader is walking: go through sig_mgmt_start_service()/_restart_service().

sig_runtime_service_restart

sig_status_t sig_runtime_service_restart(sig_runtime * rt, const char * name)
ParameterType
rtsig_runtime *
nameconst char *

shared with sig_runtime_service_start

Service control. The only supported way for modules and transports to start/stop a project service at runtime: calls are serialized by one mutex and rejected with SIG_ERR_STATE unless the runtime is RUNNING, so they cannot race sig_runtime_stop(). SIG_ERR_NOT_FOUND for an unknown service; start on an already-started service is SIG_ERR_EXISTS; stop on a stopped service is SIG_OK; restart is stop-then-start under one hold of the mutex. Each blocks until the service's hooks have run (see sig_service.h). start and restart first rebuild a stopped service whose definition (or a capsule configuration it uses) was edited since it was built; see sig_project_refresh_service(). Do NOT call sig_runtime_service_* (or sig_runtime_uptime_seconds) from a service or capsule thread: the control mutex is held across service start/stop joins, so a call from a thread being joined deadlocks. Call them from module, transport or host threads only. start and restart may rebuild (and free) a service. While sig_mgmt is in use, calling them directly can free a service that a mgmt reader is walking: go through sig_mgmt_start_service()/_restart_service().

sig_runtime_service_start_prebuilt

sig_status_t sig_runtime_service_start_prebuilt(sig_runtime * rt, const char * name)
ParameterType
rtsig_runtime *
nameconst char *

Like sig_runtime_service_start(), but never rebuilds: if the service is stopped and its definition (or a capsule configuration it uses) was edited since it was built, it returns SIG_ERR_STALE without starting anything. The caller rebuilds (sig_project_refresh_service(), inside sig_runtime_edit()) and tries again. The check and the start are atomic under the control mutex, so a caller can make sure the rebuild, which frees the old live service, only ever runs inside its own exclusion. Other results as for start.

sig_runtime_set_tap

sig_status_t sig_runtime_set_tap(sig_runtime * rt, sig_tap_fn fn, void * ctx)
ParameterType
rtsig_runtime *
fnsig_tap_fn
ctxvoid *

Installs a tap (see sig_tap.h) on every service of the running project, and on every service configured later (a rebuild after an edit keeps it). A NULL fn clears it everywhere. The tap is forgotten when the runtime stops. ctx must outlive the tap. SIG_ERR_STATE unless RUNNING. Same thread restriction as sig_runtime_service_*().

sig_runtime_find_terminal

sig_status_t sig_runtime_find_terminal(sig_runtime * rt, const char * service, const char * instance, const char * terminal, sig_tap_direction * out_dir)
ParameterType
rtsig_runtime *
serviceconst char *
instanceconst char *
terminalconst char *
out_dirsig_tap_direction *

Looks up `terminal` on capsule instance `instance` of service `service` and reports whether it is an input (SIG_TAP_INPUT) or an output (SIG_TAP_OUTPUT) terminal; an input wins if a capsule names both. Runs under the service-control lock, so it is serialized with edits and service rebuilds (see sig_runtime_edit()). SIG_ERR_NOT_FOUND if any of the three is absent, SIG_ERR_STATE unless RUNNING. Same thread restriction as sig_runtime_service_*().

sig_runtime_uptime_seconds

double sig_runtime_uptime_seconds(sig_runtime * rt)
ParameterType
rtsig_runtime *

Seconds since the last successful-or-in-progress start; 0.0 when the runtime is not running. Takes the control mutex: see the thread restriction above.

sig_runtime_version

const char * sig_runtime_version()

sig_runtime_dump_info

void sig_runtime_dump_info(sig_runtime * rt, FILE * out)
ParameterType
rtsig_runtime *
outFILE *

Writes a human-readable, multi-line snapshot to `out`: version and uptime, the compiled-in capsule types, the project's services (with running/stopped state) and their capsule instances, the registered runtime modules, and allocator statistics (plus the top allocation sites in a SIG_DEBUG build). Works in any runtime state; the service section is only available while RUNNING. NULL rt or out is a no-op. Takes the control mutex, so the thread restriction above applies: call it from host, module or transport threads, not a service thread.