Browse Source

Orphaned chunks can’t have empty arenas.

Rika Ichinose 2 months ago
parent
commit
02752fdd36
1 changed files with 13 additions and 25 deletions
  1. 13 25
      rtl/inc/heap.inc

+ 13 - 25
rtl/inc/heap.inc

@@ -449,7 +449,6 @@ type
       procedure AdoptArena(arena: pFixedArena);
       procedure AdoptArena(arena: pFixedArena);
       procedure AdoptVarOwner(p: pointer); { Adopts the OS chunk that contains p. Must be performed under gs.lock. }
       procedure AdoptVarOwner(p: pointer); { Adopts the OS chunk that contains p. Must be performed under gs.lock. }
       class procedure ChangeThreadState(vOs: pVarOSChunk; ts: pThreadState); static;
       class procedure ChangeThreadState(vOs: pVarOSChunk; ts: pThreadState); static;
-      procedure FreeEmptyArenas(untilCount: SizeUint);
 
 
     {$ifndef FPC_SECTION_THREADVARS}
     {$ifndef FPC_SECTION_THREADVARS}
       procedure FixupSelfPtr;
       procedure FixupSelfPtr;
@@ -1722,11 +1721,21 @@ type
 
 
   procedure HeapInc.ThreadState.Orphan;
   procedure HeapInc.ThreadState.Orphan;
   var
   var
+    arena: pFixedArena;
     lastFree, nextFree: pFreeOSChunk;
     lastFree, nextFree: pFreeOSChunk;
     vOs, nextVOs, lastVOs: pVarOSChunk;
     vOs, nextVOs, lastVOs: pVarOSChunk;
   begin
   begin
     FlushToFree;
     FlushToFree;
-    FreeEmptyArenas(0);
+
+    { Has to free all empty arenas, otherwise the chunk that contains only empty arenas will leak (no one will ever adopt it, as it has nothing to free). }
+    while nEmptyArenas > 0 do
+    begin
+      arena := emptyArenas;
+      emptyArenas := arena^.next;
+      dec(nEmptyArenas);
+      FreeVar(arena);
+    end;
+
     { Prepend freeOS to gs.freeOS. }
     { Prepend freeOS to gs.freeOS. }
     lastFree := freeOS.last;
     lastFree := freeOS.last;
     if Assigned(lastFree) then
     if Assigned(lastFree) then
@@ -1773,14 +1782,8 @@ type
     sizeIndex := pCommonHeader(pointer(arena) + FixedArenaDataOffset)^.h and SizeIndexMask;
     sizeIndex := pCommonHeader(pointer(arena) + FixedArenaDataOffset)^.h and SizeIndexMask;
     inc(used, arena^.usedSize); { maxUsed is updated at the end of AdoptVarOwner. }
     inc(used, arena^.usedSize); { maxUsed is updated at the end of AdoptVarOwner. }
 
 
-    if arena^.usedSize = 0 then
-    begin
-      { Add arena to emptyArenas. }
-      arena^.next := emptyArenas;
-      emptyArenas := arena;
-      inc(nEmptyArenas); { May exceed MaxKeptFixedArenas, extra arenas are freed at the end of AdoptVarOwner. }
-    end
-    else if arena^.usedSize < arena^.fullThreshold then
+    { Orphan frees all empty arenas, so adopted arena can’t be empty. }
+    if arena^.usedSize < arena^.fullThreshold then
     begin
     begin
       { Add arena to partialArenas[sizeIndex]. }
       { Add arena to partialArenas[sizeIndex]. }
       nextArena := partialArenas[sizeIndex];
       nextArena := partialArenas[sizeIndex];
@@ -1839,8 +1842,6 @@ type
       inc(p, h and uint32(VarSizeMask));
       inc(p, h and uint32(VarSizeMask));
     until h and LastFlag <> 0;
     until h and LastFlag <> 0;
 
 
-    FreeEmptyArenas(MaxKeptFixedArenas);
-
     statv := used + gs.hugeUsed;
     statv := used + gs.hugeUsed;
     if statv > maxUsed then
     if statv > maxUsed then
       maxUsed := statv;
       maxUsed := statv;
@@ -1859,19 +1860,6 @@ type
     until h and LastFlag <> 0;
     until h and LastFlag <> 0;
   end;
   end;
 
 
-  procedure HeapInc.ThreadState.FreeEmptyArenas(untilCount: SizeUint);
-  var
-    arena: pFixedArena;
-  begin
-    while nEmptyArenas > untilCount do
-    begin
-      arena := emptyArenas;
-      emptyArenas := arena^.next;
-      dec(nEmptyArenas);
-      FreeVar(arena);
-    end;
-  end;
-
 {$ifndef FPC_SECTION_THREADVARS}
 {$ifndef FPC_SECTION_THREADVARS}
   procedure HeapInc.ThreadState.FixupSelfPtr;
   procedure HeapInc.ThreadState.FixupSelfPtr;
   var
   var