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)Nintendo All rights reserved.
These coded instructions, statements, and computer programs contain proprietary
information of Nintendo and/or its licensed developers and are protected by
national and international copyright laws. 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.
The content herein is highly confidential and should be handled accordingly.
*--------------------------------------------------------------------------------*/
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 kNumThread = 6; // kNumThread must be even
Thread g_th[kNumThread];
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[kNumThread];
for (int i = 0; i < kNumThread; ++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 < kNumThread; ++i) {
e = g_th[i].Join();
if (nlib_is_error(e)) return false;
}
for (int i = 0; i < kNumThread; ++i) {
if (!param[i].IsSuccessful()) return false;
}
return true;
}
static bool SampleMain(int, char**) { return CondVarDemo(); }
NLIB_MAINFUNC