yohhoyの日記

技術的メモをしていきたい日記

条件変数とspurious wakeup


(condition variable) "spurious wakeup" 

*1spurious  ; ;  *2wakeup  




spurious wakeup (wait)OS

spurious wakeup C++11 Predicate 

 spurious wakeup *3C++11Boost.ThreadPOSIX(pthread)Windows APIJava

spurious wakeup の具体例


C++11(Producer-Consumer)FIFOavailavailFIFOavailavail.wait(lk) 
#include <deque>
#include <mutex>
#include <condition_variable>

struct Data { /*...*/ };
std::deque<Data> queue;         // FIFOキュー
std::mutex mtx;                 // queue保護
std::condition_variable avail;  // queueに有効なデータが存在?

// 生産者スレッド
void producer()
{
  for (;;) {
    Data data = /* データ生成 */;
    {
      std::lock_guard<std::mutex> lk(mtx);
      queue.push_back(data);
    }
    avail.nofity_one();  // 条件変数availへ通知
  }
}

// 消費者スレッド
void consumer()
{
  for (;;) {
    Data data;
    {
      std::unique_lock<std::mutex> lk(mtx);
      while (queue.empty()) {
        avail.wait(lk);  // ★条件変数availへの通知を待機
      }
      data = queue.front();
      queue.pop_front();
    }
    /* データ消費 */;
  }
}

queue.empty() spurious wakeup  spurious wakeup 
// BUG: 消費者スレッドの誤った実装
{
  std::unique_lock<std::mutex> lk(mtx);
  if (queue.empty()) {  // BUG: 待機処理がループで囲われていない
    avail.wait(lk);  // (1) spurious wakeupでブロック解除されると...
  }
  // (2) FIFOキューが空なのに取り出し操作!
  data = queue.front();  queue.pop_front();
}

2 Predicate wait
template <class Predicate>
  void wait(unique_lock<mutex>& lock, Predicate pred);
// Effects: while (!pred()) wait(lock);

 wait  spurious wakeup 
{
  std::unique_lock<std::mutex> lk(mtx);
  // 条件「キュー内にデータが存在する」まで待機
  avail.wait(lk, [&]{ return !queue.empty(); });
  data = queue.front();  queue.pop_front();
}

C++11標準ライブラリ:std::condition_variable

C++11標準ライブラリで追加された条件変数オブジェクト(std::condition_variableおよびstd::condition_variable_any)において、spurious なブロック解除&スレッド始動が起こりえると言及されている。N3337 30.5.1/p10より引用(下線部は強調)。

void wait(unique_lock& lock);
Effects:

  • Atomically calls lock.unlock() and blocks on *this.
  • When unblocked, calls lock.lock() (possibly blocking on the lock), then returns.
  • The function will unblock when signaled by a call to notify_one() or a call to notify_all(), or spuriously.
  • If the function exits via an exception, lock.lock() shall be called prior to exiting the function scope.

Boost.Threadライブラリ:boost::condition_variable

Boost.Threadの条件変数オブジェクト(boost::condition_variableおよびboost::condition_variable_any)も同様。

Effects:
Atomically call lock.unlock() and blocks the current thread. The thread will unblock when notified by a call to this->notify_one() or this->notify_all(), or spuriously. When the thread is unblocked (for whatever reason), the lock is reacquired by invoking lock.lock() before the call to wait returns. The lock is also reacquired by invoking lock.lock() if the function exits with an exception.

http://www.boost.org/doc/html/thread/synchronization.html#thread.synchronization.condvar_ref.condition_variable.wait

POSIX(pthread):pthread_cond_t

pthreadの条件変数(pthread_cond_t型)。wikipedia:en:Spurious_wakeupによればpthreadが発祥?

When using condition variables there is always a Boolean predicate involving shared variables associated with each condition wait that is true if the thread should proceed. Spurious wakeups from the pthread_cond_timedwait() or pthread_cond_wait() functions may occur. Since the return from pthread_cond_timedwait() or pthread_cond_wait() does not imply anything about the value of this predicate, the predicate should be re-evaluated upon such return.

http://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_cond_wait.html

(The following sections are informative.)
RATIONALE
An added benefit of allowing spurious wakeups is that applications are forced to code a predicate-testing-loop around the condition wait. This also makes the application tolerate superfluous condition broadcasts or signals on the same condition variable that may be coded in some other part of the application. The resulting applications are thus more robust. Therefore, IEEE Std 1003.1-2001 explicitly documents that spurious wakeups may occur.

http://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_cond_signal.html

Windows API:CONDITION_VARIABLE

Windows APIの条件変数(CONDITION_VARIABLE型)はWindows Vistaで追加された。同APIWindows XP以前には存在しない。

Condition variables are subject to spurious wakeups (those not associated with an explicit wake) and stolen wakeups (another thread manages to run before the woken thread). Therefore, you should recheck a predicate (typically in a while loop) after a sleep operation returns.

http://msdn.microsoft.com/en-us/library/windows/desktop/ms682052.aspx

Java:Object#wait(), Conditionインタフェース


JavaObjectJava1.5 java.util.concurrent.locks Condition spurious wakeup 


A thread can also wake up without being notified, interrupted, or timing out, a so-called spurious wakeup. While this will rarely occur in practice, applications must guard against it by testing for the condition that should have caused the thread to be awakened, and continuing to wait if the condition is not satisfied. In other words, waits should always occur in loops, like this one: (snip)
 http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html#wait%28long%29  


When waiting upon a Condition, a "spurious wakeup" is permitted to occur, in general, as a concession to the underlying platform semantics. This has little practical impact on most application programs as a Condition should always be waited upon in a loop, testing the state predicate that is being waited for. An implementation is free to remove the possibility of spurious wakeups but it is recommended that applications programmers always assume that they can occur and so always wait in a loop. 
http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/locks/Condition.html  

2013-10-18JDK1.4Java


waitnotifyJavaDocObject#waitCondition#await
Object#waitCondition#await
Effective JavaJDK1.4JavaDoc
 Hatena -   

URL

*1:WebJava

*2:http://ejje.weblio.jp/content/spurious

*3: spurious wakeup