| 1006 | } |
| 1007 | |
| 1008 | void cbm_watcher_free(cbm_watcher_t *w) { |
| 1009 | if (!w) { |
| 1010 | return; |
| 1011 | } |
| 1012 | /* Safety net: ensure stopped is set before draining pending_free. |
| 1013 | * In production the caller should cbm_watcher_stop() + join first. */ |
| 1014 | atomic_store(&w->stopped, 1); |
| 1015 | cbm_mutex_lock(&w->coordination_lock); |
| 1016 | cbm_mutex_lock(&w->projects_lock); |
| 1017 | cbm_ht_foreach(w->projects, free_state_entry, NULL); |
| 1018 | cbm_ht_free(w->projects); |
| 1019 | for (int i = 0; i < w->pending_free_count; i++) { |
| 1020 | state_free(w->pending_free[i]); |
| 1021 | } |
| 1022 | free(w->pending_free); |
| 1023 | cbm_mutex_unlock(&w->projects_lock); |
| 1024 | cbm_mutex_unlock(&w->coordination_lock); |
| 1025 | cbm_mutex_destroy(&w->projects_lock); |
| 1026 | cbm_mutex_destroy(&w->coordination_lock); |
| 1027 | free(w); |
| 1028 | } |
| 1029 | |
| 1030 | void cbm_watcher_set_project_mutation_guard(cbm_watcher_t *w, |
| 1031 | cbm_watcher_project_mutation_begin_fn begin, |