Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9c07970943 | |||
| 0832865952 |
@@ -5168,10 +5168,23 @@ el_val_t state_get(el_val_t key) {
|
|||||||
if (!k) return el_wrap_str(el_strdup(""));
|
if (!k) return el_wrap_str(el_strdup(""));
|
||||||
pthread_mutex_lock(&_state_mu);
|
pthread_mutex_lock(&_state_mu);
|
||||||
StateEntry* e = state_find(k);
|
StateEntry* e = state_find(k);
|
||||||
char* result = el_strdup_persist(e ? e->value : "");
|
/* ONE arena-tracked copy, taken under the lock.
|
||||||
|
*
|
||||||
|
* This used to make TWO copies: an el_strdup_persist temporary, then an
|
||||||
|
* arena-tracked copy of that temporary. The persistent one was never
|
||||||
|
* returned and never freed — el_strdup_persist bypasses the arena by
|
||||||
|
* design ("state_set, engram internals"), so arena-pop could not reclaim
|
||||||
|
* it. Every state_get therefore leaked its full value string, permanently.
|
||||||
|
*
|
||||||
|
* The soul's awareness loop has 68 state_get call sites and ticks every
|
||||||
|
* 200ms; measured leak was ~1.1 MB per tick, about 19 GB/hour. It went
|
||||||
|
* unnoticed for as long as the soul restarted often enough to mask it.
|
||||||
|
*
|
||||||
|
* el_strdup tracks into the thread-local arena, which touches no shared
|
||||||
|
* state, so doing it under _state_mu is safe and removes the need for the
|
||||||
|
* temporary entirely. */
|
||||||
|
char* copy = el_strdup(e ? e->value : "");
|
||||||
pthread_mutex_unlock(&_state_mu);
|
pthread_mutex_unlock(&_state_mu);
|
||||||
/* wrap in arena-tracked copy for the caller's request lifetime */
|
|
||||||
char* copy = el_strdup(result);
|
|
||||||
return el_wrap_str(copy);
|
return el_wrap_str(copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user