nlib
misc/threading/condvar/condvar.cpp

Sample that uses condition variables.

Conditional variables generally use certain combinations of the following values.

Follow the steps below to wait to be notified from other threads.

  1. Apply a lock.
  2. Pass a lock and wait. The lock is released internally.
  3. When notified, the lock is automatically reacquired. Check the state (to account for waiting after notification), and execute if possible.
  4. Change the state to non-signal (when appropriate).

Follow the steps below to notify other threads.

  1. Apply a lock.
  2. Change to the signal state.
  3. Call nn::nlib::threading::CondVar::Notify or nn::nlib::threading::CondVar::NotifyAll() to give notification to other threads.
  4. Unlock.

This is written in the ThreadParam::Exec function.

The even and odd threads signal each other to continue the process in this sample.

/*---------------------------------------------------------------------------*
Project: CrossRoad
Copyright (C)2012-2016 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.
*---------------------------------------------------------------------------*/
using nlib_ns::threading::ScopedLock;
using nlib_ns::threading::UniqueLock;
using nlib_ns::threading::CondVar;
using nlib_ns::threading::SimpleCriticalSection;
using nlib_ns::threading::Thread;
const int NUM_THREADS = 6; // NUM_THREADS must be even
Thread g_Th[NUM_THREADS];
struct CondVarState {
CondVar cond;
SimpleCriticalSection lock;
bool flag;
public:
CondVarState() : flag(false) {}
} g_Cond1, g_Cond2;
class ThreadParam {
public:
ThreadParam() : thread_num_(0), is_success_(false), wait_(NULL), signal_(NULL) {}
void Initialize(int num, CondVarState* w, CondVarState* s) {
thread_num_ = num;
wait_ = w;
signal_ = s;
is_success_ = true;
}
void Exec();
static void ThreadFunc(ThreadParam* param) { param->Exec(); }
bool IsSuccessful() { return is_success_; }
private:
int thread_num_;
bool is_success_;
CondVarState* wait_;
CondVarState* signal_;
};
void ThreadParam::Exec() {
{
UniqueLock<SimpleCriticalSection> lock(wait_->lock);
// Note that the thread is already notified at this point.
while (!wait_->flag) {
nlib_printf(" Thread %d waiting\n", thread_num_);
// releases 'lock' and waits notification
errno_t e = wait_->cond.Wait(lock);
if (nlib_is_error(e)) {
is_success_ = false;
return;
}
// 'lock' is acquired when it returns from Wait(lock)
nlib_printf(" Thread %d may be notified and acquires lock\n", thread_num_);
}
nlib_printf(" Thread %d is notified, and do its own job\n", thread_num_);
wait_->flag = false; // turn off the flag, note that 'lock' is acquired
}
{
ScopedLock<SimpleCriticalSection> lock(signal_->lock);
// you have to set signal_->flag within the criticalsection
signal_->flag = true;
nlib_printf(" Thread %d notifies\n", thread_num_);
signal_->cond.NotifyAll();
}
}
bool CondVarDemo() {
ThreadParam param[NUM_THREADS];
for (int i = 0; i < NUM_THREADS; ++i) {
if (i % 2 == 0) {
param[i].Initialize(i, &g_Cond1, &g_Cond2);
e = g_Th[i].Start(ThreadParam::ThreadFunc, &param[i]);
} else {
param[i].Initialize(i, &g_Cond2, &g_Cond1);
e = g_Th[i].Start(ThreadParam::ThreadFunc, &param[i]);
}
if (nlib_is_error(e)) return false;
}
g_Cond1.lock.lock();
nlib_printf(" Main thread notifies\n");
// At this point, the threads may have already started to wait or not.
g_Cond1.flag = true;
g_Cond1.cond.NotifyAll();
g_Cond1.lock.unlock();
for (int i = 0; i < NUM_THREADS; ++i) {
e = g_Th[i].Join();
if (nlib_is_error(e)) return false;
}
for (int i = 0; i < NUM_THREADS; ++i) {
if (!param[i].IsSuccessful()) return false;
}
return true;
}
static bool SampleMain(int, char**) { return CondVarDemo(); }
NLIB_MAINFUNC