| 889 | } |
| 890 | |
| 891 | void cbm_watcher_free(cbm_watcher_t *w) { |
| 892 | if (!w) { |
| 893 | return; |
| 894 | } |
| 895 | /* Safety net: ensure stopped is set before draining pending_free. |
| 896 | * In production the caller should cbm_watcher_stop() + join first. */ |
| 897 | atomic_store(&w->stopped, 1); |
| 898 | cbm_mutex_lock(&w->coordination_lock); |
| 899 | cbm_mutex_lock(&w->projects_lock); |
| 900 | cbm_ht_foreach(w->projects, free_state_entry, NULL); |
| 901 | cbm_ht_free(w->projects); |
| 902 | for (int i = 0; i < w->pending_free_count; i++) { |
| 903 | state_free(w->pending_free[i]); |
| 904 | } |
| 905 | free(w->pending_free); |
| 906 | cbm_mutex_unlock(&w->projects_lock); |
| 907 | cbm_mutex_unlock(&w->coordination_lock); |
| 908 | cbm_mutex_destroy(&w->projects_lock); |
| 909 | cbm_mutex_destroy(&w->coordination_lock); |
| 910 | free(w); |
| 911 | } |
| 912 | |
| 913 | void cbm_watcher_set_project_mutation_guard(cbm_watcher_t *w, |
| 914 | cbm_watcher_project_mutation_begin_fn begin, |