1#ifndef SEAD_HEAPMGR_H_
2#define SEAD_HEAPMGR_H_
3
4#include <container/seadPtrArray.h>
5#include <heap/seadArena.h>
6#include <heap/seadHeap.h>
7#include <hostio/seadHostIONode.h>
8#include <prim/seadDelegate.h>
9#include <prim/seadSafeString.h>
10#include <thread/seadAtomic.h>
11#include <thread/seadCriticalSection.h>
12#include <time/seadTickSpan.h>
13
14namespace sead
15{
16class HeapMgr : hostio::Node
17{
18 struct AllocCallbackArg;
19 struct CreateCallbackArg;
20 struct DestroyCallbackArg;
21 struct FreeCallbackArg;
22 using IAllocCallback = IDelegate1<const AllocCallbackArg*>;
23 using ICreateCallback = IDelegate1<const CreateCallbackArg*>;
24 using IDestroyCallback = IDelegate1<const DestroyCallbackArg*>;
25 using IFreeCallback = IDelegate1<const FreeCallbackArg*>;
26
27public:
28 struct AllocFailedCallbackArg
29 {
30 Heap* heap;
31 size_t request_size;
32 s32 request_alignment;
33 size_t alloc_size;
34 s32 alloc_alignment;
35 };
36 using IAllocFailedCallback = IDelegate1<const AllocFailedCallbackArg*>;
37
38 HeapMgr();
39 virtual ~HeapMgr();
40
41 static void initialize(size_t size);
42 static void initializeImpl_();
43 static void initialize(Arena* arena);
44 static void createRootHeap_();
45 static void destroy();
46 void initHostIO();
47
48 Heap* findContainHeap(const void* ptr) const;
49 static bool isContainedInAnyHeap(const void* ptr);
50 static void dumpTreeYAML(WriteStream& stream);
51 void setAllocFromNotSeadThreadHeap(Heap* heap);
52 static void removeFromFindContainHeapCache_(Heap* heap);
53
54 Heap* findHeapByName(const SafeString& name, int index) const;
55 static Heap* findHeapByName_(Heap*, const SafeString&, int* index);
56 Heap* getCurrentHeap() const;
57
58 static void removeRootHeap(Heap*);
59
60 IAllocFailedCallback* setAllocFailedCallback(IAllocFailedCallback* callback);
61 IAllocFailedCallback* getAllocFailedCallback() { return mAllocFailedCallback; }
62
63 static HeapMgr* instance() { return sInstancePtr; }
64 static s32 getRootHeapNum() { return sRootHeaps.size(); }
65
66 static Heap* getRootHeap(s32 index) { return sRootHeaps[index]; }
67
68 // TODO: these should be private
69 static Arena* sArena;
70 static HeapMgr sInstance;
71 static HeapMgr* sInstancePtr;
72
73 using RootHeaps = FixedPtrArray<Heap, 4>;
74 using IndependentHeaps = FixedPtrArray<Heap, 4>;
75
76private:
77 friend class ScopedCurrentHeapSetter;
78
79 /// Set the current heap to the specified heap and returns the previous "current heap".
80 Heap* setCurrentHeap_(Heap* heap);
81
82 static Arena sDefaultArena;
83 static RootHeaps sRootHeaps;
84 static IndependentHeaps sIndependentHeaps;
85 static CriticalSection sHeapTreeLockCS;
86 static TickSpan sSleepSpanAtRemoveCacheFailure;
87
88 /// fallback heap that is returned when getting the current heap outside of an sead::Thread
89 Heap* mAllocFromNotSeadThreadHeap = nullptr;
90 IAllocFailedCallback* mAllocFailedCallback = nullptr;
91};
92
93/// Sets the "current heap" to the specified heap and restores the previous "current heap"
94/// when this goes out of scope.
95class ScopedCurrentHeapSetter
96{
97public:
98 explicit ScopedCurrentHeapSetter(sead::Heap* heap)
99 {
100 if (heap)
101 setPreviousHeap_(HeapMgr::instance()->setCurrentHeap_(heap));
102 else
103 setPreviousHeapToNone_();
104 }
105
106 ~ScopedCurrentHeapSetter()
107 {
108 if (hasPreviousHeap_())
109 HeapMgr::instance()->setCurrentHeap_(getPreviousHeap_());
110 }
111
112protected:
113 /// @warning Only call this if hasPreviousHeap returns true.
114 Heap* getPreviousHeap_() const { return reinterpret_cast<Heap*>(mPreviousHeap); }
115 void setPreviousHeap_(Heap* heap) { mPreviousHeap = reinterpret_cast<uintptr_t>(heap); }
116 void setPreviousHeapToNone_() { mPreviousHeap = 1; }
117 bool hasPreviousHeap_() const
118 {
119 // XXX: We cannot just do `mPreviousHeap != 1` because that results in different codegen.
120 // The cast smells like implementation defined behavior, but 1 should not be a valid
121 // pointer on any platform that we support. In practice, this will work correctly.
122 return reinterpret_cast<Heap*>(mPreviousHeap) != reinterpret_cast<Heap*>(1);
123 }
124
125 uintptr_t mPreviousHeap;
126};
127
128class FindContainHeapCache
129{
130public:
131 FindContainHeapCache();
132
133 bool tryRemoveHeap(Heap* heap);
134 Heap* tryAddHeap()
135 {
136 mHeap |= 1;
137 return reinterpret_cast<Heap*>(mHeap.load());
138 }
139 Heap* getHeap() const { return reinterpret_cast<Heap*>(mHeap.load()); }
140 void setHeap(Heap* heap) { mHeap.storeNonAtomic(uintptr_t(heap)); }
141 void resetHeap() { mHeap.fetchAnd(~1LL); }
142
143 Atomic<uintptr_t> mHeap;
144};
145
146} // namespace sead
147
148#endif // SEAD_HEAPMGR_H_
149