/*---------------------------------------------------------------------------* Project: MEM library File: thread_safe.cpp Programmers: Takano Makoto Copyright 2005 Nintendo. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. *---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------* Note: Demo to confirm thread safety of extended heap 1. Creates a thread with medium priority that wakes at fixed intervals using Alarm, and calls Alloc and Free once. 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. 3. The low-priority main thread repeatedly allocates and frees memory. 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. 5. Ends in an error when THREAD_SAFE_ON define is invalidated, resulting in MEM_HEAP_OPT_THREAD_SAFE option not getting specified. *---------------------------------------------------------------------------*/ #include #include // Outputs the heap status for non-debug builds. static void ReportExpHeap( MEMHeapHandle heap ); // Heap's thread safe enable switch #define THREAD_SAFE_ON #define HEAP_BUF_ELEMENT_NUM (4096 / sizeof(u32)) // Memory area for heap static u32 sHeapBuf[ HEAP_BUF_ELEMENT_NUM ]; static MEMHeapHandle hExpHeap; // Thread structure static OSThread sThread1; static u8 sThreadStack1[4096]; static OSThread sCheckThread; static u8 sCheckThreadStack[4096]; // Alarm to switch between threads static OSAlarm sAlarm; static volatile BOOL sPadTrig = 0; /*---------------------------------------------------------------------------* Name: RunThread1 Description: Main function for thread Repeatedly allocates and frees memory. If it is Thread 1, it executes once and then suspends. Arguments: param: Parameter arguments Returns: None *---------------------------------------------------------------------------*/ static void* RunThread1( void* param ) { #pragma unused( param ) while ( TRUE ) { void* pMBlocks; (void)MEMSetGroupIDForExpHeap( hExpHeap, 0x1 ); // Obtains memory from the heap pMBlocks = MEMAllocFromExpHeap( hExpHeap, 100 ); // Returns memory to the heap. MEMFreeToExpHeap( hExpHeap, pMBlocks ); // After executing once, suspends until the next alarm. (void)OSSuspendThread( OSGetCurrentThread() ); } return NULL; } /*---------------------------------------------------------------------------* Name: RunAlarm Description: Alarm handler Wakes Thread 1, which has a high priority. Arguments: alarm: Unused context: Unused Returns: None *---------------------------------------------------------------------------*/ static void RunAlarm( OSAlarm* alarm, OSContext* context ) { #pragma unused( alarm, context ) // Wakes Thread 1. (void)OSResumeThread( &sThread1 ); } /*---------------------------------------------------------------------------* Name: CheckThreadMain Description: Outputs the heap state when the START button is pressed. Main function for heap state check thread Arguments: None Returns: None *---------------------------------------------------------------------------*/ static void* CheckThreadMain( void* param ) { #pragma unused( param ) while ( TRUE ) { // When the START button is pressed, the state of the current heap is dumped. if ( sPadTrig ) { sPadTrig = 0; #ifdef _DEBUG MEMDumpHeap( hExpHeap ); #else ReportExpHeap( hExpHeap ); #endif } // After executing once, suspends until the next PAD sampling. (void)OSSuspendThread( OSGetCurrentThread() ); } } /*---------------------------------------------------------------------------* Name: RunPad Description: Callback function during PAD sampling Dumps the heap state when the START button is triggered. Arguments: None Returns: None *---------------------------------------------------------------------------*/ static void RunPad( void ) { static u16 lastPad = 0; PADStatus pad[ PAD_MAX_CONTROLLERS ]; (void)PADRead( pad ); if ( PADButtonDown( lastPad, pad[0].button ) & PAD_BUTTON_START ) { sPadTrig = 1; } lastPad = pad[0].button; // Wakes the thread for heap status check. (void)OSResumeThread( &sCheckThread ); } /*---------------------------------------------------------------------------* Name: main *---------------------------------------------------------------------------*/ void main( void ) { OSInit(); VIInit(); (void)PADInit(); // Creation of the expanded heap #if defined(THREAD_SAFE_ON) // Creates heap using the thread-safe option. hExpHeap = MEMCreateExpHeapEx( sHeapBuf, sizeof sHeapBuf, MEM_HEAP_OPT_THREAD_SAFE ); #else hExpHeap = MEMCreateExpHeapEx( sHeapBuf, sizeof sHeapBuf, 0 ); #endif // Create Thread 1 (medium priority) (void)OSCreateThread( &sThread1, RunThread1, NULL, // Parameter NULL sThreadStack1 + sizeof sThreadStack1, sizeof sThreadStack1, 15, // Higher priority than the default thread OS_THREAD_ATTR_DETACH ); // Create thread for status check (high priority) (void)OSCreateThread( &sCheckThread, CheckThreadMain, NULL, // Parameter NULL sCheckThreadStack + sizeof sCheckThreadStack, sizeof sCheckThreadStack, 14, // Priority is highest OS_THREAD_ATTR_DETACH ); // Callback setting during PAD sampling (Used to display heap status) (void)PADSetSamplingCallback( RunPad ); // Creates alarm and wakes the thread at fixed intervals using the timer. OSCreateAlarm( &sAlarm ); OSSetPeriodicAlarm( &sAlarm, OSGetTick() + OSMicrosecondsToTicks(1000), OSMicrosecondsToTicks(10), RunAlarm ); while ( TRUE ) { // Repeatedly allocates and releases memory. void* pMBlocks[ 20 ]; u32 i; (void)MEMSetGroupIDForExpHeap( hExpHeap, 0x2 ); // Obtains memory from the heap for ( i = 0; i < 20; i++ ) { pMBlocks[ i ] = MEMAllocFromExpHeap( hExpHeap, 50 ); } for ( i = 0; i < 20; i++ ) { // Returns memory to the heap. MEMFreeToExpHeap( hExpHeap, pMBlocks[ i ] ); } } } /* ------------------------------------------------------------------------ * This is the code for outputting the heap state as a sample for non-debug build. * * Normally, more detailed output can be derived during a debug build by using the MEMDumpHeap function. */ #ifndef _DEBUG /* Structure that gets information for reports. Used to specify parameters when calling the visitor function. */ typedef struct { u32 size; // Data size storage region u32 cnt; // Number of memory blocks used } TReportParam; /*---------------------------------------------------------------------------* Name: ReportVisitorFunc Description: A Visitor function for outputting the heap state. Arguments: memBlock: The memory block heap: Handle for expanded heap userParam: call parameter for the visitor function Gets the total memory size. Returns: None. *---------------------------------------------------------------------------*/ static void ReportVisitorFunc( void* memBlock, MEMHeapHandle heap, u32 userParam ) { #pragma unused( heap ) TReportParam* pParam = (TReportParam*)userParam; u16 allocDir = MEMGetAllocDirForMBlockExpHeap( memBlock ); u16 groupId = MEMGetGroupIDForMBlockExpHeap ( memBlock ); u32 size = MEMGetSizeForMBlockExpHeap ( memBlock ); pParam->size += MEMGetSizeForMBlockExpHeap( memBlock ); pParam->cnt++; OSReport(" %s %08x: %8d %3d\n", allocDir == MEM_EXPHEAP_ALLOC_DIR_REAR ? " rear" : "front", (u32)memBlock, size, groupId ); } /*---------------------------------------------------------------------------* Name: ReportExpHeap Description: Outputs the state of the expanded heap. The MEMDumpHeap will provide detailed output for debug builds. Arguments: heap: heap handle Returns: None. *---------------------------------------------------------------------------*/ static void ReportExpHeap( MEMHeapHandle heap ) { TReportParam param = { 0, 0 }; OSReport("[OS Foundation Exp Heap]\n"); OSReport(" whole [%p - %p)\n", MEMGetHeapStartAddress( heap ), MEMGetHeapEndAddress( heap ) ); OSReport(" attr address: size gid\n"); // Header line MEMVisitAllocatedForExpHeap( heap, // The heap handle ReportVisitorFunc, // visitor function (u32)¶m // User parameters ); OSReport("\n"); { u32 usedSize = param.size; u32 usedCnt = param.cnt; OSReport(" %d bytes allocated (U:%d)\n", usedSize, usedCnt); } OSReport("\n"); } #endif