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 | |
14 | namespace sead |
15 | { |
16 | class 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 | |
27 | public: |
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 | |
76 | private: |
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. |
95 | class ScopedCurrentHeapSetter |
96 | { |
97 | public: |
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 | |
112 | protected: |
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 | |
128 | class FindContainHeapCache |
129 | { |
130 | public: |
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 |