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 : public 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 Atomic<u32> sHeapCheckTag;
87 static TickSpan sSleepSpanAtRemoveCacheFailure;
88
89 /// fallback heap that is returned when getting the current heap outside of an sead::Thread
90 Heap* mAllocFromNotSeadThreadHeap = nullptr;
91 IAllocFailedCallback* mAllocFailedCallback = nullptr;
92};
93
94/// Sets the "current heap" to the specified heap and restores the previous "current heap"
95/// when this goes out of scope.
96class ScopedCurrentHeapSetter
97{
98public:
99 explicit ScopedCurrentHeapSetter(sead::Heap* heap)
100 {
101 if (heap)
102 setPreviousHeap_(HeapMgr::instance()->setCurrentHeap_(heap));
103 else
104 setPreviousHeapToNone_();
105 }
106
107 ~ScopedCurrentHeapSetter()
108 {
109 if (hasPreviousHeap_())
110 HeapMgr::instance()->setCurrentHeap_(getPreviousHeap_());
111 }
112
113protected:
114 /// @warning Only call this if hasPreviousHeap returns true.
115 Heap* getPreviousHeap_() const { return reinterpret_cast<Heap*>(mPreviousHeap); }
116 void setPreviousHeap_(Heap* heap) { mPreviousHeap = reinterpret_cast<uintptr_t>(heap); }
117 void setPreviousHeapToNone_() { mPreviousHeap = 1; }
118 bool hasPreviousHeap_() const
119 {
120 // XXX: We cannot just do `mPreviousHeap != 1` because that results in different codegen.
121 // The cast smells like implementation defined behavior, but 1 should not be a valid
122 // pointer on any platform that we support. In practice, this will work correctly.
123 return reinterpret_cast<Heap*>(mPreviousHeap) != reinterpret_cast<Heap*>(1);
124 }
125
126 uintptr_t mPreviousHeap = 0;
127};
128
129class FindContainHeapCache
130{
131public:
132 FindContainHeapCache();
133
134 bool tryRemoveHeap(Heap* heap);
135 Heap* tryAddHeap()
136 {
137 mHeap |= 1;
138 return reinterpret_cast<Heap*>(mHeap.load());
139 }
140 Heap* getHeap() const { return reinterpret_cast<Heap*>(mHeap.load()); }
141 void setHeap(Heap* heap) { mHeap.storeNonAtomic(uintptr_t(heap)); }
142 void resetHeap() { mHeap.fetchAnd(~1LL); }
143
144 Atomic<uintptr_t> mHeap;
145};
146
147} // namespace sead
148
149#endif // SEAD_HEAPMGR_H_
150