1#include <heap/seadExpHeap.h>
2#include <heap/seadHeap.h>
3#include <heap/seadHeapMgr.h>
4#include <prim/seadScopedLock.h>
5#include <thread/seadThread.h>
6#include <time/seadTickSpan.h>
7#include <utility>
8
9namespace sead
10{
11HeapMgr* HeapMgr::sInstancePtr = nullptr;
12
13HeapMgr HeapMgr::sInstance;
14Arena HeapMgr::sDefaultArena;
15HeapMgr::RootHeaps HeapMgr::sRootHeaps;
16HeapMgr::IndependentHeaps HeapMgr::sIndependentHeaps;
17CriticalSection HeapMgr::sHeapTreeLockCS;
18
19HeapMgr::HeapMgr() = default;
20
21void HeapMgr::initialize(size_t size)
22{
23 sHeapTreeLockCS.lock();
24 sArena = &sDefaultArena;
25 sDefaultArena.initialize(size);
26 initializeImpl_();
27 sHeapTreeLockCS.unlock();
28}
29
30void HeapMgr::initializeImpl_()
31{
32 sInstance.mAllocFailedCallback = nullptr;
33 sSleepSpanAtRemoveCacheFailure = TickSpan::makeFromMicroSeconds(usec: 10);
34 createRootHeap_();
35 sInstancePtr = &sInstance;
36}
37
38void HeapMgr::initialize(Arena* arena)
39{
40 sArena = arena;
41 initializeImpl_();
42}
43
44void HeapMgr::createRootHeap_()
45{
46 auto* expHeap = ExpHeap::tryCreate(address: sArena->mStart, size: sArena->mSize, name: "RootHeap", false);
47 sRootHeaps.pushBack(ptr: expHeap);
48}
49
50// NON_MATCHING: mismatching on the loops, maybe due to an issue in popBack
51void HeapMgr::destroy()
52{
53 sHeapTreeLockCS.lock();
54 sInstance.mAllocFailedCallback = nullptr;
55
56 while (!sIndependentHeaps.isEmpty())
57 {
58 sIndependentHeaps.back()->destroy();
59 sIndependentHeaps.popBack();
60 }
61
62 while (!sRootHeaps.isEmpty())
63 {
64 sRootHeaps.back()->destroy();
65 sRootHeaps.popBack();
66 }
67
68 sInstancePtr = nullptr;
69 sArena->destroy();
70 sArena = nullptr;
71 sHeapTreeLockCS.unlock();
72}
73
74void HeapMgr::initHostIO() {}
75
76bool HeapMgr::isContainedInAnyHeap(const void* ptr)
77{
78 for (auto& heap : sRootHeaps)
79 {
80 if (heap.isInclude(ptr))
81 return true;
82 }
83 for (auto& heap : sIndependentHeaps)
84 {
85 if (heap.isInclude(ptr))
86 return true;
87 }
88 return false;
89}
90void HeapMgr::dumpTreeYAML(WriteStream& stream)
91{
92 sHeapTreeLockCS.lock();
93 for (auto& heap : sRootHeaps)
94 {
95 heap.dumpTreeYAML(stream, 0);
96 }
97 for (auto& heap : sIndependentHeaps)
98 {
99 heap.dumpTreeYAML(stream, 0);
100 }
101 sHeapTreeLockCS.unlock();
102}
103
104void HeapMgr::setAllocFromNotSeadThreadHeap(Heap* heap)
105{
106 mAllocFromNotSeadThreadHeap = heap;
107}
108
109void HeapMgr::removeFromFindContainHeapCache_(Heap* heap)
110{
111 auto* threadMgr = ThreadMgr::instance();
112 if (!threadMgr)
113 return;
114
115 Thread* mainThread = threadMgr->getMainThread();
116 if (mainThread)
117 {
118 while (!mainThread->getFindContainHeapCache()->tryRemoveHeap(heap))
119 Thread::sleep(howLong: sSleepSpanAtRemoveCacheFailure);
120 }
121
122 while (threadMgr->tryRemoveFromFindContainHeapCache(heap))
123 Thread::sleep(howLong: sSleepSpanAtRemoveCacheFailure);
124}
125
126Heap* HeapMgr::findHeapByName(const sead::SafeString& name, int index) const
127{
128 auto lock = makeScopedLock(lock&: sHeapTreeLockCS);
129 for (auto& heap : sRootHeaps)
130 {
131 Heap* found = findHeapByName_(&heap, name, index: &index);
132 if (found)
133 return found;
134 }
135 for (auto& heap : sIndependentHeaps)
136 {
137 Heap* found = findHeapByName_(&heap, name, index: &index);
138 if (found)
139 return found;
140 }
141 return nullptr;
142}
143
144Heap* HeapMgr::findHeapByName_(Heap* heap, const SafeString& name, int* index)
145{
146 if (heap->getName() == name)
147 {
148 if (*index == 0)
149 return heap;
150 --*index;
151 }
152 for (auto& child : heap->mChildren)
153 {
154 Heap* found = findHeapByName_(heap: &child, name, index);
155 if (found)
156 return found;
157 }
158 return nullptr;
159}
160
161Heap* HeapMgr::getCurrentHeap() const
162{
163 Thread* currentThread = ThreadMgr::instance()->getCurrentThread();
164 if (currentThread)
165 return currentThread->getCurrentHeap();
166 return mAllocFromNotSeadThreadHeap;
167}
168
169Heap* HeapMgr::setCurrentHeap_(Heap* heap)
170{
171 return ThreadMgr::instance()->getCurrentThread()->setCurrentHeap(heap);
172}
173
174void HeapMgr::removeRootHeap(Heap* heap)
175{
176 if (sRootHeaps.size() < 1)
177 return;
178 s32 index = sRootHeaps.indexOf(ptr: heap);
179 if (index != -1)
180 sRootHeaps.erase(position: index);
181}
182
183HeapMgr::IAllocFailedCallback*
184HeapMgr::setAllocFailedCallback(HeapMgr::IAllocFailedCallback* callback)
185{
186 return std::exchange(obj&: mAllocFailedCallback, new_value&: callback);
187}
188
189FindContainHeapCache::FindContainHeapCache() = default;
190
191bool FindContainHeapCache::tryRemoveHeap(Heap* heap)
192{
193 uintptr_t original;
194 if (mHeap.compareExchange(expected: uintptr_t(heap), desired: 0, original: &original))
195 return true;
196 return (original & ~1u) != uintptr_t(heap);
197}
198} // namespace sead
199