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