| 537 | } poll_ctx_t; |
| 538 | |
| 539 | static void prune_missing_project(cbm_watcher_t *w, project_state_t *s) { |
| 540 | if (!w || !s || !s->project_name) { |
| 541 | return; |
| 542 | } |
| 543 | |
| 544 | char project_name[CBM_SZ_1K]; |
| 545 | snprintf(project_name, sizeof(project_name), "%s", s->project_name); |
| 546 | |
| 547 | bool removed = false; |
| 548 | cbm_mutex_lock(&w->projects_lock); |
| 549 | project_state_t *current = cbm_ht_get(w->projects, project_name); |
| 550 | /* Deferred free (same discipline as cbm_watcher_unwatch): this state |
| 551 | * is referenced by the poll_once snapshot iterating us. On OOM the |
| 552 | * watch stays registered and pruning retries on the next cycle. */ |
| 553 | if (current == s && defer_state_free(w, s)) { |
| 554 | delete_cached_project_db(project_name); |
| 555 | cbm_ht_delete(w->projects, project_name); |
| 556 | removed = true; |
| 557 | } |
| 558 | cbm_mutex_unlock(&w->projects_lock); |
| 559 | |
| 560 | if (removed) { |
| 561 | cbm_log_info("watcher.root_pruned", "project", project_name); |
| 562 | } |
| 563 | } |
| 564 | |
| 565 | static void poll_project(const char *key, void *val, void *ud) { |
| 566 | (void)key; |
no test coverage detected