CyberEngineMkIII
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
CYBHeap.cpp
Go to the documentation of this file.
1 #include "CYB.hpp"
3 
4 CYB::Engine::Memory::Heap::Heap(const unsigned long long AInitialCommitSize) :
5  FReservation(Parameters::HEAP_RESERVATION_SIZE),
6  FFreeList(nullptr),
7  FLocked(false)
8 {
10 
11  FLargeBlock = API::Interop::Allocator::InPlaceAllocation<LargeBlock>(&FirstBlock(), FReservation.CommitSize() - sizeof(LargeBlock), nullptr);
12 }
13 
14 unsigned long long CYB::Engine::Memory::Heap::CalculateInitialCommitSize(const unsigned long long AInitialCommitSize) noexcept {
15  const auto MinimumInitialCommit(sizeof(LargeBlock) + 1);
16  return AInitialCommitSize >= MinimumInitialCommit ? AInitialCommitSize : MinimumInitialCommit;
17 }
18 
20  return *static_cast<Block*>(FReservation());
21 }
22 
24  return *static_cast<Block*>(FReservation());
25 }
26 
27 void CYB::Engine::Memory::Heap::AddToFreeList(Block& ABlock, Block* const APreviousEntry) noexcept {
28  if (APreviousEntry == nullptr) {
29  ABlock.FNextFree = FFreeList;
30  FFreeList = &ABlock;
31  }
32  else {
33  ABlock.FNextFree = APreviousEntry->FNextFree;
34  APreviousEntry->FNextFree = &ABlock;
35  }
36 }
37 
38 void CYB::Engine::Memory::Heap::RemoveFromFreeList(Block& ABlock, Block* const APreviousEntry) noexcept {
39  if (APreviousEntry == nullptr)
40  FFreeList = FFreeList->FNextFree;
41  else
42  APreviousEntry->FNextFree = ABlock.FNextFree;
43 }
44 
45 void CYB::Engine::Memory::Heap::LargeBlockNeedsAtLeast(unsigned int ARequiredNumBytes) {
46  ARequiredNumBytes += sizeof(LargeBlock);
47  API::Assert::LessThan(ARequiredNumBytes, static_cast<unsigned int>(std::numeric_limits<int>::max()));
48  if (FLargeBlock->Size() <= ARequiredNumBytes) {
49  const auto SizeDifference(ARequiredNumBytes - FLargeBlock->Size());
50  const auto NewCommitSize(FReservation.CommitSize() + SizeDifference + 1000);
51  bool Throw(false);
52  try {
53  FReservation.Commit(NewCommitSize);
54  }catch(CYB::Exception::Internal AException){
55  API::Assert::Equal<unsigned int>(AException.FErrorCode, CYB::Exception::Internal::MEMORY_COMMITAL_FAILURE);
56  Throw = true;
57  }
58  if(Throw)
60  FLargeBlock->SetSize(FLargeBlock->Size() + SizeDifference + 1000);
61  }
62 }
63 
64 void CYB::Engine::Memory::Heap::MergeBlockIfPossible(Block*& ABlock, Block* const ALastFreeListEntry) noexcept {
65  API::Assert::True(ABlock->IsFree());
66 
67  if (ABlock->LeftBlock() != nullptr && ABlock->LeftBlock()->IsFree()) {
68  auto LeftBlock(ABlock->LeftBlock());
69  const auto NewSize(ABlock->Size() + LeftBlock->Size() + sizeof(Block));
70 
71  if (NewSize <= static_cast<unsigned long long>(std::numeric_limits<int>::max())) {
72  RemoveFromFreeList(*ABlock, ALastFreeListEntry);
73  ABlock = &ABlock->EatLeftBlock();
74  //don't add it to the free list, we're already IN the free list
75  }
76  }
77 }
78 
80  static_cast<void>(ALock);
81 
82  ANumBytes = ANumBytes + (sizeof(void*) - (ANumBytes % sizeof(void*))); //align
83 
84  API::Assert::LessThan(ANumBytes, static_cast<unsigned int>(std::numeric_limits<int>::max()));
85  const auto MinimumBlockSize(16); //Do not splice a block that will have a size smaller than this
86  const auto MinimumBlockFootprint(MinimumBlockSize + sizeof(Block));
87 
88  //First search the free list for the first fit
89  for (Block* CurrentBlock(FFreeList), *LastFreeListEntry(nullptr); CurrentBlock != nullptr; CurrentBlock = CurrentBlock->FNextFree) {
90 
91  API::Assert::True(CurrentBlock->IsFree());
92 
93  if (CurrentBlock->Size() >= ANumBytes) {
94  //this is the block we're using
95 
96  RemoveFromFreeList(*CurrentBlock, LastFreeListEntry);
97 
98  //splice it if it's big enough
99  if ((CurrentBlock->Size() - ANumBytes) >= MinimumBlockFootprint) {
100  auto& NewBlock(CurrentBlock->Splice(ANumBytes));
101  //and add it to the free list
102  AddToFreeList(NewBlock, LastFreeListEntry);
103  }
104 
105  CurrentBlock->SetFree(false);
106  return *CurrentBlock;
107  }
108  else
109  //we can't actually remove it from the free list since we don't know the previous entry which changes if this succeeds, so only do this here
110  MergeBlockIfPossible(CurrentBlock, LastFreeListEntry);
111  LastFreeListEntry = CurrentBlock;
112  }
113 
114  LargeBlockNeedsAtLeast(ANumBytes);
115 
116  auto& AllocatedBlock(LargeBlock::AllocateBlock(FLargeBlock, ANumBytes));
117 
118  AllocatedBlock.SetFree(false);
119 
120  return AllocatedBlock;
121 }
123  ANumBytes = ANumBytes + (sizeof(void*) - (ANumBytes % sizeof(void*)));
124  API::Assert::LessThan(ANumBytes, static_cast<unsigned int>(std::numeric_limits<int>::max()));
125  auto& RightBlock(ABlock.RightBlock());
126  if (!RightBlock.IsLargeBlock()) {
127  //only thing we can do without reverse pointers
128  auto& NewData(AllocImpl(ANumBytes, ALock));
129  ALock.Release();
130  std::copy(static_cast<byte*>(ABlock.GetData()), static_cast<byte*>(ABlock.GetData()) + ABlock.Size(), static_cast<byte*>(NewData.GetData()));
131  Free(ABlock.GetData());
132  return Block::FromData(NewData.GetData());
133  }
134  else {
135  //still pretty decent
136  API::Assert::Equal<Block*>(&RightBlock, FLargeBlock);
137  const auto Required(ANumBytes - ABlock.Size());
138  LargeBlockNeedsAtLeast(Required);
139 
140  auto& AllocatedBlock(LargeBlock::AllocateBlock(FLargeBlock, Required));
141 
142  API::Assert::Equal(&ABlock, &AllocatedBlock.EatLeftBlock());
143  ABlock.SetFree(false);
144 
145  return ABlock;
146  }
147 }
149  static_cast<void>(ALock);
150  if (ABlock.LeftBlock() != nullptr && ABlock.LeftBlock()->IsFree()) {
151  ABlock.EatLeftBlock().Validate();
152  }
153  else {
154  AddToFreeList(ABlock, nullptr);
155  ABlock.SetFree(true);
156  ABlock.Validate();
157  }
158 }
159 
161  static_cast<void>(ALock);
162  long long ExpectedFreeListSize(0);
163  for (const Block* CurrentBlock(&FirstBlock()), *PreviousBlock(nullptr); !CurrentBlock->IsLargeBlock(); PreviousBlock = CurrentBlock, CurrentBlock = &CurrentBlock->RightBlock()) {
164  CurrentBlock->Validate();
165  API::Assert::Equal(static_cast<const Block*>(const_cast<Block*>(CurrentBlock)->LeftBlock()), PreviousBlock);
166  if (CurrentBlock->IsFree())
167  ++ExpectedFreeListSize;
168  }
169  FLargeBlock->Validate();
170 
171  for (auto CurrentBlock(FFreeList); CurrentBlock != nullptr; CurrentBlock = CurrentBlock->FNextFree, --ExpectedFreeListSize)
172  API::Assert::True(CurrentBlock->IsFree());
173 
174  API::Assert::Equal(ExpectedFreeListSize, 0LL);
175 }
176 
178  API::LockGuard Lock(FMutex);
179  WalkImpl(Lock);
180 }
181 
182 void* CYB::Engine::Memory::Heap::Alloc(const int ANumBytes) {
183  AllocCheck();
184  if (ANumBytes != 0) {
185  if (ANumBytes > 0) {
186  if (static_cast<unsigned int>(ANumBytes) <= API::ByteConverters::Megabytes(2047)) {
187  API::LockGuard Lock(FMutex); //alignment
188  return AllocImpl(static_cast<unsigned int>(ANumBytes), Lock).GetData();
189  }
191  }
193  }
194  return nullptr;
195 }
196 
197 void* CYB::Engine::Memory::Heap::Realloc(void* const APreviousAllocation, const int ANumBytes) {
198  AllocCheck();
199  if (APreviousAllocation != nullptr) {
200 
201  auto& WorkingBlock(Block::FromData(APreviousAllocation));
202  if (ANumBytes != 0) {
203  if (ANumBytes > 0) {
204  if (static_cast<unsigned int>(ANumBytes) <= API::ByteConverters::Megabytes(2047)) {
205  if (WorkingBlock.Size() >= static_cast<unsigned int>(ANumBytes))
206  return APreviousAllocation;
207  else {
208  API::LockGuard Lock(FMutex);
209  return ReallocImpl(WorkingBlock, static_cast<unsigned int>(ANumBytes), Lock).GetData();
210  }
211  }
213  }
215  }
216  API::LockGuard Lock(FMutex);
217  FreeImpl(WorkingBlock, Lock);
218  return nullptr;
219  }
220  return Alloc(ANumBytes);
221 }
222 
223 void CYB::Engine::Memory::Heap::Free(void* const APreviousAllocation) noexcept {
224  if (APreviousAllocation != nullptr) {
225  auto& WorkingBlock(Block::FromData(APreviousAllocation));
226  API::LockGuard Lock(FMutex);
227  FreeImpl(WorkingBlock, Lock);
228  }
229 }
230 
Block & AllocImpl(unsigned int ANumBytes, API::LockGuard &ALock)
Allocates a Block.
Definition: CYBHeap.cpp:79
A RAII locking mechanism.
Definition: LockGuard.hpp:7
unsigned char byte
It's a byte, 8 bits, etc...
Definition: Types.hpp:4
Block & EatLeftBlock(void) noexcept
Merge size and header into the size of the Block to the left. Does not modify free lists...
Definition: CYBBlock.cpp:109
static void True(const bool AExpression) noexcept
Assertion function. AExpression should always be evaluated.
An allocation was attempted with a negative size value.
Definition: Exception.hpp:40
Block & RightBlock(void) noexcept
Get the block to the right of this Block.
Definition: CYBBlock.cpp:71
Heap(const unsigned long long AInitialCommitSize)
Create a Heap.
Definition: CYBHeap.cpp:4
void Commit(const unsigned long long ANumBytes)
Commit some amount of memory from a reserved address space. On return, the memory will be usable...
void SetFree(const bool ANewFree) noexcept
Set the Block's free state.
Definition: CYBBlock.cpp:84
Used to identify the end of a Heap.
static constexpr bool IsDebug(void)
Get if the current compilation is a debug compilation.
void Walk(void) const
Walks the heap blocks and free list and HCFs if an error is detected.
Definition: CYBHeap.cpp:177
static void LessThan(const AType &ALHS, const AType &ARHS) noexcept
Less than assertion function. May not be evaluated.
void FreeImpl(Block &ABlock, API::LockGuard &ALock) noexcept(!API::Platform::IsDebug())
Frees a Block.
Definition: CYBHeap.cpp:148
void * GetData(void) noexcept
Get the data portion of the memory owned by this Block.
Definition: CYBBlock.cpp:55
const unsigned int FErrorCode
The assigned error code.
Definition: Exception.hpp:18
void * Alloc(const int ASize) finaloverride
Allocate memory from the heap for use.
Definition: CYBHeap.cpp:182
static Block & FromData(void *const AData)
Retrieves a reference to a Block given it's data pointer.
Definition: CYBBlock.cpp:59
Exceptions caused by external call failures or invalid external data. Only classifies ones that can p...
Definition: Exception.hpp:65
An allocation was attempted with a size above 2047MB.
Definition: Exception.hpp:41
bool IsLargeBlock(void) const noexcept
Check if this Block is a LargeBlock.
Definition: CYBBlock.cpp:51
void WalkImpl(API::LockGuard &ALock) const
Walks the heap blocks and free list and throws if an error is detected.
Definition: CYBHeap.cpp:160
Compilation configuration variables.
static Block & AllocateBlock(LargeBlock *&ALargeBlock, const unsigned int ANewBlockSize) noexcept
Allocate a portion of the LargeBlock's owned data to create a regular block.
static void Equal(const AType &ALHS, const AType &ARHS) noexcept
Equivalence assertion function. May not be evaluated.
unsigned long long CommitSize(void) const noexcept
Get the current commit size of the reservation.
A heap has no block large enough for a requested allocation and expansion failed. ...
Definition: Exception.hpp:74
void Release(void) noexcept
Releases the lock on Mutex, this same LockGuard can never reaquire it.
static unsigned long long CalculateInitialCommitSize(const unsigned long long AInitialCommitSize) noexcept
A small max comparison of AInitialCommitSize and sizeof(Block) + 1.
Definition: CYBHeap.cpp:14
Memory could not be commited from a reservation.
Definition: Exception.hpp:108
void MergeBlockIfPossible(Block *&ABlock, Block *const ALastFreeListEntry) noexcept
Coalesces Blocks to the left of ABlock and updates the free list.
Definition: CYBHeap.cpp:64
Precompiled header for inter-engine operations.
Platform::System::VirtualMemory FReservation
The VirtualMemory mapping owned by the heap, also a pointer to the first block.
Definition: CYBHeap.hpp:9
void Free(void *const APreviousAllocation) noexceptfinaloverride
Return memory to the heap. Data in allocated range will be lost.
Definition: CYBHeap.cpp:223
void LargeBlockNeedsAtLeast(unsigned int ARequiredNumBytes)
Ensures that FLargeBlock has at least ARequiredNumBytes of Size by committing more memory if necessar...
Definition: CYBHeap.cpp:45
Block & ReallocImpl(Block &ABlock, unsigned int ANumBytes, API::LockGuard &ALock)
Reallocates a Block.
Definition: CYBHeap.cpp:122
void * Realloc(void *const APreviousAllocation, const int ANewSize) finaloverride
Allocate memory from the heap for use while preserving previous data. Passing a valid APreviousAlloca...
Definition: CYBHeap.cpp:197
A unit of memory allocation.
Definition: CYBBlock.hpp:7
LargeBlock * FLargeBlock
The block that extends to the end of the free list.
Definition: CYBHeap.hpp:12
Exceptions that are thrown internally in the engine that the should never see, these are a superset o...
Definition: Exception.hpp:104
unsigned int Size(void) const noexcept
Get the size of the Block.
Definition: CYBBlock.cpp:41
Exceptions indicating an API contract violation. Should not be anticipated.
Definition: Exception.hpp:32
void RemoveFromFreeList(Block &ABlock, Block *const APreviousEntry) noexcept
Removes a Block from the free list after APreviousEntry while performing all the checks and reassignm...
Definition: CYBHeap.cpp:38
Block & FirstBlock(void) noexcept
Get a reference to the first block in the reservation.
Definition: CYBHeap.cpp:19
void AddToFreeList(Block &ABlock, Block *const APreviousEntry) noexcept
Adds a Block to the free list after APreviousEntry while performing all the checks and reassignments...
Definition: CYBHeap.cpp:27
static constexpr unsigned long long Megabytes(const unsigned long long AAmount)
Get the true byte value of some amount of megabytes.
Block * FNextFree
The next block in the free list.
Definition: CYBBlock.hpp:19