1#define _POSIX_C_SOURCE 200809L
7#define _DARWIN_C_SOURCE
28#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
40#elif defined(SIGRTMAX)
41#define NSIG (SIGRTMAX + 1)
48static pthread_t g_sync_thread = 0;
49static _Atomic
bool g_running =
false;
50static pthread_mutex_t g_nuke_lock = PTHREAD_MUTEX_INITIALIZER;
51static sigset_t g_sigset;
53#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
55static inline int bsd_sigtimedwait(
const sigset_t *set, siginfo_t *info,
56 const struct timespec *timeout) {
62 struct kevent changes[
NSIG];
65 for (
int sig = 1; sig <
NSIG; sig++) {
66 if (sigismember(set, sig)) {
67 EV_SET(&changes[nchanges], sig, EVFILT_SIGNAL, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, 0,
80 int ret = kevent(kq, changes, nchanges, &event, 1, timeout);
82 int saved_errno = errno;
95 int sig = (int)event.ident;
98 memset(info, 0,
sizeof(siginfo_t));
111static uint64_t nuke_estimate_required_ram(
const char *disk_path) {
113 uint64_t db_size = 0;
114 if (disk_path && stat(disk_path, &st) == 0 && st.st_size > 0) {
115 db_size = (uint64_t)st.st_size;
118 uint64_t required = (db_size * 2) +
CWIST_MIB(32);
130static bool nuke_disk_has_tables(sqlite3 *db) {
131 if (!db)
return true;
132 sqlite3_stmt *stmt = NULL;
133 bool has_tables =
true;
134 if (sqlite3_prepare_v2(db,
"SELECT count(*) FROM sqlite_master WHERE type='table';", -1, &stmt,
135 NULL) == SQLITE_OK) {
136 if (sqlite3_step(stmt) == SQLITE_ROW) {
137 has_tables = sqlite3_column_int(stmt, 0) > 0;
140 if (stmt) sqlite3_finalize(stmt);
149static bool nuke_integrity_ok(sqlite3 *db) {
150 if (!db)
return false;
151 sqlite3_stmt *stmt = NULL;
152 bool healthy =
false;
153 if (sqlite3_prepare_v2(db,
"PRAGMA integrity_check;", -1, &stmt, NULL) == SQLITE_OK) {
154 if (sqlite3_step(stmt) == SQLITE_ROW) {
155 const unsigned char *state = sqlite3_column_text(stmt, 0);
156 healthy = (state && strcmp((
const char *)state,
"ok") == 0);
159 if (stmt) sqlite3_finalize(stmt);
170static int nuke_backup(sqlite3 *dest, sqlite3 *source) {
171 if (!dest || !source)
return -1;
172 sqlite3_backup *backup = sqlite3_backup_init(dest,
"main", source,
"main");
174 fprintf(stderr,
"[NukeDB] sqlite3_backup_init failed: %s\n", sqlite3_errmsg(dest));
178 int rc = sqlite3_backup_step(backup, -1);
179 if (rc != SQLITE_DONE) {
180 fprintf(stderr,
"[NukeDB] Backup step failed: %d (%s)\n", rc, sqlite3_errmsg(dest));
183 sqlite3_backup_finish(backup);
184 return (rc == SQLITE_DONE) ? 0 : -1;
190static void nuke_switch_to_disk(
void) {
193 printf(
"[NukeDB] CRITICAL: Low RAM detected (%zu bytes). Switching to Disk DB.\n",
196 pthread_mutex_lock(&g_nuke_lock);
202 sqlite3_db_release_memory(g_nuke.
mem_db);
206 pthread_mutex_unlock(&g_nuke_lock);
215static int nuke_commit_hook(
void *arg) {
217 if (g_running && !g_nuke.
is_disk_mode && g_sync_thread != 0) {
219 pthread_kill(g_sync_thread, SIGUSR2);
229 pthread_mutex_lock(&g_nuke_lock);
233 sqlite3_wal_checkpoint_v2(g_nuke.
disk_db,
"main", SQLITE_CHECKPOINT_PASSIVE, NULL,
235 pthread_mutex_unlock(&g_nuke_lock);
238 pthread_mutex_unlock(&g_nuke_lock);
244 pthread_mutex_unlock(&g_nuke_lock);
249 pthread_mutex_unlock(&g_nuke_lock);
254 pthread_mutex_unlock(&g_nuke_lock);
262static void nuke_cleanup_internal(
void) {
263 pthread_mutex_lock(&g_nuke_lock);
267 sqlite3_close(g_nuke.
mem_db);
287 pthread_mutex_unlock(&g_nuke_lock);
294 if (!g_running)
return;
298 if (g_sync_thread != 0) {
299 pthread_kill(g_sync_thread, SIGUSR1);
300 pthread_join(g_sync_thread, NULL);
305 nuke_cleanup_internal();
313static void *sync_thread_func(
void *arg) {
315 struct timespec timeout;
323#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
324#define sigtimedwait bsd_sigtimedwait
327 signum = sigtimedwait(&g_sigset, NULL, &timeout);
330 if (signum == SIGUSR1) {
335 if (signum == SIGUSR2) {
342 printf(
"\n[NukeDB] Intercepted Signal %d. Saving data...\n", signum);
345 nuke_cleanup_internal();
351 sigaddset(&s, signum);
352 pthread_sigmask(SIG_UNBLOCK, &s, NULL);
355 signal(signum, SIG_DFL);
359 if (errno == EAGAIN && g_running && g_nuke.
auto_sync) {
361 nuke_switch_to_disk();
381 uint64_t required_ram = nuke_estimate_required_ram(disk_path);
383 if (available_ram > 0 && available_ram < required_ram) {
385 "[NukeDB] Insufficient RAM for in-memory mode (have %zu bytes, need ~%zu bytes).\n",
386 (
size_t)available_ram, (
size_t)required_ram);
395 g_nuke.
auto_sync = (sync_interval_ms > 0);
400 int rc = sqlite3_open(disk_path, &g_nuke.
disk_db);
401 if (rc != SQLITE_OK) {
402 fprintf(stderr,
"[NukeDB] Failed to open disk DB: %s\n", sqlite3_errmsg(g_nuke.
disk_db));
403 nuke_cleanup_internal();
407 if (!nuke_integrity_ok(g_nuke.
disk_db)) {
408 fprintf(stderr,
"[NukeDB] Integrity check failed for '%s'. Aborting in-memory mode.\n",
410 nuke_cleanup_internal();
414 sqlite3_exec(g_nuke.
disk_db,
"PRAGMA journal_mode=WAL;", NULL, NULL, NULL);
415 sqlite3_exec(g_nuke.
disk_db,
"PRAGMA synchronous=NORMAL;", NULL, NULL, NULL);
418 rc = sqlite3_open(
":memory:", &g_nuke.
mem_db);
419 if (rc != SQLITE_OK) {
420 fprintf(stderr,
"[NukeDB] Failed to open memory DB: %s\n", sqlite3_errmsg(g_nuke.
mem_db));
421 nuke_cleanup_internal();
426 pthread_mutex_lock(&g_nuke_lock);
430 if (backup_rc == 0) {
432 }
else if (!nuke_disk_has_tables(g_nuke.
disk_db)) {
436 "[NukeDB] Initial disk->memory load failed. Falling back to disk-only mode.\n");
438 sqlite3_close(g_nuke.
mem_db);
441 pthread_mutex_unlock(&g_nuke_lock);
445 sqlite3_commit_hook(g_nuke.
mem_db, nuke_commit_hook, NULL);
451 sigemptyset(&g_sigset);
452 sigaddset(&g_sigset, SIGINT);
453 sigaddset(&g_sigset, SIGTERM);
454 sigaddset(&g_sigset, SIGUSR1);
455 sigaddset(&g_sigset, SIGUSR2);
457 pthread_sigmask(SIG_BLOCK, &g_sigset, NULL);
460 if (pthread_create(&g_sync_thread, NULL, sync_thread_func, NULL) != 0) {
462 pthread_sigmask(SIG_UNBLOCK, &g_sigset, NULL);
463 nuke_cleanup_internal();
498 if (!out_size)
return NULL;
501 pthread_mutex_lock(&g_nuke_lock);
504 pthread_mutex_unlock(&g_nuke_lock);
508 unsigned char *buf = sqlite3_serialize(src,
"main", out_size, 0);
509 pthread_mutex_unlock(&g_nuke_lock);
521 if (!data || data_len <= 0)
return -1;
523 pthread_mutex_lock(&g_nuke_lock);
526 pthread_mutex_unlock(&g_nuke_lock);
533 int rc = sqlite3_deserialize(g_nuke.
mem_db,
"main", data, data_len, data_len,
534 SQLITE_DESERIALIZE_FREEONCLOSE | SQLITE_DESERIALIZE_RESIZEABLE);
535 pthread_mutex_unlock(&g_nuke_lock);
536 return (rc == SQLITE_OK) ? 0 : -1;
Memory allocation wrappers backed by libttak.
void cwist_free(void *ptr)
Free memory obtained via cwist_alloc/cwist_strdup/etc.
char * cwist_strdup(const char *src)
Duplicate a string using libttak-backed storage.
Convenience size-conversion and utility macros shared across CWIST.
#define CWIST_UNUSED(x)
Mark an intentionally unused variable to silence compiler warnings.
#define CWIST_MIB(x)
Convert binary mebibytes to bytes.
int cwist_nuke_deserialize(unsigned char *data, sqlite3_int64 data_len)
Replace the in-memory NUKE database with serialized contents.
sqlite3 * cwist_nuke_get_db(void)
Return the SQLite handle that callers should use for the NUKE database.
void cwist_nuke_close(void)
Stop the sync thread, flush state, and tear down the global NUKE context.
int cwist_nuke_sync(void)
Persist the current in-memory database back to disk or checkpoint WAL mode.
int cwist_nuke_init(const char *disk_path, int sync_interval_ms)
Initialize the global NUKE database mirror around one disk database.
void cwist_nuke_signal_handler(int signum)
Placeholder signal handler (no-op).
unsigned char * cwist_nuke_serialize(sqlite3_int64 *out_size)
Serialize the current NUKE database contents into a buffer.
High-Performance Read-Optimal Persistent Store using SQLite.
#define CWIST_NUKE_ERR_LOW_MEMORY
#define CWIST_NUKE_ERR_GENERIC
#define CWIST_NUKE_OK
Return codes for cwist_nuke_init.
sqlite3 * disk_db
The backup disk database handle.
char * disk_path
Path to the disk database file.
bool is_disk_mode
True if running in low-memory disk fallback mode.
bool load_successful
True if initial load from disk was successful.
bool auto_sync
Whether auto-sync is enabled.
sqlite3 * mem_db
The active in-memory database handle.
int sync_interval_ms
Sync interval in milliseconds.
System Resource Monitoring Utilities.
bool cwist_is_ram_critical(uint64_t threshold_bytes)
Check if system RAM is critically low.
uint64_t cwist_get_available_ram(void)
Get the available (free + reclaimable) system RAM in bytes.