User:AerialX/Coding Sandbox/Threading

From WiiBrew
Jump to navigation Jump to search

This is a simple example of how to use libOGC's threading subsystem complete with mutex locks. As per most entries in my sandbox, it was a test of something I intend on implementing in my own project. It just increments a counter on one thread and displays it on-screen on the other, main thread. Most of this was done by looking at libOGC's doxygen documentation until I found an example that cleared up what priority range to use.

Download

The download contains the source and binary of the simple example program. The supplied binaries were compiled with devkitPPC r15 and libogc r20080602 on Linux x86_64.

Download

Usage

Execute the dol via your favourite homebrew booting method *cough*wiiload*cough* and watch the progress magically rise.

template.c

#include <stdio.h>
#include <stdlib.h>
#include <gccore.h>
#include <wiiuse/wpad.h>
#include <unistd.h>

static void *xfb = NULL;
static GXRModeObj *rmode = NULL;

static mutex_t mutex;

void* threadFunc(void* param)
{
	int* percentage = param;
	
	while (*percentage < 100) {
		// Slowly increase the percentage
		usleep(100000);
		
		// Wait for a lock on the mutex (wait until the application thread has read the value)
		while (LWP_MutexLock(mutex))
			;

		// Now that we have exclusive permission to mess with the variable (we've acquired the mutex lock)
		//   we can change it at will
		(*percentage)++;

		// Unlock the mutex for our main thread to read the counter
		LWP_MutexUnlock(mutex);
	}
	
	return NULL;
}

//---------------------------------------------------------------------------------
int main(int argc, char **argv) {
//---------------------------------------------------------------------------------

	// Initialise the video system
	VIDEO_Init();
	
	// This function initialises the attached controllers
	WPAD_Init();
	
	// Obtain the preferred video mode from the system
	// This will correspond to the settings in the Wii menu
	rmode = VIDEO_GetPreferredMode(NULL);

	// Allocate memory for the display in the uncached region
	xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
	
	// Initialise the console, required for printf
	console_init(xfb,20,20,rmode->fbWidth,rmode->xfbHeight,rmode->fbWidth*VI_DISPLAY_PIX_SZ);
	
	// Set up the video registers with the chosen mode
	VIDEO_Configure(rmode);
	
	// Tell the video hardware where our display memory is
	VIDEO_SetNextFramebuffer(xfb);
	
	// Make the display visible
	VIDEO_SetBlack(FALSE);

	// Flush the video register changes to the hardware
	VIDEO_Flush();

	// Wait for Video setup to complete
	VIDEO_WaitVSync();
	if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync();

	// Declare the variable as volatile to prevent compiler optimizations from messing up cross-thread
	//  access with caching and such
	volatile int percentage = 0;
	lwp_t thread;
	
	// Initialize the mutex lock that we'll use to safely read and write to the percentage counter
	LWP_MutexInit(&mutex, false);

	// Create a new thread that will execute the threadFunc method and pass it percentage's memory address
	// Allow the stack to be handled by the thread system with default stack space (parameters 4 and 5)
	// Finally, give the thread a decent priority (I'm told that "Userland threads should have
	//   priorities in the range 80 - 128")
	LWP_CreateThread(&thread, threadFunc, (void*)&percentage, NULL, 0, 80);

	while(1) {
		// Call WPAD_ScanPads each loop, this reads the latest controller states
		WPAD_ScanPads();

		// WPAD_ButtonsDown tells us which buttons were pressed in this loop
		// this is a "one shot" state which will not fire again until the button has been released
		u32 pressed = WPAD_ButtonsDown(0);

		// We return to the launcher application via exit
		if ( pressed & WPAD_BUTTON_HOME ) exit(0);

		// Only print the value if the other thread isn't writing right now (and lock the mutex
		//   when we're reading it)
		if (LWP_MutexLock(mutex)) {
			// The console understands VT terminal escape codes
			// This positions the cursor on row 2, column 0
			// we can use variables for this with format codes too
			// e.g. printf ("\x1b[%d;%dH", row, column );
			printf("\x1b[2;0H");
			// Print out the current value of percentage
			printf("Thread progress: %.2d%%\n", percentage);

			// Allow the other thread to write again
			LWP_MutexUnlock(mutex);
		}

		// Wait for the next frame
		VIDEO_WaitVSync();
	}
	
	// Destroy the mutex (pointless here but meh, mind as well clean up)
	LWP_MutexDestroy(mutex);

	return 0;
}