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