Mark3 Realtime Kernel
lab5_mutexes/main.cpp

This example demonstrates how to use mutexes to protect against concurrent access to resources.

/*===========================================================================
_____ _____ _____ _____
___| _|__ __|_ |__ __|__ |__ __| __ |__ ______
| \ / | || \ || | || |/ / ||___ |
| \/ | || \ || \ || \ ||___ |
|__/\__/|__|_||__|\__\ __||__|\__\ __||__|\__\ __||______|
|_____| |_____| |_____| |_____|
--[Mark3 Realtime Platform]--------------------------------------------------
Copyright (c) 2012 - 2019 m0slevin, all rights reserved.
See license.txt for more information
===========================================================================*/
#include "mark3.h"
/*===========================================================================
Lab Example 5: using Mutexes.
Lessons covered in this example include:
-You can use mutexes to lock accesses to a shared resource
Takeaway:
===========================================================================*/
extern "C" {
void __cxa_pure_virtual(void) {}
void DebugPrint(const char* szString_);
}
namespace
{
using namespace Mark3;
//---------------------------------------------------------------------------
// This block declares the thread data for one main application thread. It
// defines a thread object, stack (in word-array form), and the entry-point
// function used by the application thread.
#define APP1_STACK_SIZE (PORT_KERNEL_DEFAULT_STACK_SIZE)
Thread clApp1Thread;
K_WORD awApp1Stack[APP1_STACK_SIZE];
void App1Main(void* unused_);
//---------------------------------------------------------------------------
// This block declares the thread data for one main application thread. It
// defines a thread object, stack (in word-array form), and the entry-point
// function used by the application thread.
#define APP2_STACK_SIZE (PORT_KERNEL_DEFAULT_STACK_SIZE)
Thread clApp2Thread;
K_WORD awApp2Stack[APP2_STACK_SIZE];
void App2Main(void* unused_);
//---------------------------------------------------------------------------
// idle thread -- do nothing
Thread clIdleThread;
void IdleMain(void* /*unused_*/)
{
while (1) {}
}
//---------------------------------------------------------------------------
// This is the mutex that we'll use to synchronize two threads in this
// demo application.
Mutex clMyMutex;
// This counter variable is the "shared resource" in the example, protected
// by the mutex. Only one thread should be given access to the counter at
// any time.
volatile uint32_t u32Counter = 0;
//---------------------------------------------------------------------------
void App1Main(void* unused_)
{
while (1) {
// Claim the mutex. This will prevent any other thread from claiming
// this lock simulatenously. As a result, the other thread has to
// wait until we're done before it can do its work. You will notice
// that the Start/Done prints for the thread will come as a pair (i.e.
// you won't see "Thread2: Start" then "Thread1: Start").
clMyMutex.Claim();
// Start our work (incrementing a counter). Notice that the Start and
// Done prints wind up as a pair when simuated with flAVR.
Kernel::DebugPrint("Thread1: Start\n");
u32Counter++;
while (u32Counter <= 1000000) { u32Counter++; }
u32Counter = 0;
Kernel::DebugPrint("Thread1: Done\n");
// Release the lock, allowing the other thread to do its thing.
clMyMutex.Release();
}
}
//---------------------------------------------------------------------------
void App2Main(void* unused_)
{
while (1) {
// Claim the mutex. This will prevent any other thread from claiming
// this lock simulatenously. As a result, the other thread has to
// wait until we're done before it can do its work. You will notice
// that the Start/Done prints for the thread will come as a pair (i.e.
// you won't see "Thread2: Start" then "Thread1: Start").
clMyMutex.Claim();
// Start our work (incrementing a counter). Notice that the Start and
// Done prints wind up as a pair when simuated with flAVR.
Kernel::DebugPrint("Thread2: Start\n");
u32Counter++;
while (u32Counter <= 1000000) { u32Counter++; }
u32Counter = 0;
Kernel::DebugPrint("Thread2: Done\n");
// Release the lock, allowing the other thread to do its thing.
clMyMutex.Release();
}
}
} // anonymous namespace
using namespace Mark3;
//---------------------------------------------------------------------------
int main(void)
{
// See the annotations in previous labs for details on init.
clIdleThread.Init(awIdleStack, sizeof(awIdleStack), 0, IdleMain, 0);
clIdleThread.Start();
clApp1Thread.Init(awApp1Stack, sizeof(awApp1Stack), 1, App1Main, 0);
clApp2Thread.Init(awApp2Stack, sizeof(awApp2Stack), 1, App2Main, 0);
clApp1Thread.Start();
clApp2Thread.Start();
// Initialize the mutex used in this example.
clMyMutex.Init();
return 0;
}