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