1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     fslow_SafeHandle.h
4 
5   Copyright 2010 Nintendo. All rights reserved.
6   These coded instructions, statements, and computer programs contain
7   proprietary information of Nintendo of America Inc. and/or Nintendo
8   Company Ltd., and are protected by Federal copyright law. They may
9   not be disclosed to third parties or copied or duplicated in any form,
10   in whole or in part, without the prior written consent of Nintendo.
11   $Rev: 19964 $
12  *---------------------------------------------------------------------------
13 
14 */
15 
16 #ifndef NN_FSLOW_FSLOW_SAFEHANDLE_H_
17 #define NN_FSLOW_FSLOW_SAFEHANDLE_H_
18 
19 #include <nn.h>
20 #include <nn/os/os_WaitableCounter.h>
21 #include <nn/util/util_NonCopyable.h>
22 #include <nn/WithInitialize.h>
23 #include <nn/os/os_CriticalSection.h>
24 
25 namespace nn { namespace fslow {
26 
27 /*---------------------------------------------------------------------------*
28  * Use as ReaderWriterLock
29  * Pre-initialization   : 0
30  * - After-initialization   : 1
31  *    - ReaderLock   : (positive) increment, (negative) do not unlock
32  *    - ReaderUnlock : Absolute value decrement
33  *    - WriterLock   : (positive) reverse sign and wait until -1
34  *---------------------------------------------------------------------------*/
35 class SafeReferenceCounter : private nn::util::NonCopyable<SafeReferenceCounter>
36 {
37 private:
38     nn::os::WaitableCounter m_Counter;
39 
40     struct IncrementIfNonPositive
41     {
operatorIncrementIfNonPositive42         bool operator()(s32& x) const
43         {
44             if(x < 0)
45             {
46                 return false;
47             }
48             else
49             {
50                 ++x;
51                 return true;
52             }
53         }
54     };
55 
56     struct AbsDecrement
57     {
operatorAbsDecrement58         bool operator()(s32& x) const
59         {
60             if(x >= 0)
61             {
62                 --x;
63             }
64             else
65             {
66                 ++x;
67             }
68             return true;
69         }
70     };
71 
72     struct Negate
73     {
operatorNegate74         bool operator()(s32& x) const
75         {
76             NN_TASSERT_(x > 0);
77             x = -x;
78             return true;
79         }
80     };
81 
82 public:
83 
SafeReferenceCounter()84     SafeReferenceCounter()
85     {
86         *m_Counter = 0;
87     }
88 
SafeReferenceCounter(nn::WithInitialize)89     SafeReferenceCounter(nn::WithInitialize)
90     {
91         *m_Counter = 1;
92     }
93 
Initialize()94     void Initialize()
95     {
96         NN_TASSERT_(!IsInitialized());
97         *m_Counter = 1;
98     }
99 
Increment()100     bool Increment()
101     {
102         // Remove ReaderLock
103         NN_TASSERT_(IsInitialized());
104         IncrementIfNonPositive f;
105         return m_Counter->AtomicUpdateConditional(f);
106     }
107 
Decrement()108     void Decrement()
109     {
110         // Do ReaderUnlock
111         NN_TASSERT_(IsInitialized());
112         AbsDecrement f;
113         m_Counter->AtomicUpdateConditional(f);
114         if (*m_Counter == -1)
115         {
116             m_Counter.SignalAll();
117         }
118     }
119 
IsInitialized()120     bool IsInitialized() const
121     {
122         return *m_Counter != 0;
123     }
124 
IsValid()125     bool IsValid() const
126     {
127         NN_TASSERT_(IsInitialized());
128         return *m_Counter > 0;
129     }
130 
IsLocked()131     bool IsLocked() const
132     {
133         NN_TASSERT_(IsInitialized());
134         return (*m_Counter < -1) || (1 < *m_Counter);
135     }
136 
PrepareFinalization()137     void PrepareFinalization()
138     {
139         // Prepare WriterLock (Disables removal of ReaderLock)
140         NN_TASSERT_(IsValid());
141         Negate f;
142         m_Counter->AtomicUpdateConditional(f);
143     }
144 
IsFinalizationPrepared()145     bool IsFinalizationPrepared() const
146     {
147         NN_TASSERT_(IsInitialized());
148         return *m_Counter < 0;
149     }
150 
TryFinalize()151     bool TryFinalize()
152     {
153         return m_Counter->CompareAndSwap(-1, 0) == 0;
154     }
155 
Finalize()156     void Finalize()
157     {
158         // Wait until WriterLock is removed, and then terminate
159         if(IsInitialized())
160         {
161             if(IsValid())
162             {
163                 Negate f;
164                 m_Counter->AtomicUpdateConditional(f);
165             }
166             m_Counter.WaitIfLessThan(-1);
167             *m_Counter = 0;
168         }
169     }
170 };
171 
172 
173 /*---------------------------------------------------------------------------*
174  *  Handle
175  *---------------------------------------------------------------------------*/
176 class SafeHandleBase
177 {
178 protected:
179     bit32   m_Data[2];
180 
181 public:
SafeHandleBase()182     SafeHandleBase() { Invalidate(); }
183 
Invalidate()184     void    Invalidate() { m_Data[0] = m_Data[1] = 0; }
185 
IsValid()186     bool    IsValid() const { return (m_Data[0] != 0) || (m_Data[1] != 0); }
187 
188     operator bool() const { return IsValid(); }
189     bool    operator!() const { return !IsValid(); }
190     bool    operator==(const SafeHandleBase& rhs) const { return (m_Data[0] == rhs.m_Data[0]) && (m_Data[1] == rhs.m_Data[1]); }
191     bool    operator!=(const SafeHandleBase& rhs) const { return (m_Data[0] != rhs.m_Data[0]) || (m_Data[1] != rhs.m_Data[1]); }
192 };
193 
194 /*---------------------------------------------------------------------------*
195  *  Archive handle
196  *    - Permanent handle: <w0> object address, <w1> always 0
197  *    - Non-permanent handle: <w0[0:7]> fixed value, <w0[8:31] w1[0:31]> incrementable unique value
198  *---------------------------------------------------------------------------*/
199 class SafeHandle : public SafeHandleBase
200 {
201 public:
SafeHandle()202     SafeHandle() {}
SafeHandle(bit8 index)203     SafeHandle(bit8 index) { Initialize(index); }
SafeHandle(void * pObject)204     SafeHandle(void* pObject) { Initialize(pObject); }
205 
206     // Initialization as permanent handle
Initialize(void * pObject)207     void Initialize(void* pObject)
208     {
209         NN_TASSERT_(!IsValid());
210         m_Data[0] = reinterpret_cast<bit32>(pObject);
211     }
212 
213     // Initialization as non-permanent handle
Initialize(bit8 fixedValue)214     void Initialize(bit8 fixedValue)
215     {
216         NN_TASSERT_(!IsValid());
217         m_Data[0] = fixedValue;
218         m_Data[1] = 1;      // Distinguish from permanent handle
219     }
220 
Finalize()221     void Finalize() { Invalidate(); }
222 
IsPermanent()223     bool    IsPermanent() const { return m_Data[1] == 0; }
224 
GetObject()225     void* GetObject() const
226     {
227         NN_TASSERT_(IsPermanent());
228         return reinterpret_cast<void*>(m_Data[0]);
229     }
230 
GetFixedValue()231     bit8 GetFixedValue() const
232     {
233         NN_TASSERT_(!IsPermanent());
234         return static_cast<bit8>(m_Data[0] & 0xFF);
235     }
236 
237     SafeHandle& operator++();
238 };
239 
240 // invalid handle
241 typedef const SafeHandle  InvalidSafeHandle;
242 
243 
244 /*---------------------------------------------------------------------------*
245  *  Archive entry
246  *---------------------------------------------------------------------------*/
247 class SafeHandleTableEntry
248 {
249 public:
250     static const s32    INVALID_INDEX   = -1;
251     static const s32    MAX_INDEX       = 255;
252 
SafeHandleTableEntry()253     SafeHandleTableEntry() :
254         m_pObject(0),
255         m_DependencyIndex(0) {}
~SafeHandleTableEntry()256     ~SafeHandleTableEntry() {}
257 
258     void        Initialize(s32 index);
259     void        Finalize();
260     SafeHandle  Activate(void* pObject, s32 dependencyIndex=INVALID_INDEX);
261     void        Deactivate();
262 
263     bool        Lock();
264     void        Unlock();
265     void        LockForUnregister();
GetObject()266     void*       GetObject() { NN_TASSERT_(IsLocked()); return m_pObject; }
267 
IsInitialized()268     bool        IsInitialized() const { return m_Handle.IsValid(); }
IsActivated()269     bool        IsActivated() const { return m_AccessLockCounter.IsInitialized(); }
HasHandle(const SafeHandle & handle)270     bool        HasHandle(const SafeHandle& handle) const { return IsInitialized() && (handle == m_Handle); }
IsLocked()271     bool        IsLocked() const { return m_AccessLockCounter.IsValid(); }
272 
GetHandle()273     SafeHandle  GetHandle() const { return IsActivated() ? m_Handle : InvalidSafeHandle(); }
HasDependencyIndex()274     bool        HasDependencyIndex() const { return m_DependencyIndex != m_Handle.GetFixedValue(); }
GetDependencyIndex()275     s32         GetDependencyIndex() const { return HasDependencyIndex() ? m_DependencyIndex : INVALID_INDEX; }
276 
277 private:
278     SafeHandle              m_Handle;
279     void*                   m_pObject;
280     SafeReferenceCounter    m_AccessLockCounter;
281     bit8                    m_DependencyIndex;  // Own index value == no dependency
282     NN_PADDING3;
283 };
284 
285 /*---------------------------------------------------------------------------*
286  *  Archive handle table
287  *---------------------------------------------------------------------------*/
288 class SafeHandleTable
289 {
290 public:
SafeHandleTable()291     SafeHandleTable() : m_NumEntry(0), m_pEntryBuffer(0) {}
~SafeHandleTable()292     ~SafeHandleTable() {}
293 
294     void        Initialize(SafeHandleTableEntry* pEntryBuffer, s32 NumEntry);
295     void        Finalize();
296 
IsInitialized()297     bool        IsInitialized() const { return m_pEntryBuffer != 0; }
298 
299     SafeHandle  Register(void* pObject, SafeHandle dependencyHandle=InvalidSafeHandle());
300     void        Unregister(SafeHandle handle);
301 
LockHandle(SafeHandle handle)302     void* LockHandle(SafeHandle handle)
303     {
304         SafeHandleTableEntry* entry = FindEntry(handle);
305         return LockEntry(entry, handle) ? entry->GetObject() : 0;
306     }
307 
UnlockHandle(SafeHandle handle)308     void UnlockHandle(SafeHandle handle) { return UnlockEntry(FindEntry(handle)); }
309 
310     friend class Accessor;
311     class Accessor
312     {
313     private:
314         SafeHandleTableEntry* m_pEntry;
315 
316     protected:
317 
GetPointer()318         void* GetPointer() const { return m_pEntry ? m_pEntry->GetObject() : 0; }
319 
320     public:
Accessor(SafeHandleTable & table,SafeHandle handle)321         Accessor(SafeHandleTable& table, SafeHandle handle)
322         {
323             SafeHandleTableEntry* entry = table.FindEntry(handle);
324             this->m_pEntry = LockEntry(entry, handle) ? entry : 0;
325         }
326 
~Accessor()327         ~Accessor()
328         {
329             if (m_pEntry)
330             {
331                 UnlockEntry(m_pEntry);
332             }
333         }
334 
335         operator bool() const { return GetPointer() != 0; }
336         bool operator!() const { return GetPointer() == 0; }
337     };
338 
339 private:
340 
341     static bool LockEntry(SafeHandleTableEntry* pEntry, SafeHandle handle);
342     static void UnlockEntry(SafeHandleTableEntry* pEntry);
343     SafeHandleTableEntry* FindEntry(SafeHandle handle) const;
344 
GetEntry(s32 index)345     SafeHandleTableEntry* GetEntry(s32 index) const
346     {
347         return (index < m_NumEntry) ? (m_pEntryBuffer + index) : 0;
348     }
349 
350     s32                     m_NumEntry;
351     SafeHandleTableEntry*   m_pEntryBuffer;
352     mutable nn::os::CriticalSection m_CriticalSection;
353 
354     SafeHandleTableEntry*   FindFreeEntry() const;
355     void                    UnregisterRecursive(s32 index);
356 };
357 
358 //--- Template
359 template <class TObject, s32 TNumEntry>
360 class SafeArchiveHandleTable
361 {
362 public:
363     typedef SafeHandle              Handle;
364     typedef SafeHandleTableEntry    Entry;
365     typedef SafeHandleTable         Table;
366 
SafeArchiveHandleTable()367     SafeArchiveHandleTable() { m_Table.Initialize(m_Entry, TNumEntry); }
368 
~SafeArchiveHandleTable()369     ~SafeArchiveHandleTable() { m_Table.Finalize(); }
370 
371     Handle Register(TObject* pObject, Handle dependencyHandle=InvalidSafeHandle())
372     {
373         return m_Table.Register(pObject, dependencyHandle);
374     }
375 
Unregister(SafeHandle handle)376     void Unregister(SafeHandle handle)
377     {
378         m_Table.Unregister(handle);
379     }
380 
LockHandle(SafeHandle handle)381     TObject* LockHandle(SafeHandle handle)
382     {
383         return reinterpret_cast<TObject*>(m_Table.LockHandle(handle));
384     }
385 
UnlockHandle(SafeHandle handle)386     void UnlockHandle(SafeHandle handle)
387     {
388         m_Table.UnlockHandle(handle);
389     }
390 
HasHandle(SafeHandle handle)391     bool HasHandle(SafeHandle handle)
392     {
393         return (m_Table.FindEntry(handle) != 0);
394     }
395 
396 private:
397     Entry   m_Entry[TNumEntry];   // May end up allocating separately
398     Table   m_Table;
399 
FindEntry(Handle handle)400     Entry*  FindEntry(Handle handle) const { return m_Table.FindEntry(handle); }
401 
402 public:
403 
404     class Accessor : public SafeHandleTable::Accessor
405     {
406     public:
Accessor(SafeArchiveHandleTable & table,Handle handle)407         Accessor(SafeArchiveHandleTable& table, Handle handle) : SafeHandleTable::Accessor(table.m_Table, handle) {}
408 
GetPointer()409         TObject* GetPointer() { return static_cast<TObject*>(SafeHandleTable::Accessor::GetPointer()); }
410 
411         TObject* operator->() { return GetPointer(); }
412         TObject& operator*() { return *GetPointer(); }
413     };
414 
415 };
416 
417 
418 }}
419 
420 #endif
421