1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     fnd_UnitHeap.h
4 
5   Copyright (C)2009 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: 34147 $
14  *---------------------------------------------------------------------------*/
15 
16 /*! @file
17   @brief    ユニットヒープに関するAPIの宣言
18 
19 */
20 
21 #ifndef NN_FND_FND_UNIT_HEAP_H_
22 #define NN_FND_FND_UNIT_HEAP_H_
23 
24 #include <nn/fnd/fnd_HeapBase.h>
25 #include <nn/util/util_TypeTraits.h>
26 #include <nn/util/util_StaticAssert.h>
27 #include <nn/fnd/fnd_Allocator.h>
28 #include <nn/Assert.h>
29 #include <nn/os.h>
30 
31 
32 #ifdef __cplusplus
33 
34 namespace nn { namespace fnd {
35 
36 /*!
37     @brief    ユニットヒープのベースクラスです。
38 
39               このクラスのインスタンスを直接作成することはできません。
40  */
41 class UnitHeapBase : public HeapBase
42 {
43 public:
44 
45     /*!
46         @brief 指定したユニットサイズとアラインメントのブロックを指定数を管理できるだけのヒープメモリのサイズを取得します。
47 
48         @param[in] unit ユニットのサイズを指定します。
49         @param[in] numUnit 確保したいブロックの数。
50         @param[in] alignment ユニットのアラインメント。
51      */
52     static size_t GetRequiredHeapSize(size_t unit, size_t numUnit, s32 alignment = DEFAULT_ALIGNMENT);
53 
54 protected:
55 
UnitHeapBase()56     UnitHeapBase() : m_FreeNode(0) {}
57 
58     UnitHeapBase(size_t unit, uptr addr, size_t size, s32 alignment = DEFAULT_ALIGNMENT, bit32 option = 0) : m_FreeNode(0) { Initialize(unit, addr, size, alignment, option); }
59 
60     template <class MemoryBlock>
61     explicit UnitHeapBase(size_t unit, const MemoryBlock& block, s32 alignment = DEFAULT_ALIGNMENT, bit32 option = 0) : m_FreeNode(0) { Initialize(unit, block.GetAddress(), block.GetSize(), alignment, option); }
62 
63     void Initialize(size_t unit, uptr addr, size_t size, s32 alignment = DEFAULT_ALIGNMENT, bit32 option = 0);
64 
65     void Invalidate();
66 
67     void Finalize();
68 
69     virtual ~UnitHeapBase();
70 
71     void* Allocate();
72 
73     void Free(void* p);
74 
75     virtual void FreeV(void* p);
76 
GetUnitSize()77     size_t GetUnitSize() const { return m_Unit; }
78 
GetAllocatableCount()79     size_t GetAllocatableCount() const { return m_Size / m_Unit - m_Count; }
80 
GetAllocatedCount()81     size_t GetAllocatedCount() const { return m_Count; }
82 
GetStartAddress()83     virtual void* GetStartAddress() const { return reinterpret_cast<void*>(m_Addr); }
84 
GetTotalSize()85     virtual size_t GetTotalSize() const { return m_Size; }
86 
87     virtual void Dump() const;
88 
89     virtual bool HasAddress(const void* addr) const;
90 
91 private:
92 
93     struct Node
94     {
95         Node* next;
96     };
97 
98     size_t m_Unit;
99     uptr m_Addr;
100     size_t m_Size;
101     Node* m_FreeNode;
102     s32 m_Alignment;
103     size_t m_Count;
104 
105 };
106 
Finalize()107 inline void UnitHeapBase::Finalize()
108 {
109     if (m_FreeNode == 0)
110     {
111         return;
112     }
113     NN_TASSERT_(m_Count == 0);
114     this->m_FreeNode = 0;
115 }
116 
~UnitHeapBase()117 inline UnitHeapBase::~UnitHeapBase()
118 {
119     Finalize();
120 }
121 
Invalidate()122 inline void UnitHeapBase::Invalidate()
123 {
124     this->m_FreeNode = 0;
125 }
126 
Allocate()127 inline void* UnitHeapBase::Allocate()
128 {
129     void* ret = reinterpret_cast<void*&>(m_FreeNode);
130     if (ret)
131     {
132         this->m_FreeNode = m_FreeNode->next;
133         ++this->m_Count;
134 
135         // フィルオプション
136         DebugFillMemory(reinterpret_cast<uptr>(ret), m_Unit, HEAP_FILL_TYPE_ALLOC);
137         FillMemoryZero(reinterpret_cast<uptr>(ret), m_Unit);
138     }
139 
140     return ret;
141 }
142 
HasAddress(const void * addr)143 inline bool UnitHeapBase::HasAddress(const void* addr) const
144 {
145     return m_Addr <= reinterpret_cast<uptr>(addr)
146         && reinterpret_cast<uptr>(addr) < (m_Addr+m_Size);
147 }
148 
Free(void * p)149 inline void UnitHeapBase::Free(void* p)
150 {
151     NN_TASSERT_(HasAddress(p));
152 
153     DebugFillMemory(reinterpret_cast<uptr>(p), m_Unit, HEAP_FILL_TYPE_FREE);
154 
155     reinterpret_cast<Node*&>(p)->next = m_FreeNode;
156     this->m_FreeNode = reinterpret_cast<Node*&>(p);
157     --this->m_Count;
158 }
159 
FreeV(void * p)160 inline void UnitHeapBase::FreeV(void* p)
161 {
162     Free(p);
163 }
164 
165 /*!
166     @brief    ユニットヒープのためのクラステンプレートです。
167 
168               このクラステンプレートを実体化したクラスでは、
169               指定したロックポリシーによって、排他制御が行われます。
170               ロックポリシーは @ref nn::os::LockPolicy クラスの内部で宣言される
171               クラスもしくはクラステンプレートを実体化したクラスを指定することができます。
172               上述のクラス以外を LockPolicy に指定した場合の動作・互換性は保障されません。
173 
174               ロックされるのは、ヒープ内の操作に対してのみであり、
175               ヒープの階層化に対する操作などを行う場合は、適切な排他処理などが別途必要となります。
176 
177     @tparam   LockPolicy ヒープに対する操作を行うときのロックポリシーを指定します。
178 */
179 template <class LockPolicy>
180 class UnitHeapTemplate : public UnitHeapBase, private LockPolicy::LockObject
181 {
182 private:
183     typedef UnitHeapBase Base;
184     typedef typename LockPolicy::LockObject LockObject;
185     typedef typename LockPolicy::ScopedLock ScopedLock;
186 public:
187 
188     /*!
189         @brief 指定したユニットサイズとアラインメントのブロックを指定数を管理できるだけのヒープメモリのサイズを取得します。
190 
191         @param[in] unit ユニットのサイズを指定します。
192         @param[in] numUnit 確保したいブロックの数。
193         @param[in] alignment ユニットのアラインメント。
194      */
195     static size_t GetRequiredHeapSize(size_t unit, size_t numUnit, s32 alignment = DEFAULT_ALIGNMENT)
196     {
197         return Base::GetRequiredHeapSize(unit, numUnit, alignment);
198     }
199 
200     /*!
201         @brief コンストラクタです。初期化は行いません。
202      */
UnitHeapTemplate()203     UnitHeapTemplate() {}
204 
205     /*!
206         @brief コンストラクタです。指定したパラメータを用いてユニットヒープの初期化を行います。
207 
208         @param[in] unit      ユニットのサイズを指定します。
209         @param[in] addr      ユニットヒープで使うメモリの先頭アドレスを指定します。
210         @param[in] size      ユニットヒープで使うメモリのサイズを指定します。
211         @param[in] alignment ユニットのアラインメントを指定します。
212         @param[in] option    オプションを指定します。未実装です。
213      */
214     UnitHeapTemplate(size_t unit, uptr addr, size_t size, s32 alignment = DEFAULT_ALIGNMENT, bit32 option = 0)
215     {
216         Initialize(unit, addr, size, alignment, option);
217     }
218 
219     /*!
220         @brief コンストラクタです。指定したパラメータを用いてユニットヒープの初期化を行います。
221 
222         @param[in] unit      ユニットのサイズを指定します。
223         @param[in] block    フレームヒープに割り当てるメモリブロックを指定します。
224         @param[in] alignment ユニットのアラインメントを指定します。
225         @param[in] option    オプションを指定します。未実装です。
226      */
227     template <class MemoryBlock>
228     explicit UnitHeapTemplate(size_t unit, const MemoryBlock& block, s32 alignment = DEFAULT_ALIGNMENT, bit32 option = 0)
229     {
230         Initialize(unit, block.GetAddress(), block.GetSize(), alignment, option);
231     }
232 
233     /*!
234         @brief  ヒープ内にユニットヒープを作成します。
235 
236                 この関数で作成したヒープは @ref HeapBase::Destroy により破壊してください。
237                 ExpHeap自身をaddrとsizeで指定した領域内に作成します。
238                 このため、指定した領域の大きさから sizeof(ExpHeap) の大きさだけ少ない領域をヒープとして割り当てます。
239 
240                 placementによりExpHeapオブジェクト自身の配置場所を指定します。
241                 placementに HEAP_INFOPLACEMENT_HEAD を指定すると領域の先頭(addr から addr+sizeof(ExpHeap) までの領域)にExpHeapオブジェクト自身を配置します。
242                 placementに HEAP_INFOPLACEMENT_TAIL を指定すると領域の末尾(addr+size-sizeof(ExpHeap) から addr+size までの領域)にExpHeapオブジェクト自身を配置します。
243 
244         @param[in] parent    addr を領域内に持つ親のヒープを指定します。
245         @param[in] unit      ユニットのサイズを指定します。
246         @param[in] addr      ユニットヒープで使うメモリの先頭アドレスを指定します。
247         @param[in] size      ユニットヒープで使うメモリのサイズを指定します。
248         @param[in] alignment ユニットのアラインメントを指定します。
249         @param[in] option    オプションを指定します。未実装です。
250         @param[in] placement 管理領域(UnitHeapオブジェクト)の配置場所を指定します。
251     */
252     static UnitHeapTemplate* Create(HeapBase* parent, size_t unit, void* addr, size_t size, s32 alignment = DEFAULT_ALIGNMENT, bit32 option = 0, bit32 placement = HEAP_INFOPLACEMENT_HEAD);
253 
254     /*!
255         @brief 指定したパラメータを用いてユニットヒープの初期化を行います。
256                初期化無しのコンストラクタを使ってオブジェクトを生成した場合に用います。
257 
258         @param[in] unit      ユニットのサイズを指定します。
259         @param[in] addr      ユニットヒープで使うメモリの先頭アドレスを指定します。このアドレスは alignment パラメータの値でアラインメントされている必要があります。
260         @param[in] size      ユニットヒープで使うメモリのサイズを指定します。
261         @param[in] alignment ユニットのアラインメントを指定します。
262         @param[in] option    オプションを指定します。未実装です。
263      */
264     void Initialize(size_t unit, uptr addr, size_t size, s32 alignment = DEFAULT_ALIGNMENT, bit32 option = 0)
265     {
266         Base::Initialize(unit, addr, size, alignment, option);
267         LockObject::Initialize();
268     }
269 
270     /*!
271         @brief ヒープオブジェクトを無効化します。
272                この関数を呼んだ後は @ref Finalize 以外の操作をヒープに対して行うことができなくなります。
273      */
Invalidate()274     void Invalidate() { Base::Invalidate(); }
275 
276     /*!
277         @brief ヒープオブジェクトの終了処理をします。
278                この関数はデストラクタの中で暗黙的に呼ばれるため、明示的に呼ぶ必要はありません。
279 
280                ヒープの中に未解放のブロックが存在した場合、アプリケーションは強制終了します。
281                意図して未解放のままこの関数を呼ぶ際は、その前に、明示的に @ref Invalidate 関数を呼んでください。
282      */
Finalize()283     void Finalize()
284     {
285         LockObject::Finalize();
286         Base::Finalize();
287     }
288 
289     /*!
290         @brief デストラクタです。
291 
292                破棄時の注意は @ref Finalize を参照してください。
293     */
~UnitHeapTemplate()294     virtual ~UnitHeapTemplate() {}
295 
296     /*!
297         @brief ヒープからユニットに基づいてメモリを確保します。
298 
299         @return 確保に成功した場合は、メモリへのポインタを返します。
300                 確保に失敗した場合は、0 を返します。
301     */
Allocate()302     void* Allocate()
303     {
304         ScopedLock lk(*this);
305         return Base::Allocate();
306     }
307 
308     /*!
309         @brief このヒープから確保したメモリを解放します。
310 
311         @param[in] p メモリへのポインタを指定します。
312     */
Free(void * p)313     void Free(void* p)
314     {
315         ScopedLock lk(*this);
316         Base::Free(p);
317     }
318 
319     /*!
320         @brief このヒープから確保したメモリを解放します。
321 
322                仮想メンバ関数であるため、HeapBaseから解放できます。
323 
324         @param[in] p メモリへのポインタを指定します。
325     */
FreeV(void * p)326     virtual void FreeV(void* p) { Free(p); }
327 
328 
329     /*!
330         @brief このユニットヒープのユニットサイズを取得します。
331 
332         @return ユニットサイズを返します。
333     */
GetUnitSize()334     size_t GetUnitSize() const { return Base::GetUnitSize(); }
335 
336     /*!
337         @brief 現在このヒープから確保できるユニットの数を取得します。
338 
339         @return 確保できるユニットの数を返します。
340     */
GetAllocatableCount()341     size_t GetAllocatableCount() const
342     {
343         ScopedLock lk(*this);
344         return Base::GetAllocatableCount();
345     }
346 
347     /*!
348         @brief 現在このヒープから確保されているユニットの数を取得します。
349 
350         @return 確保されているユニットの数を返します。
351     */
GetAllocatedCount()352     size_t GetAllocatedCount() const {
353         ScopedLock lk(*this);
354         return Base::GetAllocatedCount();
355     }
356 
357     /*!
358         @brief ユニットヒープのメモリ領域の開始アドレスを取得します。
359 
360         @return ユニットヒープのメモリ領域の開始アドレスをを返します。
361      */
GetStartAddress()362     virtual void* GetStartAddress() const { return Base::GetStartAddress(); }
363 
364     /*!
365         @brief ユニットヒープに割り当てられているメモリサイズを取得します。
366 
367         @return ユニットヒープに割り当てられているメモリサイズを返します。
368      */
GetTotalSize()369     virtual size_t GetTotalSize() const { return Base::GetTotalSize(); }
370 
371     /*!
372         @brief ヒープ内部の情報を表示します。(デバッグ用)
373 
374         @return 無し。
375      */
Dump()376     virtual void Dump() const
377     {
378         ScopedLock lk(*this);
379         Base::Dump();
380     }
381 
382     /*!
383         @brief  指定したアドレスがヒープに含まれているか調べます。
384 
385         @param[in]  addr    調べたいアドレスを指定します。
386 
387         @return ヒープに含まれていれば true を返し、含まれていなければ false を返します。
388     */
HasAddress(const void * addr)389     virtual bool HasAddress(const void* addr) const { return Base::HasAddress(addr); }
390 
391     class Allocator;
392 
393 };
394 
395 /*!
396     @brief ユニットヒープ用のアロケータクラスです。
397  */
398 template <class LockPolicy>
399 class UnitHeapTemplate<LockPolicy>::Allocator : public IAllocator
400 {
401 public:
402 
403     /*!
404         @brief ユニットヒープを指定してアロケータを初期化するコンストラクタです。
405 
406         @param[in] ユニットヒープを指定します。
407      */
Allocator(UnitHeapTemplate<LockPolicy> & heap)408     Allocator(UnitHeapTemplate<LockPolicy>& heap) : m_Heap(&heap) {}
409 
410     /*!
411         @brief 初期化をしないコンストラクタです。
412      */
Allocator()413     Allocator() : m_Heap(0) {}
414 
415     /*!
416         @brief 初期化されていないアロケータを初期化します。
417 
418         @param[in] ユニットヒープを指定します。
419      */
Initialize(UnitHeapTemplate<LockPolicy> & heap)420     void Initialize(UnitHeapTemplate<LockPolicy>& heap) { m_Heap = &heap; }
421 
422     /*!
423         @brief アロケータにセットされているユニットヒープを返します。
424         @return アロケータにセットされているユニットヒープ
425      */
GetHeap()426     UnitHeapTemplate<LockPolicy>* GetHeap() { return m_Heap; }
427 
428     /*!
429         @brief アロケータにセットされているユニットヒープを返します。
430         @return アロケータにセットされているユニットヒープ
431      */
GetHeap()432     const UnitHeapTemplate<LockPolicy>* GetHeap() const { return m_Heap; }
433 
434     /*!
435         @brief 指定したサイズとアラインメントでメモリ領域を確保します。
436 
437         @param[in] size 確保するメモリのサイズ
438         @param[in] alignment 確保するメモリのアラインメント
439 
440         @return 確保したメモリ領域の先頭へのポインタ
441     */
442     virtual void* Allocate(size_t size, s32 alignment);
443 
444     /*!
445         @brief メモリ領域を解放します。
446         @param[in] p 確保されているメモリ領域の先頭へのポインタ
447     */
448     virtual void Free(void* p);
449 
450 private:
451     UnitHeapTemplate<LockPolicy>* m_Heap;
452 };
453 
454 template <class LockPolicy>
Allocate(size_t size,s32 alignment)455 inline void* UnitHeapTemplate<LockPolicy>::Allocator::Allocate(size_t size, s32 alignment)
456 {
457     if (size == m_Heap->m_Unit && alignment >= 0 && m_Heap->m_Alignment % alignment == 0)
458     {
459         return m_Heap->Allocate();
460     }
461     else
462     {
463         return 0;
464     }
465 }
466 
467 template <class LockPolicy>
Free(void * p)468 inline void UnitHeapTemplate<LockPolicy>::Allocator::Free(void* p)
469 {
470     m_Heap->Free(p);
471 }
472 
473 /*!
474     @brief ロック操作をしないスレッドアンセーフなユニットヒープを表す型です。
475 */
476 typedef UnitHeapTemplate<nn::os::LockPolicy::NoLock> UnitHeap;
477 
478 /*!
479     @brief ヒープごとに用意されるクリティカルセクションで保護された、スレッドセーフなユニットヒープを表す型です。
480 */
481 typedef UnitHeapTemplate<nn::os::LockPolicy::Object<nn::os::CriticalSection> > ThreadSafeUnitHeap;
482 
483 template <class LockPolicy>
Create(HeapBase * parent,size_t unit,void * addr,size_t size,s32 alignment,bit32 option,bit32 placement)484 UnitHeapTemplate<LockPolicy>* UnitHeapTemplate<LockPolicy>::Create(HeapBase* parent, size_t unit, void* addr, size_t size, s32 alignment, bit32 option, bit32 placement)
485 {
486     UnitHeapTemplate* heap;
487     if ( parent->FindHeap(addr) != parent ) return 0;
488 
489     if ( placement == HEAP_INFOPLACEMENT_HEAD )
490     {
491         heap = new (addr) UnitHeapTemplate(unit, reinterpret_cast<uptr>(addr)+sizeof(UnitHeapTemplate), static_cast<size_t>(size - sizeof(UnitHeapTemplate)), alignment, option);
492     }
493     else if ( placement == HEAP_INFOPLACEMENT_TAIL )
494     {
495         void* placeaddr = reinterpret_cast<void*>(reinterpret_cast<uptr>(addr)+static_cast<size_t>(size - sizeof(UnitHeapTemplate)));
496         heap = new (placeaddr) UnitHeapTemplate(unit, reinterpret_cast<uptr>(addr), static_cast<size_t>(size - sizeof(UnitHeapTemplate)), alignment, option);
497     }
498     else
499     {
500         return 0;
501     }
502 
503     heap->SetParent(parent);
504     return heap;
505 }
506 
507 }}
508 
509 #endif
510 
511 #endif
512