1 /*---------------------------------------------------------------------------*
2   Project:     MEM library
3   File:        thread_safe.cpp
4   Programmers: Takano Makoto
5 
6   Copyright 2005 Nintendo. All rights reserved.
7 
8   These coded instructions, statements, and computer programs contain
9   proprietary information of Nintendo of America Inc. and/or Nintendo
10   Company Ltd., and are protected by Federal copyright law.  They may
11   not be disclosed to third parties or copied or duplicated in any form,
12   in whole or in part, without the prior written consent of Nintendo.
13  *---------------------------------------------------------------------------*/
14 
15 /*---------------------------------------------------------------------------*
16   Note:         Demo to confirm thread safety of extended heap
17 
18         1. Creates a thread with medium priority that wakes at fixed intervals using Alarm, and calls Alloc and Free once.
19 
20 
21         2. Creates a check thread with high priority that wakes each time PAD is sampled and dumps the heap state when the START button is pushed.
22 
23 
24         3. The low-priority main thread repeatedly allocates and frees memory.
25         4. The memory size obtained by each thread differs, and thus it is possible to determine which thread has allocated a memory based on the size.
26 
27         5. Ends in an error when THREAD_SAFE_ON define is invalidated, resulting in MEM_HEAP_OPT_THREAD_SAFE option not getting specified.
28 
29  *---------------------------------------------------------------------------*/
30 
31 #include <revolution/mem.h>
32 #include <revolution.h>
33 
34 
35 // Outputs the heap status for non-debug builds.
36 static void     ReportExpHeap( MEMHeapHandle  heap );
37 
38 // Heap's thread safe enable switch
39 #define THREAD_SAFE_ON
40 
41 
42 #define HEAP_BUF_ELEMENT_NUM    (4096 / sizeof(u32))
43 
44 // Memory area for heap
45 static u32              sHeapBuf[ HEAP_BUF_ELEMENT_NUM ];
46 static MEMHeapHandle    hExpHeap;
47 
48 // Thread structure
49 static OSThread         sThread1;
50 static u8               sThreadStack1[4096];
51 static OSThread         sCheckThread;
52 static u8               sCheckThreadStack[4096];
53 
54 // Alarm to switch between threads
55 static OSAlarm          sAlarm;
56 
57 static volatile BOOL    sPadTrig = 0;
58 
59 
60 /*---------------------------------------------------------------------------*
61   Name:         RunThread1
62 
63   Description:  Main function for thread
64                 Repeatedly allocates and frees memory.
65                 If it is Thread 1, it executes once and then suspends.
66 
67   Arguments:    param:	 Parameter arguments
68 
69   Returns:      None
70  *---------------------------------------------------------------------------*/
71 static void*
RunThread1(void * param)72 RunThread1( void* param )
73 {
74 #pragma unused( param )
75 
76     while ( TRUE )
77     {
78         void* pMBlocks;
79 
80         (void)MEMSetGroupIDForExpHeap( hExpHeap, 0x1 );
81 
82         // Obtains memory from the heap
83         pMBlocks = MEMAllocFromExpHeap( hExpHeap, 100 );
84 
85         // Returns memory to the heap.
86         MEMFreeToExpHeap( hExpHeap, pMBlocks );
87 
88         // After executing once, suspends until the next alarm.
89         (void)OSSuspendThread( OSGetCurrentThread() );
90     }
91     return NULL;
92 }
93 
94 /*---------------------------------------------------------------------------*
95   Name:         RunAlarm
96 
97   Description:  Alarm handler
98                 Wakes Thread 1, which has a high priority.
99 
100   Arguments:    alarm:       Unused
101                 context:     Unused
102 
103   Returns:      None
104  *---------------------------------------------------------------------------*/
105 static void
RunAlarm(OSAlarm * alarm,OSContext * context)106 RunAlarm( OSAlarm* alarm, OSContext* context )
107 {
108 #pragma unused( alarm, context )
109     // Wakes Thread 1.
110     (void)OSResumeThread( &sThread1 );
111 }
112 
113 
114 /*---------------------------------------------------------------------------*
115   Name:         CheckThreadMain
116 
117   Description:  Outputs the heap state when the START button is pressed.
118                 Main function for heap state check thread
119 
120   Arguments:    None
121 
122   Returns:      None
123  *---------------------------------------------------------------------------*/
124 static void*
CheckThreadMain(void * param)125 CheckThreadMain( void* param )
126 {
127 #pragma unused( param )
128     while ( TRUE )
129     {
130         // When the START button is pressed, the state of the current heap is dumped.
131         if ( sPadTrig )
132         {
133             sPadTrig = 0;
134             #ifdef _DEBUG
135                 MEMDumpHeap( hExpHeap );
136             #else
137                 ReportExpHeap( hExpHeap );
138             #endif
139         }
140 
141         // After executing once, suspends until the next PAD sampling.
142         (void)OSSuspendThread( OSGetCurrentThread() );
143     }
144 }
145 
146 
147 /*---------------------------------------------------------------------------*
148   Name:         RunPad
149 
150   Description:  Callback function during PAD sampling
151                 Dumps the heap state when the START button is triggered.
152 
153   Arguments:    None
154 
155   Returns:      None
156  *---------------------------------------------------------------------------*/
157 static void
RunPad(void)158 RunPad( void )
159 {
160     static u16 lastPad = 0;
161     PADStatus  pad[ PAD_MAX_CONTROLLERS ];
162     (void)PADRead( pad );
163 
164     if ( PADButtonDown( lastPad, pad[0].button ) & PAD_BUTTON_START )
165     {
166         sPadTrig = 1;
167     }
168     lastPad = pad[0].button;
169 
170     // Wakes the thread for heap status check.
171     (void)OSResumeThread( &sCheckThread );
172 }
173 
174 
175 /*---------------------------------------------------------------------------*
176   Name:         main
177  *---------------------------------------------------------------------------*/
178 void
main(void)179 main( void )
180 {
181     OSInit();
182     VIInit();
183     (void)PADInit();
184 
185     // Creation of the expanded heap
186 #if defined(THREAD_SAFE_ON)
187     // Creates heap using the thread-safe option.
188     hExpHeap = MEMCreateExpHeapEx( sHeapBuf, sizeof sHeapBuf, MEM_HEAP_OPT_THREAD_SAFE );
189 #else
190     hExpHeap = MEMCreateExpHeapEx( sHeapBuf, sizeof sHeapBuf, 0 );
191 #endif
192 
193     // Create Thread 1 (medium priority)
194     (void)OSCreateThread(
195                 &sThread1,
196                 RunThread1,
197                 NULL,                                   // Parameter NULL
198                 sThreadStack1 + sizeof sThreadStack1,
199                 sizeof sThreadStack1,
200                 15,                                     // Higher priority than the default thread
201                 OS_THREAD_ATTR_DETACH );
202 
203     // Create thread for status check (high priority)
204     (void)OSCreateThread(
205                 &sCheckThread,
206                 CheckThreadMain,
207                 NULL,                                   // Parameter NULL
208                 sCheckThreadStack + sizeof sCheckThreadStack,
209                 sizeof sCheckThreadStack,
210                 14,                                     // Priority is highest
211                 OS_THREAD_ATTR_DETACH );
212 
213     // Callback setting during PAD sampling (Used to display heap status)
214     (void)PADSetSamplingCallback( RunPad );
215 
216     // Creates alarm and wakes the thread at fixed intervals using the timer.
217     OSCreateAlarm( &sAlarm );
218 
219     OSSetPeriodicAlarm(
220                 &sAlarm,
221                 OSGetTick() + OSMicrosecondsToTicks(1000),
222                 OSMicrosecondsToTicks(10),
223                 RunAlarm );
224 
225     while ( TRUE )
226     {
227         // Repeatedly allocates and releases memory.
228         void* pMBlocks[ 20 ];
229         u32   i;
230 
231         (void)MEMSetGroupIDForExpHeap( hExpHeap, 0x2 );
232 
233         // Obtains memory from the heap
234         for ( i = 0; i < 20; i++ )
235         {
236             pMBlocks[ i ] = MEMAllocFromExpHeap( hExpHeap, 50 );
237         }
238 
239         for ( i = 0; i < 20; i++ )
240         {
241             // Returns memory to the heap.
242             MEMFreeToExpHeap( hExpHeap, pMBlocks[ i ] );
243         }
244     }
245 }
246 
247 
248 /* ------------------------------------------------------------------------
249  * This is the code for outputting the heap state as a sample for non-debug build.
250  *
251  * Normally, more detailed output can be derived during a debug build by using the MEMDumpHeap function.
252  */
253 #ifndef _DEBUG
254 /*
255     Structure that gets information for reports.
256     Used to specify parameters when calling the visitor function.
257  */
258 typedef struct
259 {
260     u32     size;       // Data size storage region
261     u32     cnt;        // Number of memory blocks used
262 } TReportParam;
263 
264 /*---------------------------------------------------------------------------*
265   Name:         ReportVisitorFunc
266 
267   Description:  A Visitor function for outputting the heap state.
268 
269   Arguments:    memBlock:   	The memory block
270                 heap: 		Handle for expanded heap
271                 userParam: 	call parameter for the visitor function
272                             Gets the total memory size.
273 
274   Returns:      None.
275  *---------------------------------------------------------------------------*/
276 static void
ReportVisitorFunc(void * memBlock,MEMHeapHandle heap,u32 userParam)277 ReportVisitorFunc(
278     void*           memBlock,
279     MEMHeapHandle   heap,
280     u32             userParam
281 )
282 {
283 #pragma unused( heap )
284     TReportParam* pParam   = (TReportParam*)userParam;
285     u16           allocDir = MEMGetAllocDirForMBlockExpHeap( memBlock );
286     u16           groupId  = MEMGetGroupIDForMBlockExpHeap ( memBlock );
287     u32           size     = MEMGetSizeForMBlockExpHeap    ( memBlock );
288 
289     pParam->size += MEMGetSizeForMBlockExpHeap( memBlock );
290     pParam->cnt++;
291 
292     OSReport("    %s %08x: %8d  %3d\n",
293         allocDir == MEM_EXPHEAP_ALLOC_DIR_REAR ? " rear" : "front",
294         (u32)memBlock,
295         size,
296         groupId
297     );
298 }
299 
300 /*---------------------------------------------------------------------------*
301   Name:         ReportExpHeap
302 
303   Description:  Outputs the state of the expanded heap.
304                 The MEMDumpHeap will provide detailed output for debug builds.
305 
306   Arguments:    heap:    heap handle
307 
308   Returns:      None.
309  *---------------------------------------------------------------------------*/
310 static void
ReportExpHeap(MEMHeapHandle heap)311 ReportExpHeap( MEMHeapHandle  heap )
312 {
313     TReportParam param = { 0, 0 };
314 
315     OSReport("[OS Foundation Exp Heap]\n");
316 
317     OSReport("    whole [%p - %p)\n", MEMGetHeapStartAddress( heap ), MEMGetHeapEndAddress( heap ) );
318     OSReport("     attr  address:   size    gid\n");   // Header line
319 
320     MEMVisitAllocatedForExpHeap(
321         heap,                       // The heap handle
322         ReportVisitorFunc,          // visitor function
323         (u32)&param                 // User parameters
324     );
325 
326     OSReport("\n");
327 
328     {
329         u32 usedSize  = param.size;
330         u32 usedCnt   = param.cnt;
331 
332         OSReport("    %d bytes allocated (U:%d)\n",
333                            usedSize, usedCnt);
334     }
335     OSReport("\n");
336 }
337 
338 #endif
339 
340 
341