Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions src/core/db/nuke_db.c
Original file line number Diff line number Diff line change
Expand Up @@ -468,15 +468,30 @@ int cwist_nuke_init(const char *disk_path, int sync_interval_ms) {
return CWIST_NUKE_OK;
}

/**
* @brief Return the SQLite handle that callers should use for the NUKE database.
* @return The in-memory handle in normal mode, or the disk handle after a
* low-memory fallback to disk mode. May be NULL before init or after close.
*/
sqlite3 *cwist_nuke_get_db(void) {
if (g_nuke.is_disk_mode) return g_nuke.disk_db;
return g_nuke.mem_db;
}

/**
* @brief Placeholder signal handler (no-op).
* @param signum Signal number; unused.
*/
void cwist_nuke_signal_handler(int signum) {
CWIST_UNUSED(signum);
}

/**
* @brief Serialize the current NUKE database contents into a buffer.
* @param out_size Receives the serialized byte count.
* @return Heap buffer produced by sqlite3_serialize (caller frees with sqlite3_free),
* or NULL when out_size is NULL or no database is open. Thread-safe via g_nuke_lock.
*/
unsigned char *cwist_nuke_serialize(sqlite3_int64 *out_size) {
if (!out_size) return NULL;
*out_size = 0;
Expand All @@ -493,6 +508,13 @@ unsigned char *cwist_nuke_serialize(sqlite3_int64 *out_size) {
return buf;
}

/**
* @brief Replace the in-memory NUKE database with serialized contents.
* @param data Serialized database buffer; ownership transfers to SQLite, which
* frees it on close (SQLITE_DESERIALIZE_FREEONCLOSE).
* @param data_len Length of @p data in bytes.
* @return 0 on success, or -1 on invalid arguments or when running in disk mode.
*/
int cwist_nuke_deserialize(unsigned char *data, sqlite3_int64 data_len) {
if (!data || data_len <= 0) return -1;

Expand Down
63 changes: 63 additions & 0 deletions src/core/db/pool.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,16 @@ struct cwist_db_pool {

static atomic_ulong pool_sequence = 1;

/** @brief Build the error reported when a pooled operation cannot acquire a connection.
* @return Error tagged @c CWIST_ERR_INT16 with code -1. The caller owns the result. */
static cwist_error_t pool_error(void) {
cwist_error_t err = make_error(CWIST_ERR_INT16);
err.error.err_i16 = -1;
return err;
}

/** @brief Pick the clock used for pool condition-variable deadlines.
* @return @c CLOCK_REALTIME on BSD/macOS (no monotonic condvar support), otherwise @c CLOCK_MONOTONIC. */
static clockid_t pool_cond_clock(void) {
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
return CLOCK_REALTIME;
Expand All @@ -37,6 +41,10 @@ static clockid_t pool_cond_clock(void) {
#endif
}

/** @brief Initialize a condition variable with the pool's deadline clock.
* @param cond Condition variable to initialize; must not be NULL.
* @retval true Initialization succeeded.
* @retval false Initialization failed; @a cond is left in an unusable state. */
static bool pool_cond_init(pthread_cond_t *cond) {
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
return pthread_cond_init(cond, NULL) == 0;
Expand All @@ -50,6 +58,9 @@ static bool pool_cond_init(pthread_cond_t *cond) {
#endif
}

/** @brief Compute an absolute deadline @a timeout_ms milliseconds from now.
* @param deadline Output absolute deadline on the pool condvar clock; must not be NULL.
* @param timeout_ms Timeout in milliseconds. */
static void pool_deadline_after_ms(struct timespec *deadline, int timeout_ms) {
clock_gettime(pool_cond_clock(), deadline);
deadline->tv_sec += timeout_ms / 1000;
Expand All @@ -60,6 +71,11 @@ static void pool_deadline_after_ms(struct timespec *deadline, int timeout_ms) {
}
}

/** @brief Derive the SQLite open path for a pooled database.
* @param path Requested database path.
* @return Newly allocated open path (caller frees), or NULL on allocation failure. The
* ":memory:" pseudo-path is rewritten to a unique shared-cache URI so every pooled
* connection sees one in-memory database. */
static char *pool_open_path(const char *path) {
if (strcmp(path, ":memory:") != 0) return cwist_strdup(path);
unsigned long seq = atomic_fetch_add_explicit(&pool_sequence, 1, memory_order_relaxed);
Expand All @@ -69,6 +85,11 @@ static char *pool_open_path(const char *path) {
return written > 0 && (size_t)written < sizeof(buffer) ? cwist_strdup(buffer) : NULL;
}

/** @brief Create a pool of @a max_conns SQLite connections to @a path.
* @param path Database path, or ":memory:" for a shared in-memory database.
* @param max_conns Maximum number of pooled connections; must be > 0.
* @return New pool on success, or NULL on invalid arguments or any allocation/open failure.
* On success all connections start idle. Thread-safe. */
cwist_db_pool_t *cwist_db_pool_create(const char *path, size_t max_conns) {
if (!path || max_conns == 0) return NULL;
cwist_db_pool_t *pool = cwist_alloc(sizeof(*pool));
Expand Down Expand Up @@ -118,6 +139,9 @@ cwist_db_pool_t *cwist_db_pool_create(const char *path, size_t max_conns) {
return NULL;
}

/** @brief Take an idle connection from the pool; caller must hold @c pool->mtx.
* @param pool Pool to lease from.
* @return Leased connection, or NULL if the pool is closing or has no idle connections. */
static cwist_db *pool_acquire_locked(cwist_db_pool_t *pool) {
if (pool->closing || pool->idle_count == 0) return NULL;
size_t slot = pool->idle_slots[--pool->idle_count];
Expand All @@ -126,6 +150,10 @@ static cwist_db *pool_acquire_locked(cwist_db_pool_t *pool) {
return pool->conns[slot];
}

/** @brief Lease a pooled connection, blocking indefinitely until one is available.
* @param pool Pool to lease from; must not be NULL.
* @return Leased connection to pass to cwist_db_pool_release(), or NULL if the pool is NULL or closing.
* Thread-safe; the caller must not use the connection after releasing it. */
cwist_db *cwist_db_pool_acquire(cwist_db_pool_t *pool) {
if (!pool) return NULL;
pthread_mutex_lock(&pool->mtx);
Expand All @@ -135,6 +163,11 @@ cwist_db *cwist_db_pool_acquire(cwist_db_pool_t *pool) {
return db;
}

/** @brief Lease a pooled connection, waiting at most @a timeout_ms.
* @param pool Pool to lease from; must not be NULL.
* @param timeout_ms Maximum wait in milliseconds; must be >= 0.
* @return Leased connection to pass to cwist_db_pool_release(), or NULL on invalid arguments,
* on timeout, or if the pool is closing. Thread-safe. */
cwist_db *cwist_db_pool_acquire_timeout(cwist_db_pool_t *pool, int timeout_ms) {
if (!pool || timeout_ms < 0) return NULL;
struct timespec deadline;
Expand All @@ -148,6 +181,11 @@ cwist_db *cwist_db_pool_acquire_timeout(cwist_db_pool_t *pool, int timeout_ms) {
return db;
}

/** @brief Return a leased connection to the pool and wake one waiting acquirer.
* @param pool Pool that owns the connection.
* @param conn Connection previously returned by an acquire call; must belong to @a pool
* and still be leased. Invalid or already-released connections are ignored.
* Thread-safe. */
void cwist_db_pool_release(cwist_db_pool_t *pool, cwist_db *conn) {
if (!pool || !conn) return;
pthread_mutex_lock(&pool->mtx);
Expand All @@ -162,6 +200,9 @@ void cwist_db_pool_release(cwist_db_pool_t *pool, cwist_db *conn) {
pthread_mutex_unlock(&pool->mtx);
}

/** @brief Count connections currently leased from the pool.
* @param pool Pool to inspect.
* @return Number of connections in use, or 0 if @a pool is NULL. Thread-safe. */
size_t cwist_db_pool_in_use(cwist_db_pool_t *pool) {
if (!pool) return 0;
pthread_mutex_lock(&pool->mtx);
Expand All @@ -170,6 +211,14 @@ size_t cwist_db_pool_in_use(cwist_db_pool_t *pool) {
return count;
}

/** @brief Close a pool, waiting up to @a timeout_ms for leased connections.
* @param pool Pool to destroy; must not be NULL.
* @param timeout_ms Wait limit in milliseconds, or -1 to wait indefinitely for all
* connections to be released.
* @retval true All connections were released and the pool (and every connection) was closed and freed.
* @retval false The wait timed out with connections still leased; the pool is left alive and closing,
* so further acquires fail and the caller must retry the destroy.
* Thread-safe. */
bool cwist_db_pool_destroy_timeout(cwist_db_pool_t *pool, int timeout_ms) {
if (!pool || timeout_ms < -1) return false;
pthread_mutex_lock(&pool->mtx);
Expand Down Expand Up @@ -201,10 +250,18 @@ bool cwist_db_pool_destroy_timeout(cwist_db_pool_t *pool, int timeout_ms) {
return true;
}

/** @brief Close a pool, blocking until all leased connections are released.
* @param pool Pool to destroy; no-op if NULL. Equivalent to
* cwist_db_pool_destroy_timeout(pool, -1). */
void cwist_db_pool_destroy(cwist_db_pool_t *pool) {
(void)cwist_db_pool_destroy_timeout(pool, -1);
}

/** @brief Execute a SQL statement on a leased pooled connection.
* @param pool Pool to run the statement on.
* @param sql SQL statement to execute.
* @return Execution result, or an INT16 error with code -1 if no connection could be acquired.
* The caller owns the result. */
cwist_error_t cwist_db_pool_exec(cwist_db_pool_t *pool, const char *sql) {
cwist_db *db = cwist_db_pool_acquire(pool);
if (!db) return pool_error();
Expand All @@ -213,6 +270,12 @@ cwist_error_t cwist_db_pool_exec(cwist_db_pool_t *pool, const char *sql) {
return err;
}

/** @brief Run a query on a leased pooled connection.
* @param pool Pool to run the query on.
* @param sql SQL query to execute.
* @param result Output JSON result written by the query; caller owns and must free it.
* @return Query result, or an INT16 error with code -1 if no connection could be acquired.
* The connection is released back to the pool before returning. */
cwist_error_t cwist_db_pool_query(cwist_db_pool_t *pool, const char *sql, cJSON **result) {
cwist_db *db = cwist_db_pool_acquire(pool);
if (!db) return pool_error();
Expand Down
30 changes: 30 additions & 0 deletions src/core/html/component.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ struct cwist_html_component {
cwist_html_component_render_fn render_fn;
};

/**
* @brief Create a named component wrapping a render function.
* @param name Non-empty component name; copied into the new component.
* @param render_fn Render function invoked by cwist_html_component_instantiate().
* @return New component on success, NULL if arguments are invalid or
* allocation fails. Caller owns the returned component and must
* release it with cwist_html_component_destroy().
*/
cwist_html_component_t *cwist_html_component_create(const char *name,
cwist_html_component_render_fn render_fn) {
if (!name || !*name || !render_fn) return NULL;
Expand All @@ -28,16 +36,38 @@ cwist_html_component_t *cwist_html_component_create(const char *name,
return comp;
}

/**
* @brief Destroy a component and free its name.
* @param comp Component to destroy; NULL is accepted and does nothing.
* Must not be used after this call.
*/
void cwist_html_component_destroy(cwist_html_component_t *comp) {
if (!comp) return;
cwist_free(comp->name);
cwist_free(comp);
}

/**
* @brief Get the component's name.
* @param comp Component to query; may be NULL.
* @return The component's name, or NULL if @p comp is NULL.
*/
const char *cwist_html_component_name(const cwist_html_component_t *comp) {
return comp ? comp->name : NULL;
}

/**
* @brief Run the component's render function to produce an element tree.
* @param comp Component whose render function is invoked; may be NULL.
* @param props Opaque props forwarded to the render function.
* @param children Child elements forwarded to the render function. When
* @p comp is NULL they are destroyed here instead, since no render
* function takes ownership of them.
* @param child_count Number of entries in @p children.
* @return Rendered element tree, or NULL if arguments are invalid or the
* render function fails. Ownership follows the render function's
* contract.
*/
cwist_html_element_t *cwist_html_component_instantiate(cwist_html_component_t *comp,
const void *props,
cwist_html_element_t **children,
Expand Down
Loading
Loading