Created
January 6, 2019 23:54
-
-
Save CreeperMario/a694aed8999e0f6429abf4b2498104c9 to your computer and use it in GitHub Desktop.
An example of how cooperative threading works on the Wii U, with an analogy to help explain.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
cmake_minimum_required(VERSION 3.2) | |
project(thread-example) | |
include("$ENV{WUT_ROOT}/share/wut.cmake" REQUIRED) | |
add_executable(thread-example.elf thread-example.c) | |
target_link_libraries(thread-example.elf | |
whb | |
coreinit | |
nsysnet | |
sysapp) | |
wut_enable_newlib(thread-example.elf) | |
wut_create_rpx(thread-example.rpx thread-example.elf) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <coreinit/thread.h> | |
#include <sysapp/launch.h> | |
#include <whb/crash.h> | |
#include <whb/log.h> | |
#include <whb/log_cafe.h> | |
#include <whb/log_udp.h> | |
// Imagine that PowerPC core 0 is a 12-year-old child, who has to put | |
// the dishes away and clean their room. The child wants to play a game, but | |
// they cannot do so, as their chores have higher priority, and so must be | |
// completed first. Only once the chores have been completed can the child | |
// begin playing games. Eventually they do start playing games. | |
// | |
// Their mother walks into the room and says, "You forgot to take out the | |
// rubbish." Taking out the rubbish is a higher priority task than playing | |
// games, and so the child will immediately pause the game, take out the | |
// rubbish, and then return to the game. | |
// | |
// This program is an implentation of that analogy using the Wii U threading | |
// functions, created to explain the idea of cooperative threading. | |
#define THREAD_STACK_SIZE 4096 | |
// I don't think we can use OSSleepTicks, as that would result in the threads | |
// entering a "waiting state" which would allow lower priority threads to | |
// start running. For this example, this cannot happen. | |
#define STALL_FOR_TIME(x) {int timeout=x; while(timeout) timeout--;} | |
OSThread dishes_thread; | |
OSThread clean_room_thread; | |
OSThread games_thread; | |
OSThread rubbish_thread; | |
char dishes_stack[THREAD_STACK_SIZE]; | |
char clean_room_stack[THREAD_STACK_SIZE]; | |
char games_stack[THREAD_STACK_SIZE]; | |
char rubbish_stack[THREAD_STACK_SIZE]; | |
// Priority level 18, higher priority than levels 19 and 20 | |
int dishes_entry(int argc, const char ** argv) | |
{ | |
// There's a little bit of mumbo-jumbo in which lower priority threads | |
// that are created after this one will run for a brief period of time, | |
// before realising that they are lower priority and stopping again. | |
// This stops the threads from doing anything while that mumbo-jumbo | |
// occurs. | |
STALL_FOR_TIME(1000000); | |
for(int i = 0; i < 3; i++) { | |
WHBLogPrint("Putting the dishes away..."); | |
STALL_FOR_TIME(200000000); // approximately 1 second | |
} | |
return 0; | |
} | |
// Priority level 19, higher priority than level 20 | |
int clean_room_entry(int argc, const char ** argv) | |
{ | |
STALL_FOR_TIME(1000000); | |
for(int i = 0; i < 5; i++) { | |
WHBLogPrint("Cleaning my room..."); | |
STALL_FOR_TIME(200000000); | |
} | |
return 0; | |
} | |
// Priority level 20, least important thread | |
int games_entry(int argc, const char ** argv) | |
{ | |
STALL_FOR_TIME(1000000); | |
for(int i = 0; i < 10; i++) { | |
WHBLogPrint("Playing games..."); | |
STALL_FOR_TIME(200000000); | |
} | |
return 0; | |
} | |
// Priority level 17, higher priority than levels 18, 19 and 20 | |
// (though in this example, the level 18/19 threads have already finished) | |
int rubbish_entry(int argc, const char ** argv) | |
{ | |
STALL_FOR_TIME(1000000); | |
for(int i = 0; i < 3; i++) { | |
WHBLogPrint("Putting out the rubbish..."); | |
STALL_FOR_TIME(200000000); | |
} | |
return 0; | |
} | |
// Remember that the main() thread runs on core 1, while all the threads we | |
// create will run on core 0 - so main() will actually be running thrughout the | |
// entire program execution. | |
int main(int argc, const char ** argv) | |
{ | |
WHBInitCrashHandler(); | |
WHBLogCafeInit(); | |
WHBLogUdpInit(); | |
OSCreateThread(&dishes_thread, &dishes_entry, 0, NULL, dishes_stack + THREAD_STACK_SIZE, THREAD_STACK_SIZE, 18, OS_THREAD_ATTRIB_AFFINITY_CPU0); | |
OSCreateThread(&clean_room_thread, &clean_room_entry, 0, NULL, clean_room_stack + THREAD_STACK_SIZE, THREAD_STACK_SIZE, 19, OS_THREAD_ATTRIB_AFFINITY_CPU0); | |
OSCreateThread(&games_thread, &games_entry, 0, NULL, games_stack + THREAD_STACK_SIZE, THREAD_STACK_SIZE, 20, OS_THREAD_ATTRIB_AFFINITY_CPU0); | |
OSCreateThread(&rubbish_thread, &rubbish_entry, 0, NULL, rubbish_stack + THREAD_STACK_SIZE, THREAD_STACK_SIZE, 17, OS_THREAD_ATTRIB_AFFINITY_CPU0); | |
// This will start dishes_thread, clean_room_thread and games_thread. | |
// Each of them will run for a little bit of time (see above, | |
// re: mumbo-jumbo), and then dishes_thread (the thread of highest | |
// priority) will run until it finishes. Then clean_room_thread will run, | |
// and then games_thread. | |
WHBLogPrint("Doing chores..."); | |
OSResumeThread(&dishes_thread); | |
OSResumeThread(&clean_room_thread); | |
OSResumeThread(&games_thread); | |
// Once both dishes_thread and clean_room_thread have finished, put a log | |
// message saying that chores are finished. | |
OSJoinThread(&dishes_thread, NULL); | |
OSJoinThread(&clean_room_thread, NULL); | |
WHBLogPrint("Chores are done! Game should be running now!"); | |
// After ~3.5 seconds of games_thread running, start running | |
// rubbish_thread. rubbish_thread is of higher priority than games_thread | |
// and so games_thread will automatically stop to let rubbish_thread run. | |
// Once rubbish_thread finishes, games_thread will resume automatically. | |
STALL_FOR_TIME(700000000); | |
WHBLogPrint("Oops, mum wants the rubbish taken out. Stop the game!"); | |
OSResumeThread(&rubbish_thread); | |
// Wait for all the remaining threads to finish before doing anything else. | |
OSJoinThread(&games_thread, NULL); | |
OSJoinThread(&rubbish_thread, NULL); | |
WHBLogPrint("Time for bed. :)"); | |
WHBLogCafeDeinit(); | |
WHBLogUdpDeinit(); | |
SYSRelaunchTitle(0, NULL); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment