Skip to content

Commit 0733e56

Browse files
authored
Merge pull request #2430 from terminal-cs/master
Minor heap changes, Realloc function
2 parents 59ac90f + 9751487 commit 0733e56

File tree

7 files changed

+89
-31
lines changed

7 files changed

+89
-31
lines changed

Docs/articles/Kernel/MemoryManagement.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ The RAT initialisation is triggered using `GCImplementation.Init()` which is cal
3333

3434
The RAT is managed through the `RAT` class. Pages are allocated via `void* RAT.AllocPages(byte aType, uint aPageCount = 1)`. If more than 1 page is allocated at once, the first page will be marked as type `aType`, while all later pages are marked as `Extension`. Pages can be freed (set to type `Empty`) using `void RAT.Free(uint aPageIdx)` which also frees any extension pages which follow the first page. To convert between a pointer and the page index, the method `uint RAT.GetFirstRATIndex(void* aPtr)` can be used.
3535

36-
The Heap itself is managed by the `Heap` class. It contains the mechanism to allocate (`byte* Heap.Alloc(uint aSize)`) and free (`void Heap.Free(void* aPtr)`) objects of various sizes. Objects are seperated by size in bytes into Small (Smaller than 1/4 Page), Medium (Smaller than 1 Page) and Large (Larger than 1 Page). Currently Medium and Large objects are managed the same way using the methods in `HeapLarge` which do little more than allocating/freeing the necessary number of pages. Small objects are managed differently in `HeapSmall`.
36+
The Heap itself is managed by the `Heap` class. It contains the mechanism to allocate (`byte* Heap.Alloc(uint aSize)`), re-allocate ('byte* Heap.Realloc(byte* aPtr, uint newSize)') and free (`void Heap.Free(void* aPtr)`) objects of various sizes. Objects are seperated by size in bytes into Small (Smaller than 1/4 Page), Medium (Smaller than 1 Page) and Large (Larger than 1 Page). Currently Medium and Large objects are managed the same way using the methods in `HeapLarge` which do little more than allocating/freeing the necessary number of pages. Small objects are managed differently in `HeapSmall`.
3737

3838
Small Objects are managed using the SMT (Size Map Table), which is initalised using `void HeapSmall.InitSMT(uint aMaxItemSize)`. The basic idea of the SMT is to allocate objects of similar sizes on the same page. The SMT grows dynamically as required. The SMT is made up of a series of pages, each of which contains a series of `RootSMTBlock` each of which link to a chain of `SMTBlock`. The `RootSMTBlock` can be thought of as column headers and the `SMTBlock` as the elements stored in the column. The `RootSMTBlock` are a linked list, each containing the maximum object size stored in its pages, the location of the first `SMTBlock` for this size, and the location of the next `RootSMTBlock`. The list is in ascending order of size, so that the smallest large enough `RootSMTBlock` is found first. A `SMTBlock` contains a pointer to the actual page where objects are stored, how much space is left on that page, and a pointer to the next `SMTBlock`. If every `SMTBlock` for a certain size is full, a new `SMTBlock` is allocated. The page linked to by the `SMTBlock` is split into an array of spaces, each large enough to allocate an object of maximum size with header, which can be iterated through via index and fixed size when allocating. Each object allocated on the `HeapSmall` has a header of 2 `ushort`, the first one storing the actual size of the object and the second, the GC status of the object.
3939

Tests/Kernels/Cosmos.Compiler.Tests.TypeSystem/Kernel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ private unsafe void TestGarbageCollectorMethods()
103103
Assert.AreEqual(allocated, afterFree, "Free causes one object to be freed again");
104104

105105
var testString = "asd";
106-
Assert.AreEqual(RAT.PageType.Empty, RAT.GetPageType(GCImplementation.GetPointer(testString)), "String is created statically and not managed by GC");
106+
Assert.AreEqual((byte)RAT.PageType.Empty, (byte)RAT.GetPageType(GCImplementation.GetPointer(testString)), "String is created statically and not managed by GC");
107107

108108
Assert.IsTrue(Heap.Collect() >= 0, "Running GC Collect first time does not crash and returns non-negative value");
109109
}

Tests/Kernels/MemoryOperationsTest/Kernel.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Text;
66
using Cosmos.System.ExtendedASCII;
77
using Cosmos.System.ScanMaps;
8+
using Cosmos.Core.Memory;
89
using Cosmos.Core;
910
using System.Runtime.InteropServices;
1011

@@ -156,6 +157,22 @@ static unsafe void TestMemoryBlock(MemoryBlock memoryBlock)
156157
memoryBlock.Read32(read);
157158
Assert.AreEqual(values, read, "Using Fill(int, int, int) works");
158159
}
160+
static unsafe void TestRealloc()
161+
{
162+
// Allocate initial pointer and fill with value 32
163+
byte* aPtr = Heap.Alloc(16);
164+
MemoryOperations.Fill(aPtr, (byte)32, 16);
165+
166+
// Resize/realloc to 17 bytes
167+
aPtr = Heap.Realloc(aPtr, 17);
168+
169+
// Test for first 16 being 32 and last being 0
170+
for (int i = 0; i < 15; i++)
171+
{
172+
Assert.AreEqual(aPtr[i], 32, $"Expected value 32 not found in index {i} of aPtr.");
173+
}
174+
Assert.AreEqual(aPtr[16], 0, "Expected value 0 not found at the end of aPtr.");
175+
}
159176

160177
protected override void Run()
161178
{
@@ -164,6 +181,7 @@ protected override void Run()
164181
TestCopy();
165182
TestMemoryBlock(new MemoryBlock(0x60000, 128)); //we are testing in SVGA video memory which should not be in use
166183
TestManagedMemoryBlock(new ManagedMemoryBlock(128));
184+
TestRealloc();
167185
SpanTest.Execute();
168186
TestController.Completed();
169187
}

source/Cosmos.Core/GCImplementation.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public static ulong GetAvailableRAM()
6565
/// <returns>Returns the used PageSize by the MemoryManager in Bytes.</returns>
6666
public static uint GetUsedRAM()
6767
{
68-
return (RAT.TotalPageCount - RAT.GetPageCount(RAT.PageType.Empty)) * RAT.PageSize;
68+
return (RAT.TotalPageCount - RAT.GetPageCount((byte)RAT.PageType.Empty)) * RAT.PageSize;
6969
}
7070
/// <summary>
7171
/// Initialise the Memory Manager, this should not be called anymore since it is done very early during the boot process.

source/Cosmos.Core/Memory/Heap.cs

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,47 @@ public static unsafe void Init()
3232
HeapLarge.Init();
3333
}
3434

35+
/// <summary>
36+
/// Re-allocates or "re-sizes" data asigned to a pointer.
37+
/// The pointer specified must be the start of an allocated block in the heap.
38+
/// This shouldn't be used with objects as a new address is given when realocating memory.
39+
/// </summary>
40+
/// <param name="aPtr">Existing pointer</param>
41+
/// <param name="NewSize">Size to extend to</param>
42+
/// <returns>New pointer with specified size while maintaining old data.</returns>
43+
public static byte* Realloc(byte* aPtr, uint newSize)
44+
{
45+
// TODO: don't move memory position if there is enough space in the current one.
46+
47+
// Get existing size
48+
uint Size = (RAT.GetPageType(aPtr) == RAT.PageType.HeapSmall ? ((ushort*)aPtr)[-2] : ((uint*)aPtr)[-4]);
49+
50+
if (Size == newSize)
51+
{
52+
// Return existing pointer as nothing needs to be done.
53+
return aPtr;
54+
}
55+
if (Size > newSize)
56+
{
57+
Size -= (newSize - Size);
58+
}
59+
60+
// Allocate a new buffer to use
61+
byte* ToReturn = Alloc(newSize);
62+
63+
// Copy the old buffer to the new one
64+
MemoryOperations.Copy(ToReturn, aPtr, (int)Size);
65+
66+
// Comented out to help in the future if we use objects with realloc
67+
// Copy the GC state
68+
//((ushort*)ToReturn)[-1] = ((ushort*)aPtr)[-1];
69+
((ushort*)ToReturn)[-1] = 0;
70+
71+
// Free the old data and return
72+
Free(aPtr);
73+
return ToReturn;
74+
}
75+
3576
/// <summary>
3677
/// Alloc memory block, of a given size.
3778
/// </summary>
@@ -123,7 +164,7 @@ public static int Collect()
123164
for (int ratIndex = 0; ratIndex < RAT.TotalPageCount; ratIndex++)
124165
{
125166
var pageType = *(RAT.mRAT + ratIndex);
126-
if (pageType == RAT.PageType.HeapMedium || pageType == RAT.PageType.HeapLarge)
167+
if (pageType == (byte)RAT.PageType.HeapMedium || pageType == (byte)RAT.PageType.HeapLarge)
127168
{
128169
var pagePtr = RAT.RamStart + ratIndex * RAT.PageSize;
129170
if (*(ushort*)(pagePtr + 3) != 0)
@@ -184,7 +225,7 @@ public static int Collect()
184225
for (int ratIndex = 0; ratIndex < RAT.TotalPageCount; ratIndex++)
185226
{
186227
var pageType = *(RAT.mRAT + ratIndex);
187-
if (pageType == RAT.PageType.HeapMedium || pageType == RAT.PageType.HeapLarge)
228+
if (pageType == (byte)RAT.PageType.HeapMedium || pageType == (byte)RAT.PageType.HeapLarge)
188229
{
189230
var pagePointer = RAT.RamStart + ratIndex * RAT.PageSize;
190231
if (*((ushort*)(pagePointer + HeapLarge.PrefixBytes) - 1) == 0)

source/Cosmos.Core/Memory/HeapLarge.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public static void Init()
2929
/// </summary>
3030
/// <param name="aSize">A size of block to alloc, in bytes.</param>
3131
/// <returns>Byte pointer to the start of the block.</returns>
32-
public static byte* Alloc(uint aSize, byte aType = RAT.PageType.HeapLarge)
32+
public static byte* Alloc(uint aSize, RAT.PageType aType = RAT.PageType.HeapLarge)
3333
{
3434
uint xPages = ((aSize + PrefixBytes) / RAT.PageSize) + 1;
3535
var xPtr = (uint*)RAT.AllocPages(aType, xPages);

source/Cosmos.Core/Memory/RAT.cs

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,48 +19,47 @@ unsafe static public class RAT
1919
// than who owns them.
2020

2121
/// <summary>
22-
/// PageType class. Used like a enum to define the type of the page.
22+
/// PageType enum. Used to define the type of the page.
2323
/// </summary>
24-
/// <remarks>Only used to define page type.</remarks>
25-
static public class PageType
24+
public enum PageType
2625
{
2726
/// <summary>
2827
/// Empty page.
2928
/// Can also indicate invalid page.
3029
/// </summary>
31-
public const byte Empty = 0;
30+
Empty = 0,
3231

3332
// Data Types from 1, special meanings from 255 down.
3433
/// <summary>
3534
/// Indicates that the page contains objects managed by the GC
3635
/// </summary>
37-
public const byte GCManaged = 1;
36+
GCManaged = 1,
3837
/// <summary>
3938
/// Small heap page.
4039
/// </summary>
41-
public const byte HeapSmall = 3;
40+
HeapSmall = 3,
4241
/// <summary>
4342
/// Medium heap page.
4443
/// </summary>
45-
public const byte HeapMedium = 5;
44+
HeapMedium = 5,
4645
/// <summary>
4746
/// Large heap page.
4847
/// </summary>
49-
public const byte HeapLarge = 7;
48+
HeapLarge = 7,
5049

5150
/// <summary>
5251
/// RAT type page.
5352
/// </summary>
54-
public const byte RAT = 32;
53+
RAT = 32,
5554
/// <summary>
5655
/// Page which is part of the SMT
5756
/// </summary>
58-
public const byte SMT = 64;
57+
SMT = 64,
5958
// Extension of previous page.
6059
/// <summary>
6160
/// Extension of pre-existing page.
6261
/// </summary>
63-
public const byte Extension = 128;
62+
Extension = 128,
6463
}
6564

6665
/// <summary>
@@ -144,12 +143,12 @@ public static void Init(byte* aStartPtr, uint aSize)
144143
// Mark empty pages as such in the RAT Table
145144
for (byte* p = mRAT; p < mRAT + TotalPageCount - xRatPageCount; p++)
146145
{
147-
*p = PageType.Empty;
146+
*p = (byte)PageType.Empty;
148147
}
149148
// Mark the rat pages as such
150149
for (byte* p = mRAT + TotalPageCount - xRatPageCount; p < mRAT + xRatTotalSize; p++)
151150
{
152-
*p = PageType.RAT;
151+
*p = (byte)PageType.RAT;
153152
}
154153
Heap.Init();
155154
}
@@ -172,7 +171,7 @@ public static uint GetPageCount(byte aType = 0)
172171
}
173172
else if (xCounting)
174173
{
175-
if (*p == PageType.Extension)
174+
if (*p == (byte)PageType.Extension)
176175
{
177176
xResult++;
178177
}
@@ -191,7 +190,7 @@ public static uint GetPageCount(byte aType = 0)
191190
/// <param name="aType">A type of pages to alloc.</param>
192191
/// <param name="aPageCount">Number of pages to alloc. (default = 1)</param>
193192
/// <returns>A pointer to the first page on success, null on failure.</returns>
194-
public static void* AllocPages(byte aType, uint aPageCount = 1)
193+
public static void* AllocPages(PageType aType, uint aPageCount = 1)
195194
{
196195
byte* xPos = null;
197196

@@ -203,7 +202,7 @@ public static uint GetPageCount(byte aType = 0)
203202
{
204203
for (byte* p = mRAT; p < mRAT + TotalPageCount; p++)
205204
{
206-
if (*p == PageType.Empty)
205+
if (*p == (byte)PageType.Empty)
207206
{
208207
xPos = p;
209208
break;
@@ -216,7 +215,7 @@ public static uint GetPageCount(byte aType = 0)
216215
// so we don't bother to account for such a case. xPos would also have issues.
217216
for (byte* p = mRAT + TotalPageCount - 1; p >= mRAT; p--)
218217
{
219-
if (*p == PageType.Empty)
218+
if (*p == (byte)PageType.Empty)
220219
{
221220
if (++xCount == aPageCount)
222221
{
@@ -236,10 +235,10 @@ public static uint GetPageCount(byte aType = 0)
236235
{
237236
var diff = xPos - mRAT;
238237
byte* xResult = RamStart + diff * PageSize;
239-
*xPos = aType;
238+
*xPos = (byte)aType;
240239
for (byte* p = xPos + 1; p < xPos + xCount; p++)
241240
{
242-
*p = PageType.Extension;
241+
*p = (byte)PageType.Extension;
243242
}
244243
CPU.ZeroFill((uint)xResult, PageSize * aPageCount);
245244
return xResult;
@@ -260,7 +259,7 @@ public static uint GetFirstRATIndex(void* aPtr)
260259
// See note about when mRAT = 0 in Alloc.
261260
for (byte* p = mRAT + xPos; p >= mRAT; p--)
262261
{
263-
if (*p != PageType.Extension)
262+
if (*p != (byte)PageType.Extension)
264263
{
265264
return (uint)(p - mRAT);
266265
}
@@ -279,13 +278,13 @@ public static uint GetFirstRATIndex(void* aPtr)
279278
/// <param name="aPtr">A pointer to the page to get the type of.</param>
280279
/// <returns>byte value.</returns>
281280
/// <exception cref="Exception">Thrown if page type is not found.</exception>
282-
public static byte GetPageType(void* aPtr)
281+
public static PageType GetPageType(void* aPtr)
283282
{
284283
if(aPtr < RamStart || aPtr > HeapEnd)
285284
{
286285
return PageType.Empty;
287286
}
288-
return mRAT[GetFirstRATIndex(aPtr)];
287+
return (PageType)mRAT[GetFirstRATIndex(aPtr)];
289288
}
290289

291290
/// <summary>
@@ -295,14 +294,14 @@ public static byte GetPageType(void* aPtr)
295294
public static void Free(uint aPageIdx)
296295
{
297296
byte* p = mRAT + aPageIdx;
298-
*p = PageType.Empty;
297+
*p = (byte)PageType.Empty;
299298
for (; p < mRAT + TotalPageCount; )
300299
{
301-
if (*++p != PageType.Extension)
300+
if (*++p != (byte)PageType.Extension)
302301
{
303302
break;
304303
}
305-
*p = PageType.Empty;
304+
*p = (byte)PageType.Empty;
306305
}
307306
}
308307
}

0 commit comments

Comments
 (0)