Move a state onto the deferred-free list (caller holds projects_lock). * The state may still be referenced by a poll_once snapshot; poll_once * drains the list at the start of its next cycle. Returns false when * growing the list fails (OOM): the state is left untouched and the * caller must keep it registered — freeing it immediately here could be * a use-after-free against an in-flight poll
| 730 | * caller must keep it registered — freeing it immediately here could be |
| 731 | * a use-after-free against an in-flight poll snapshot. */ |
| 732 | static bool defer_state_free(cbm_watcher_t *w, project_state_t *s) { |
| 733 | if (w->pending_free_count >= w->pending_free_cap) { |
| 734 | int new_cap = w->pending_free_cap ? w->pending_free_cap * 2 : 8; |
| 735 | project_state_t **tmp = |
| 736 | realloc(w->pending_free, (size_t)new_cap * sizeof(project_state_t *)); |
| 737 | if (!tmp) { |
| 738 | cbm_log_warn("watcher.unwatch.oom", "project", s->project_name); |
| 739 | return false; |
| 740 | } |
| 741 | w->pending_free = tmp; |
| 742 | w->pending_free_cap = new_cap; |
| 743 | } |
| 744 | w->pending_free[w->pending_free_count++] = s; |
| 745 | return true; |
| 746 | } |
| 747 | |
| 748 | /* ── Stale-root pruning (#286) ──────────────────────────────────── */ |
| 749 |
no outgoing calls
no test coverage detected