1 /*---------------------------------------------------------------------------* 2 Project: Horizon 3 File: fnd_BufferManager.h 4 Copyright (C)2009 Nintendo Co., Ltd. All rights reserved. 5 These coded instructions, statements, and computer programs contain 6 proprietary information of Nintendo of America Inc. and/or Nintendo 7 Company Ltd., and are protected by Federal copyright law. They may 8 not be disclosed to third parties or copied or duplicated in any form, 9 in whole or in part, without the prior written consent of Nintendo. 10 $Rev: 20405 $ 11 *--------------------------------------------------------------------------- 12 13 14 */ 15 16 #ifndef NN_FND_FND_BUFFERMANAGER_H_ 17 #define NN_FND_FND_BUFFERMANAGER_H_ 18 19 #ifdef __cplusplus 20 21 #include <nn/types.h> 22 23 namespace nn { namespace fnd { 24 25 template <size_t BuddyPageSize, size_t MaxOrder, size_t MaxCache> 26 class BufferManager 27 { 28 public: 29 30 typedef bit64 CacheHandle; 31 32 // Allocate memory for small object 33 void* AllocateSmall(size_t size); 34 void DeallocateSmall(void* p, size_t size); 35 36 private: 37 38 static const size_t BUDDY_PAGE_SIZE = BuddyPageSize; 39 40 class CacheHandleTable 41 { 42 private: 43 44 class Entry : public nn::fnd::IntrusiveLinkedList<Entry>::Item 45 { 46 private: 47 48 CacheHandle m_Handle; 49 nn::fnd::MemoryRange m_MemoryRange; 50 51 public: 52 Entry()53 Entry() : m_Handle(0) {} 54 IsValid()55 bool IsValid() const { return m_Handle != 0; } 56 Invalidate()57 void Invalidate() { this->m_Handle = 0; } 58 Register(CacheHandle handle,nn::fnd::MemoryRange memoryRange)59 void Register(CacheHandle handle, nn::fnd::MemoryRange memoryRange) 60 { 61 NN_TASSERT_(!this->IsValid()); 62 this->m_Handle = handle; 63 this->m_MemoryRange = memoryRange; 64 NN_TASSERT_(this->IsValid()); 65 } 66 Unregister()67 nn::fnd::MemoryRange Unregister() 68 { 69 NN_TASSERT_(IsValid()); 70 Invalidate(); 71 return m_MemoryRange; 72 } 73 HasHandle(CacheHandle handle)74 bool HasHandle(CacheHandle handle) 75 { 76 NN_TASSERT_(handle != 0); 77 return handle == m_Handle; 78 } 79 80 }; 81 82 static const size_t MAX_CACHE = MaxCache; 83 84 Entry m_Entries[MAX_CACHE]; 85 nn::fnd::IntrusiveLinkedList<Entry> m_CacheList; 86 nn::os::CriticalSection m_CriticalSection; 87 CacheHandle m_CurrentHandle; 88 FindEntry(CacheHandle handle)89 Entry* FindEntry(CacheHandle handle) 90 { 91 for (s32 i = 0; i < MAX_CACHE; ++i) 92 { 93 if (m_Entries[i].HasHandle(handle)) 94 { 95 return m_Entries + i; 96 } 97 } 98 return 0; 99 } 100 FindEmptyEntry()101 Entry* FindEmptyEntry() 102 { 103 for (s32 i = 0; i < MAX_CACHE; ++i) 104 { 105 if (!m_Entries[i].IsValid()) 106 { 107 return m_Entries + i; 108 } 109 } 110 return 0; 111 } 112 PublishCacheHandle()113 CacheHandle PublishCacheHandle() 114 { 115 return ++m_CurrentHandle; 116 } 117 118 public: 119 CacheHandleTable()120 CacheHandleTable() : m_CurrentHandle(0) 121 { 122 m_CriticalSection.Initialize(); 123 } 124 Register(CacheHandle * pOut,const nn::fnd::MemoryRange & memoryRange)125 bool Register(CacheHandle* pOut, const nn::fnd::MemoryRange& memoryRange) 126 { 127 nn::os::CriticalSection::ScopedLock lk(m_CriticalSection); 128 129 if (Entry* p = FindEmptyEntry()) 130 { 131 CacheHandle handle = PublishCacheHandle(); 132 p->Register(handle, memoryRange); 133 m_CacheList.PushBack(p); 134 *pOut = handle; 135 return true; 136 } 137 else 138 { 139 return false; 140 } 141 } 142 Unregister(nn::fnd::MemoryRange * pOut,CacheHandle handle)143 bool Unregister(nn::fnd::MemoryRange* pOut, CacheHandle handle) 144 { 145 nn::os::CriticalSection::ScopedLock lk(m_CriticalSection); 146 147 if (Entry* p = FindEntry(handle)) 148 { 149 m_CacheList.Erase(p); 150 *pOut = p->Unregister(); 151 return true; 152 } 153 else 154 { 155 return false; 156 } 157 } 158 UnregisterOldest(nn::fnd::MemoryRange * pOut)159 bool UnregisterOldest(nn::fnd::MemoryRange* pOut) 160 { 161 nn::os::CriticalSection::ScopedLock lk(m_CriticalSection); 162 163 if (Entry* p = m_CacheList.GetFront()) 164 { 165 m_CacheList.Erase(p); 166 *pOut = p->Unregister(); 167 return true; 168 } 169 else 170 { 171 return false; 172 } 173 } 174 175 }; 176 177 178 nn::fnd::BuddyHeapTemplate<BUDDY_PAGE_SIZE, MaxOrder, nn::os::LockPolicy::NoLock > m_Buddy; 179 nn::os::CriticalSection m_CriticalSection; 180 CacheHandleTable m_CacheHandleTable; 181 182 public: 183 Initialize(const nn::fnd::MemoryRange & memoryRange)184 void Initialize(const nn::fnd::MemoryRange& memoryRange) 185 { 186 m_Buddy.Initialize(memoryRange.GetAddress(), memoryRange.GetSize()); 187 m_CriticalSection.Initialize(); 188 } 189 190 // Register the specified buffer as cache. 191 // Return handle to access cache. RegisterAsCache(const nn::fnd::MemoryRange & memoryRange)192 CacheHandle RegisterAsCache(const nn::fnd::MemoryRange& memoryRange) 193 { 194 CacheHandle ret; 195 while (!m_CacheHandleTable.Register(&ret, memoryRange)) 196 { 197 nn::fnd::MemoryRange cachedOut; 198 if (m_CacheHandleTable.UnregisterOldest(&cachedOut)) 199 { 200 this->DeallocateBuffer(cachedOut); 201 } 202 } 203 return ret; 204 } 205 206 // Get the cache region corresponding to the given handle. 207 // In this case, return true, write the region to *pOut, and cancel cache registration. 208 // If necessary, cache could be corrupted and invalidated due to another allocation. In this case, return false. 209 // 210 // The address returned here may not always be the same as the previous one. 211 //Because it is designed to allow movement, it uses the address returned by this function, not the memory registered by RegisterAsCache. 212 // AcquireCache(nn::fnd::MemoryRange * pOut,CacheHandle handle)213 bool AcquireCache(nn::fnd::MemoryRange* pOut, CacheHandle handle) 214 { 215 return m_CacheHandleTable.Unregister(pOut, handle); 216 } 217 218 // Allocate a buffer bigger than requiredSize. 219 // If there is sufficient space, allocate a buffer that is roughly the size of idealSize. AllocateBuffer(size_t requiredSize,size_t idealSize)220 nn::fnd::MemoryRange AllocateBuffer(size_t requiredSize, size_t idealSize) 221 { 222 s32 idealOrder = m_Buddy.GetOrder(idealSize); 223 s32 requiredOrder = m_Buddy.GetOrder(requiredSize); 224 225 nn::os::CriticalSection::ScopedLock lk(m_CriticalSection); 226 227 while (true) 228 { 229 for (s32 order = idealOrder; order >= requiredOrder; --order) 230 { 231 if (void* p = m_Buddy.AllocateByOrder(order)) 232 { 233 NN_TASSERT_(requiredSize <= (BUDDY_PAGE_SIZE << order)); 234 return nn::fnd::MemoryRange(reinterpret_cast<uptr>(p), reinterpret_cast<uptr>(p) + BUDDY_PAGE_SIZE << order); 235 } 236 } 237 nn::fnd::MemoryRange memoryRange; 238 if (m_CacheHandleTable.UnregisterOldest(&memoryRange)) 239 { 240 this->DeallocateBuffer(memoryRange); 241 } 242 else 243 { 244 // What do to in this case? 245 return nn::fnd::MemoryRange(0, 0); 246 } 247 } 248 } 249 AllocateBuffer(size_t size)250 nn::fnd::MemoryRange AllocateBuffer(size_t size) { return AllocateBuffer(size, size); } 251 DeallocateBuffer(const nn::fnd::MemoryRange & memoryRange)252 void DeallocateBuffer(const nn::fnd::MemoryRange& memoryRange) 253 { 254 nn::os::CriticalSection::ScopedLock lk(m_CriticalSection); 255 m_Buddy.Free(reinterpret_cast<void*>(memoryRange.GetAddress()), m_Buddy.GetOrder(memoryRange.GetSize())); 256 } 257 258 }; 259 260 }} 261 262 #endif 263 264 #endif 265