Skip to content

Commit

Permalink
Merge pull request #2770 from CosmosOS/fix/string_gc_issues
Browse files Browse the repository at this point in the history
Fix issue when SMT Table expands to larger than one page
  • Loading branch information
valentinbreiz authored Sep 21, 2023
2 parents c443286 + d7d697f commit 1f73caf
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 144 deletions.
12 changes: 12 additions & 0 deletions Docs/articles/Internals/object.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Object

## Memory Layout

Each object is allocated with a header of three uints. The first uint is the type of the object, the second uint is the instance type of object (normal, boxed, array etc) and the third is the size in memory.

## Garbage Collector Information

The garbage collector itself also alloctes its own header of 1 uint (2 ushort). The first ushort is the allocated size of the object (this includes the size of both headers) while the second tracks the GC information.
The GC information is made up of a 1bit flag (the lowest bit) used to track if the GC has hit the object during the sweep phase already and the upper 7 bits count how many static references there are to the object.

Combined in memory we have the format | ushort: memory size | ushort: gc info | uint: object type | uint: instance type | uint: object size | variable: object data |
5 changes: 3 additions & 2 deletions Tests/Cosmos.TestRunner.Core/Engine.Helpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ private void RunProcess(string aProcess, string aWorkingDirectory, string aArgum
}
else
{
xErrorReceived($"Error invoking '{aProcess}'.");
xErrorReceived($"Error invoking '{aProcess}'.");
}
}
}
Expand Down Expand Up @@ -223,7 +223,8 @@ private void RunIL2CPU(string kernelFileName, string outputFile)
"OutputFilename:" + outputFile,
"EnableLogging:True",
"EmitDebugSymbols:True",
"IgnoreDebugStubAttribute:False"
"IgnoreDebugStubAttribute:False",
"AllowComments:True"
};

xArgs.AddRange(xReferences.Select(r => "References:" + r));
Expand Down
15 changes: 14 additions & 1 deletion Tests/Kernels/Cosmos.Compiler.Tests.TypeSystem/Kernel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ public class Kernel : Sys.Kernel

protected override void BeforeRun()
{
Console.WriteLine("Cosmos booted successfully. Starting Type tests now please wait...");
}

private void TestVTablesImpl()
Expand Down Expand Up @@ -212,6 +211,12 @@ public void RealMethodsTest()
Heap.Collect();
nowAllocated = HeapSmall.GetAllocatedObjectCount();
Assert.AreEqual(allocated, nowAllocated, "TestMethod7 does not leak string objects");

allocated = HeapSmall.GetAllocatedObjectCount();
TestMethod8();
Heap.Collect();
nowAllocated = HeapSmall.GetAllocatedObjectCount();
Assert.AreEqual(allocated, nowAllocated, "TestMethod8 does not leak any objects");
}

void TestMethod6()
Expand All @@ -228,6 +233,14 @@ void TestMethod7()
}
}

void TestMethod8()
{
for (int i = 0; i < 100000; i++)
{
new object();
}
}

#endregion

protected override void Run()
Expand Down
86 changes: 48 additions & 38 deletions source/Cosmos.Core/Memory/Heap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -178,32 +178,37 @@ public static int Collect()

// Small objects
// we go one size at a time
var rootSMTPtr = HeapSmall.SMT->First;
while (rootSMTPtr != null)
SMTPage* SMTPage = HeapSmall.SMT;
while (SMTPage != null)
{
uint size = rootSMTPtr->Size;
uint objectSize = size + HeapSmall.PrefixItemBytes;
uint objectsPerPage = RAT.PageSize / objectSize;
RootSMTBlock* rootSMTPtr = SMTPage->First;
while (rootSMTPtr != null)
{
uint size = rootSMTPtr->Size;
uint objectSize = size + HeapSmall.PrefixItemBytes;
uint objectsPerPage = RAT.PageSize / objectSize;

SMTBlock* smtBlock = rootSMTPtr->First;
SMTBlock* smtBlock = rootSMTPtr->First;

while (smtBlock != null)
{
byte* pagePtr = smtBlock->PagePtr;
for (int i = 0; i < objectsPerPage; i++)
while (smtBlock != null)
{

if (*(ushort*)(pagePtr + i * objectSize + sizeof(ushort)) > 1) // 0 means not found and 1 means marked
// after the start of the object we first have one ushort of object size and then the ushort of gc info so + 2 == sizeof(ushort)
byte* pagePtr = smtBlock->PagePtr;
for (int i = 0; i < objectsPerPage; i++)
{
MarkAndSweepObject(pagePtr + i * objectSize + HeapSmall.PrefixItemBytes);

if (*(ushort*)(pagePtr + i * objectSize + sizeof(ushort)) > 1) // 0 means not found and 1 means marked
// after the start of the object we first have one ushort of object size and then the ushort of gc info so + 2 == sizeof(ushort)
{
MarkAndSweepObject(pagePtr + i * objectSize + HeapSmall.PrefixItemBytes);
}
}

smtBlock = smtBlock->NextBlock;
}

smtBlock = smtBlock->NextBlock;
rootSMTPtr = rootSMTPtr->LargerSize;
}

rootSMTPtr = rootSMTPtr->LargerSize;
SMTPage = SMTPage->Next;
}

// Mark and sweep objects from stack
Expand Down Expand Up @@ -245,37 +250,42 @@ public static int Collect()

// Small objects
// we go one size at a time
rootSMTPtr = HeapSmall.SMT->First;
while (rootSMTPtr != null)
SMTPage = HeapSmall.SMT;
while (SMTPage != null)
{
uint size = rootSMTPtr->Size;
uint objectSize = size + HeapSmall.PrefixItemBytes;
uint objectsPerPage = RAT.PageSize / objectSize;
RootSMTBlock* rootSMTPtr = SMTPage->First;
while (rootSMTPtr != null)
{
uint size = rootSMTPtr->Size;
uint objectSize = size + HeapSmall.PrefixItemBytes;
uint objectsPerPage = RAT.PageSize / objectSize;

SMTBlock* smtBlock = rootSMTPtr->First;
SMTBlock* smtBlock = rootSMTPtr->First;

while (smtBlock != null)
{
byte* pagePtr = smtBlock->PagePtr;
for (int i = 0; i < objectsPerPage; i++)
while (smtBlock != null)
{
if (*(ushort*)(pagePtr + i * objectSize) != 0)
byte* pagePtr = smtBlock->PagePtr;
for (int i = 0; i < objectsPerPage; i++)
{
if (*((ushort*)(pagePtr + i * objectSize) + 1) == 0)
if (*(ushort*)(pagePtr + i * objectSize) != 0)
{
Free(pagePtr + i * objectSize + HeapSmall.PrefixItemBytes);
freed += 1;
}
else
{
*((ushort*)(pagePtr + i * objectSize) + 1) &= (ushort)~ObjectGCStatus.Hit;
if (*((ushort*)(pagePtr + i * objectSize) + 1) == 0)
{
Free(pagePtr + i * objectSize + HeapSmall.PrefixItemBytes);
freed += 1;
}
else
{
*((ushort*)(pagePtr + i * objectSize) + 1) &= (ushort)~ObjectGCStatus.Hit;
}
}
}
smtBlock = smtBlock->NextBlock;
}
smtBlock = smtBlock->NextBlock;
}

rootSMTPtr = rootSMTPtr->LargerSize;
rootSMTPtr = rootSMTPtr->LargerSize;
}
SMTPage = SMTPage->Next;
}

//Enable interrupts back
Expand Down
Loading

0 comments on commit 1f73caf

Please sign in to comment.