xtc_cfg(3)
---xtc_cfg(3)
typed runtime configuration registry
| XTC_CFG(3) | Library Functions Manual | XTC_CFG(3) |
NAME
xtc_cfg_register,
xtc_cfg_unregister,
xtc_cfg_get_bool,
xtc_cfg_get_int,
xtc_cfg_get_int64,
xtc_cfg_get_double,
xtc_cfg_get_string,
xtc_cfg_get_string_copy,
xtc_cfg_get_enum,
xtc_cfg_set_bool,
xtc_cfg_set_int,
xtc_cfg_set_int64,
xtc_cfg_set_double,
xtc_cfg_set_string,
xtc_cfg_set_enum,
xtc_cfg_count, xtc_cfg_kind,
xtc_cfg_load_file,
xtc_cfg_reload, xtc_cfg_ref,
xtc_cfg_ref_get_bool,
xtc_cfg_ref_get_int,
xtc_cfg_ref_get_int64,
xtc_cfg_ref_get_double,
xtc_cfg_ref_get_string,
xtc_cfg_ref_get_enum,
xtc_cfg_session_create,
xtc_cfg_session_destroy,
xtc_cfg_session_bind,
xtc_cfg_session_current,
xtc_cfg_session_push,
xtc_cfg_session_commit,
xtc_cfg_session_abort,
xtc_cfg_session_source,
xtc_cfg_session_reset,
xtc_cfg_ssn_set_bool,
xtc_cfg_ssn_set_int,
xtc_cfg_ssn_set_int64,
xtc_cfg_ssn_set_double,
xtc_cfg_ssn_set_string,
xtc_cfg_ssn_set_enum — typed
runtime configuration registry
SYNOPSIS
#include <xtc.h>
#include <xtc_cfg.h>
int
xtc_cfg_register(const
xtc_cfg_spec_t *spec);
int
xtc_cfg_unregister(const
char *name);
int
xtc_cfg_get_bool(const
char *name, int
*out);
int
xtc_cfg_get_int(const
char *name, int
*out);
int
xtc_cfg_get_int64(const
char *name, int64_t
*out);
int
xtc_cfg_get_double(const
char *name, double
*out);
int
xtc_cfg_get_string(const
char *name, const char
**out);
int
xtc_cfg_get_string_copy(const
char *name, char
**out);
int
xtc_cfg_get_enum(const
char *name, int
*out);
int
xtc_cfg_set_bool(const
char *name, int
v);
int
xtc_cfg_set_int(const
char *name, int
v);
int
xtc_cfg_set_int64(const
char *name, int64_t
v);
int
xtc_cfg_set_double(const
char *name, double
v);
int
xtc_cfg_set_string(const
char *name, const char
*v);
int
xtc_cfg_set_enum(const
char *name, int
v);
int
xtc_cfg_count(void);
int
xtc_cfg_kind(const
char *name,
xtc_cfg_kind_t *out);
int
xtc_cfg_load_file(const
char *path);
int
xtc_cfg_reload(void);
int
xtc_cfg_ref(const
char *name, xtc_cfg_ref_t
*out);
int
xtc_cfg_ref_get_bool(xtc_cfg_ref_t
ref, int *out);
int
xtc_cfg_ref_get_int(xtc_cfg_ref_t
ref, int *out);
int
xtc_cfg_ref_get_int64(xtc_cfg_ref_t
ref, int64_t
*out);
int
xtc_cfg_ref_get_double(xtc_cfg_ref_t
ref, double
*out);
int
xtc_cfg_ref_get_string(xtc_cfg_ref_t
ref, const char
**out);
int
xtc_cfg_ref_get_enum(xtc_cfg_ref_t
ref, int *out);
int
xtc_cfg_session_create(xtc_cfg_session_t
**out);
void
xtc_cfg_session_destroy(xtc_cfg_session_t
*s);
xtc_cfg_session_t *
xtc_cfg_session_bind(xtc_cfg_session_t
*s);
xtc_cfg_session_t *
xtc_cfg_session_current(void);
int
xtc_cfg_session_push(xtc_cfg_session_t
*s);
int
xtc_cfg_session_commit(xtc_cfg_session_t
*s);
int
xtc_cfg_session_abort(xtc_cfg_session_t
*s);
int
xtc_cfg_session_source(xtc_cfg_session_t
*s, const char
*name, xtc_cfg_source_t
*out);
int
xtc_cfg_session_reset(xtc_cfg_session_t
*s, const char
*name);
int
xtc_cfg_ssn_set_bool(xtc_cfg_session_t
*s, const char
*name, int v,
xtc_cfg_source_t
src);
int
xtc_cfg_ssn_set_int(xtc_cfg_session_t
*s, const char
*name, int v,
xtc_cfg_source_t
src);
int
xtc_cfg_ssn_set_int64(xtc_cfg_session_t
*s, const char
*name, int64_t v,
xtc_cfg_source_t
src);
int
xtc_cfg_ssn_set_double(xtc_cfg_session_t
*s, const char
*name, double v,
xtc_cfg_source_t
src);
int
xtc_cfg_ssn_set_string(xtc_cfg_session_t
*s, const char
*name, const char
*v, xtc_cfg_source_t
src);
int
xtc_cfg_ssn_set_enum(xtc_cfg_session_t
*s, const char
*name, int v,
xtc_cfg_source_t
src);
DESCRIPTION
xtc_cfg is a typed runtime configuration
registry, modeled on PostgreSQL's GUC system. Modules register named knobs
at startup; operators set them by name; values are validated against type
and bounds.
Supported types: bool,
int, int64,
double, string,
enum (xtc_cfg_kind_t).
There is ONE registration entry point,
xtc_cfg_register(),
taking a xtc_cfg_spec_t:
- name, the canonical name (dot-separated, e.g. ‘rexis.max_clients’).
- short_desc, an ops-friendly description.
- kind, one of the
XTC_CFG_*kinds. - dflt, a union holding the default for that kind.
- min_int / max_int
and min_double / max_double,
inclusive bounds for the numeric kinds (
0 /0 means unbounded). - enum_labels /
n_enum_labels for
XTC_CFG_ENUM: the allowed labels, the int value being the index into them. - validator, on_change, and cb_user, optional callbacks.
Storage
is internal to the registry. The spec carries NO backing-storage
pointer: a registered variable lives inside the registry and is read back
through the accessors below, not through a caller-supplied variable that the
registry writes. A value set with
xtc_cfg_set_int()
is observed with
xtc_cfg_get_int()
or, on a hot path, through an xtc_cfg_ref_t.
xtc_cfg_unregister()
removes a knob by name.
There is no string-parsing single setter:
the typed setters
xtc_cfg_set_bool(),
xtc_cfg_set_int(),
xtc_cfg_set_int64(),
xtc_cfg_set_double(),
xtc_cfg_set_string(), and
xtc_cfg_set_enum()
assign a value of the matching C type, validating it against the registered
bounds and validator and firing the on-change callback. String parsing
happens only in xtc_cfg_load_file(), which reads
‘name = value’ lines; xtc_cfg_reload()
re-reads the last loaded file.
Read a value with
xtc_cfg_get_bool(),
xtc_cfg_get_int(),
xtc_cfg_get_int64(),
xtc_cfg_get_double(),
xtc_cfg_get_string(), or
xtc_cfg_get_enum()
for the corresponding registered kind.
String
lifetime.
xtc_cfg_get_string()
and
xtc_cfg_ref_get_string()
lend the registry's (or the bound session's) own buffer, not a copy. It is
valid only until the next set of that key:
xtc_cfg_set_string(),
a session set, commit, reset, abort or destroy for a session value,
xtc_cfg_load_file()
or
xtc_cfg_reload(),
and xtc_cfg_unregister() all free it, and another
thread may do so at any moment. Read the borrowed pointer only where no
concurrent set is possible. Otherwise use
xtc_cfg_get_string_copy(),
which duplicates the value under the registry lock and stores a private copy
in *out that the caller releases with
xtc_free();
a registered string whose value is NULL copies as
NULL. It returns the same codes as
xtc_cfg_get_string(), plus
XTC_E_NOMEM if the copy cannot be allocated.
xtc_cfg_count()
returns the number of registered knobs;
xtc_cfg_kind()
reports the xtc_cfg_kind_t of a named knob.
For a knob read on a hot path, the name-keyed
getters are too slow: each does a registry lookup.
xtc_cfg_ref()
resolves a name to an opaque, pointer-stable
xtc_cfg_ref_t handle once; thereafter
xtc_cfg_ref_get_bool(),
xtc_cfg_ref_get_int(),
xtc_cfg_ref_get_int64(),
xtc_cfg_ref_get_double(),
xtc_cfg_ref_get_string(), and
xtc_cfg_ref_get_enum()
read the current value with no name lookup, still observing live
xtc_cfg_set_*()
updates. The handle stays valid until the variable is unregistered, so it
can be cached for the process lifetime like a compiled-in pointer.
Per-session scoping
A single global value per name is what a database server cannot
use for most settings. A session layers per-variable OVERRIDES over the
global registry: a read resolves the session override first, then the global
value, and a session set never touches the global value.
xtc_cfg_session_create()
and
xtc_cfg_session_destroy()
manage a session;
xtc_cfg_session_bind()
makes one the current session for the calling fiber (returning the previous
binding) so the unscoped getters resolve through it, and
xtc_cfg_session_current()
reports the bound session. The binding is per-FIBER, not per-thread: it is
keyed on the calling fiber's identity, so two fibers on one loop can each
bind their own session, and a work-stolen fiber that resumes on a different
thread keeps its binding. Off a fiber (a plain OS thread) it is per-thread.
Within a session the overrides form a transactional stack of levels:
xtc_cfg_session_push()
opens a level,
xtc_cfg_session_commit()
folds it into its parent (the values survive), and
xtc_cfg_session_abort()
discards it -- backing SET LOCAL, SET inside a rolled-back transaction,
function-local SET, and subtransactions.
xtc_cfg_ssn_set_bool(),
xtc_cfg_ssn_set_int(),
xtc_cfg_ssn_set_int64(),
xtc_cfg_ssn_set_double(),
xtc_cfg_ssn_set_string(),
and
xtc_cfg_ssn_set_enum()
set a per-session override at the current level, each carrying a source rank
(XTC_CFG_SRC_DEFAULT through
XTC_CFG_SRC_OVERRIDE); a lower-ranked source cannot
replace a higher one, and
xtc_cfg_session_source()
reports the winner.
xtc_cfg_session_reset()
drops the current level's override for a name (RESET), so the parent level
or the global value shows through again.
Unregistering a variable that a live session still overrides is safe in EITHER order: the entry's storage is refcounted and outlives the unregister until the last override naming it is torn down, while being immediately invisible to every lookup.
RETURN VALUES
Every function returning int returns
XTC_OK on success and a negative
XTC_E_* code on failure:
XTC_E_INVAL for a NULL argument, a kind mismatch, a
validator rejection, a duplicate registration, or an unregistered name
passed to a name-keyed getter, setter,
xtc_cfg_kind() or
xtc_cfg_unregister();
XTC_E_RANGE for a value outside the registered
bounds (or outside 0..1 for a bool,
or not a label index for an enum), from the global and the
per-session setters alike; XTC_E_NOTFOUND for an
unregistered name passed to xtc_cfg_ref(),
xtc_cfg_session_source() or
xtc_cfg_session_reset();
XTC_E_NOMEM on allocation failure. There is no
distinct code for a duplicate registration; an earlier version of this page
named one that does not exist in
<xtc.h>.
xtc_cfg_session_source() reports
XTC_CFG_SRC_DEFAULT for a name the session does not
override. xtc_cfg_count() returns the count.
xtc_cfg_session_bind() and
xtc_cfg_session_current() return a session pointer
or NULL.
xtc_cfg_session_destroy() returns nothing and is
NULL-safe.
Releases before 1.50 returned XTC_E_INVAL
from the per-session setters (xtc_cfg_ssn_set_*())
for an out-of-bounds value, while the global setters already returned
XTC_E_RANGE; both now return
XTC_E_RANGE.
A value from a configuration file is parsed WIDE and then
range-checked against the target C type, so ‘4294967297’ for
an XTC_CFG_INT knob is rejected as invalid rather
than silently truncated to 1.
EXAMPLES
static const xtc_cfg_spec_t spec = {
.name = "rexis.max_clients",
.short_desc = "Maximum concurrent client connections",
.kind = XTC_CFG_INT,
.dflt = { .d_int = 1000 },
.min_int = 1,
.max_int = 100000,
};
int v;
if (xtc_cfg_register(&spec) != XTC_OK)
return -1;
/* Operator runtime change, then read it back. */
(void)xtc_cfg_set_int("rexis.max_clients", 5000);
(void)xtc_cfg_get_int("rexis.max_clients", &v); /* v == 5000 */
/* Hot path: resolve once, read with no name lookup. */
xtc_cfg_ref_t ref;
if (xtc_cfg_ref("rexis.max_clients", &ref) == XTC_OK)
(void)xtc_cfg_ref_get_int(ref, &v);
SEE ALSO
HISTORY
Appeared in xtc 0.1.
| September 21, 2026 | Debian |