1 /*---------------------------------------------------------------------------* 2 Project: Horizon 3 File: fnd_BufferManager.h 4 5 Copyright (C)2009-2012 Nintendo Co., Ltd. All rights reserved. 6 7 These coded instructions, statements, and computer programs contain 8 proprietary information of Nintendo of America Inc. and/or Nintendo 9 Company Ltd., and are protected by Federal copyright law. They may 10 not be disclosed to third parties or copied or duplicated in any form, 11 in whole or in part, without the prior written consent of Nintendo. 12 13 $Rev: 46347 $ 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