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(""));
|
||||
pthread_mutex_lock(&_state_mu);
|
||||
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);
|
||||
/* wrap in arena-tracked copy for the caller's request lifetime */
|
||||
char* copy = el_strdup(result);
|
||||
return el_wrap_str(copy);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user