|
33 | 33 | #include <sys/stat.h> |
34 | 34 | #include <time.h> |
35 | 35 |
|
| 36 | +/* ── Global index lock ─────────────────────────────────────────── */ |
| 37 | +/* Prevents concurrent pipeline runs on the same DB file. |
| 38 | + * Atomic spinlock: 0 = free, 1 = locked. */ |
| 39 | +static atomic_int g_pipeline_busy = 0; |
| 40 | + |
| 41 | +bool cbm_pipeline_try_lock(void) { |
| 42 | + return atomic_exchange(&g_pipeline_busy, 1) == 0; |
| 43 | +} |
| 44 | + |
| 45 | +#define LOCK_SPIN_NS 100000000 /* 100ms between lock retries */ |
| 46 | + |
| 47 | +void cbm_pipeline_lock(void) { |
| 48 | + while (atomic_exchange(&g_pipeline_busy, 1) != 0) { |
| 49 | + struct timespec ts = {0, LOCK_SPIN_NS}; |
| 50 | + cbm_nanosleep(&ts, NULL); |
| 51 | + } |
| 52 | +} |
| 53 | + |
| 54 | +void cbm_pipeline_unlock(void) { |
| 55 | + atomic_store(&g_pipeline_busy, 0); |
| 56 | +} |
| 57 | + |
36 | 58 | /* ── Internal state ──────────────────────────────────────────────── */ |
37 | 59 |
|
38 | 60 | struct cbm_pipeline { |
@@ -344,45 +366,44 @@ int cbm_pipeline_run(cbm_pipeline_t *p) { |
344 | 366 | return -1; |
345 | 367 | } |
346 | 368 |
|
347 | | - /* Check for existing DB with file hashes → incremental path */ |
| 369 | + /* Check for existing DB → route to incremental or delete for reindex */ |
348 | 370 | { |
349 | 371 | char *db_path = resolve_db_path(p); |
350 | 372 | if (db_path) { |
351 | 373 | struct stat db_st; |
352 | 374 | if (stat(db_path, &db_st) == 0) { |
353 | | - /* DB exists — check if it has file hashes */ |
| 375 | + /* DB exists — try incremental path first */ |
354 | 376 | cbm_store_t *check_store = cbm_store_open_path(db_path); |
355 | | - if (check_store) { |
356 | | - /* Integrity check — corrupt DB → delete and fall through to full reindex */ |
357 | | - if (!cbm_store_check_integrity(check_store)) { |
358 | | - cbm_log_error("pipeline.corrupt_db", "path", db_path, "action", |
359 | | - "deleting — will do full reindex"); |
360 | | - cbm_store_close(check_store); |
361 | | - cbm_unlink(db_path); |
362 | | - char wal[1040]; |
363 | | - char shm[1040]; |
364 | | - snprintf(wal, sizeof(wal), "%s-wal", db_path); |
365 | | - snprintf(shm, sizeof(shm), "%s-shm", db_path); |
366 | | - cbm_unlink(wal); |
367 | | - cbm_unlink(shm); |
368 | | - } else { |
369 | | - cbm_file_hash_t *hashes = NULL; |
370 | | - int hash_count = 0; |
371 | | - cbm_store_get_file_hashes(check_store, p->project_name, &hashes, |
372 | | - &hash_count); |
373 | | - cbm_store_free_file_hashes(hashes, hash_count); |
374 | | - cbm_store_close(check_store); |
375 | | - |
376 | | - if (hash_count > 0) { |
377 | | - cbm_log_info("pipeline.route", "path", "incremental", "stored_hashes", |
378 | | - itoa_buf(hash_count)); |
379 | | - rc = cbm_pipeline_run_incremental(p, db_path, files, file_count); |
380 | | - cbm_discover_free(files, file_count); |
381 | | - free(db_path); |
382 | | - return rc; |
383 | | - } |
| 377 | + if (check_store && cbm_store_check_integrity(check_store)) { |
| 378 | + cbm_file_hash_t *hashes = NULL; |
| 379 | + int hash_count = 0; |
| 380 | + cbm_store_get_file_hashes(check_store, p->project_name, &hashes, &hash_count); |
| 381 | + cbm_store_free_file_hashes(hashes, hash_count); |
| 382 | + cbm_store_close(check_store); |
| 383 | + |
| 384 | + if (hash_count > 0) { |
| 385 | + cbm_log_info("pipeline.route", "path", "incremental", "stored_hashes", |
| 386 | + itoa_buf(hash_count)); |
| 387 | + rc = cbm_pipeline_run_incremental(p, db_path, files, file_count); |
| 388 | + cbm_discover_free(files, file_count); |
| 389 | + free(db_path); |
| 390 | + return rc; |
384 | 391 | } |
| 392 | + } else if (check_store) { |
| 393 | + cbm_store_close(check_store); |
385 | 394 | } |
| 395 | + |
| 396 | + /* Not eligible for incremental → reindex: delete old DB first. |
| 397 | + * cbm_write_db writes directly to the final path (no rename), |
| 398 | + * so the old file must be gone before the pipeline dumps. */ |
| 399 | + cbm_log_info("pipeline.route", "path", "reindex", "action", "deleting old db"); |
| 400 | + cbm_unlink(db_path); |
| 401 | + char wal[1040]; |
| 402 | + char shm[1040]; |
| 403 | + snprintf(wal, sizeof(wal), "%s-wal", db_path); |
| 404 | + snprintf(shm, sizeof(shm), "%s-shm", db_path); |
| 405 | + cbm_unlink(wal); |
| 406 | + cbm_unlink(shm); |
386 | 407 | } |
387 | 408 | free(db_path); |
388 | 409 | } |
|
0 commit comments