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