Skip to content

Commit 01d980d

Browse files
committed
hg_malloc: report free as the room left to carve, not size minus live
free was size minus the live figure, which reads ~95% available on a 256M arena regardless of load and barely moves. Worse, it is optimistic in the direction that matters: carved-but-recycled cells are reusable only within their own size class, so free can report hundreds of MB while an allocation of a different size fails outright. Report the uncarved remainder instead, which is what actually predicts that failure. free + real_used no longer sums to size (real_used is the live figure); free + carve does. The three figures now answer three different questions rather than two of them being redundant.
1 parent dae0ae8 commit 01d980d

2 files changed

Lines changed: 14 additions & 5 deletions

File tree

‎mem/hg_malloc.c‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,8 @@ void hg_info(struct hg_block *hb, struct mem_info *info)
461461
* never decreases and would otherwise look like a leak. */
462462
recycled = hg_slab_recycled(hb);
463463
info->real_used = hb->real_used > recycled ? hb->real_used - recycled : 0;
464-
info->free = hb->size - info->real_used;
464+
/* room left to CARVE, not size minus live - see hg_get_free() */
465+
info->free = hb->size - hb->real_used;
465466

466467
/* the peak of what real_used above actually reached - NOT the peak
467468
* carve (hb->max_real_used), which only ever grows and would drift

‎mem/hg_malloc.h‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -437,10 +437,18 @@ static inline unsigned long hg_get_used(struct hg_block *hb)
437437
* report the raw carve footprint. */
438438
static inline unsigned long hg_get_free(struct hg_block *hb)
439439
{
440-
unsigned long recycled = hg_slab_recycled(hb);
441-
442-
return hb->size - (hb->real_used > recycled ?
443-
hb->real_used - recycled : 0);
440+
/* Headroom left to CARVE - deliberately NOT size minus the live figure.
441+
* Carved-but-recycled cells are reusable only within their own size
442+
* class, so counting them as free reports ~95% available right up until
443+
* an allocation of a DIFFERENT size fails with "no more HG_MALLOC arena
444+
* memory". hb->real_used is the carve footprint, so this is the number
445+
* that actually predicts that failure.
446+
*
447+
* Consequence: free + real_used != size here (real_used is live), unlike
448+
* q_malloc. The invariant that does hold is free + carve == size. Each
449+
* figure answers a different question: used = live payload, real_used /
450+
* max_used = live commitment and its peak, free = room left to carve. */
451+
return hb->size - hb->real_used;
444452
}
445453
static inline unsigned long hg_get_real_used(struct hg_block *hb)
446454
{

0 commit comments

Comments
 (0)