The following example demonstrates how mutex objects utilize priority inheritance to prevent priority inversion.
#include <stdio.h> #include "OS_API.h" HANDLE MutexHandle; TIME StartupTime; volatile int CurrentTask; void Print(char *Str) { BOOL PrevLockState; /* Disable interrupts */ PrevLockState = arLock(); /* Print specified string */ printf("%6i msec: %s.\n", (int) (arGetTickCount() - StartupTime), Str); /* Restore interrupts */ arRestore(PrevLockState); } void LongJob(TIME Time, int TaskID) { TIME Start; /* Perform a job for specified amount of time */ Start = arGetTickCount(); while(arGetTickCount() < (Start + Time)) { BOOL PrevLockState; /* Disable interrupts */ PrevLockState = arLock(); /* Print ID of task currently executing a job */ if(CurrentTask != TaskID) { switch(TaskID) { case 1: Print("Currently processing task is a Task1"); break; case 2: Print("Currently processing task is a Task2"); break; case 3: Print("Currently processing task is a Task3"); break; } CurrentTask = TaskID; } /* Restore interrupts */ arRestore(PrevLockState); } } /* Example task with high priority */ ERROR Task1(PVOID Arg) { /* Mark parameter as unused */ AR_UNUSED_PARAM(Arg); /* Sleep for 2 seconds */ Print("Task1 starts and sleeps for 2 seconds"); osSleep(2000); /* Wait for the mutex */ Print("Task1 is resumed after 2 seconds and begins waiting for the mutex"); osWaitForObject(MutexHandle, AR_TIME_INFINITE); /* Execute job for 2 seconds */ Print("Task1 acquires the mutex and executes a job for 2 seconds"); LongJob(2000, 1); /* Release mutex */ Print("Task1 finishes its job after 2 seconds and releases mutex"); osReleaseMutex(MutexHandle); /* Exit task */ Print("Task1 ends"); return ERR_NO_ERROR; } /* Example task with medium priority */ ERROR Task2(PVOID Arg) { /* Mark parameter as unused */ AR_UNUSED_PARAM(Arg); /* Sleep for 1 second */ Print("Task2 starts and sleeps for 1 second"); osSleep(1000); /* Execute job for 10 seconds */ Print("Task2 is resumed after 1 second and starts job for 4 seconds"); LongJob(4000, 2); /* Exit task */ Print("Task2 finishes its job after 4 seconds and ends"); return ERR_NO_ERROR; } /* Example task with low priority */ ERROR Task3(PVOID Arg) { /* Mark parameter as unused */ AR_UNUSED_PARAM(Arg); /* Wait for the mutex */ Print("Task3 starts and begins waiting for the mutex"); osWaitForObject(MutexHandle, AR_TIME_INFINITE); /* Execute job for 5 seconds */ Print("Task3 acquires the mutex and starts a job for 3 seconds"); LongJob(3000, 3); /* Release mutex */ Print("Task3 finishes its job after 3 seconds and releases the mutex"); osReleaseMutex(MutexHandle); /* Exit task */ Print("Task3 ends"); return ERR_NO_ERROR; } int main(void) { /* Initialization */ arInit(); stInit(); osInit(); /* Initialize global variables */ CurrentTask = 0; StartupTime = arGetTickCount(); /* Create objects */ MutexHandle = osCreateMutex(NULL, FALSE); osCreateTask(Task1, NULL, 0, 1, FALSE); osCreateTask(Task2, NULL, 0, 2, FALSE); osCreateTask(Task3, NULL, 0, 3, FALSE); /* Start the operating system */ osStart(); /* Deinitialization */ osDeinit(); arDeinit(); return 0; }
The result on the console should look as follows:
0 msec: Task1 starts and sleeps for 2 seconds.
0 msec: Task2 starts and sleeps for 1 second.
0 msec: Task3 starts and begins waiting for the mutex.
0 msec: Task3 acquires the mutex and starts a job for 3 seconds.
0 msec: Currently processing task is a Task3.
1000 msec: Task2 is resumed after 1 second and starts job for 4 seconds.
1000 msec: Currently processing task is a Task2.
2000 msec: Task1 is resumed after 2 seconds and begins waiting for the mutex.
2000 msec: Currently processing task is a Task3.
3000 msec: Task3 finishes its job after 3 seconds and releases the mutex.
3000 msec: Task1 acquires the mutex and executes a job for 2 seconds.
3000 msec: Currently processing task is a Task1.
5000 msec: Task1 finishes its job after 2 seconds and releases mutex.
5000 msec: Task1 ends.
5000 msec: Task2 finishes its job after 4 seconds and ends.
5000 msec: Task3 ends.