C言語

【C言語】pthread_cond_timedwaitがタイムアウトしない罠

pthread ライブラリを使い、スレッド間の同期・通信を行うために pthread_cond_signal
pthread_cond_wait を使うことがあります。

また、永遠にシグナルを待ち続けることが無いように、pthread_cond_timedwait を使って
一定時間シグナルが届かい場合にタイムアウトさせるAPIも用意されています。

しかし、この pthread_cond_timedwait は使い方を誤ると意図した動作をしなくなります。
つまりタイムアウトしなくなります。(関数呼び出しから戻ってこなくなる)

本記事では、pthread_cond_timedwait の仕様、どのような条件が揃うとタイムアウトしなくなるのか、
また、それを防ぐ実装方法を紹介いたします。

 

関数仕様

書式

引数

cond pthread_cond_signal or pthread_cond_broadcast で送信されるシグナルを待つ条件変数
mutex 条件変数を保護するための mutex ロック変数
abstime シグナルを待つ時間を指定する(絶対時間)

戻り値

0 シグナルを受信し、正常に処理を完了
EINVAL 引数 cond, mutex または abstime によって指定された値が無効
EINTR 非同期シグナルによって割り込まれた
ETIMEDOUT システム時間が abstime で指定した時間に到達、または超過
SANACHAN
SANACHAN
pthread_cond_timedwait は、エラー番号を直接返します。

 

機能

  • 条件変数 cond にシグナルが到達するまでスレッドをブロック
    mutex のアンロックと条件変数 cond へのシグナル待ちを同時に行います。
    pthread_cond_signal または pthread_cond_broadcast で条件変数にシグナルが送信されるまで、
    スレッドの実行は停止され、CPUを消費しません。関数から戻る際に、再び mutex をロックします。
  • abstime で待ち時間の設定
    abstime で指定された時間内にシグナルが送信されなかった場合、mutex を再びロックし、
    ETIMEDOUT を返します。デフォルトでは、1970/1/1 00:00:00 を起点とする絶対時刻で指定します。

 

タイムアウトしないメカニズム

例えば、以下のような場合を考えてみます。

pthread_cond_timedwaitがタイムアウトしない

pthread_cond_timedwaitがタイムアウトしないメカニズム

CLOCK_REALTIME で取得できる時刻や、gettimeofday() で取得できる「絶対時刻」は、
1970/1/1 00:00:00 を起点とした時刻で、ファイルのタイムスタンプなどに使用されています。
この時刻は、NTP(Network Time Protocol)などによって補正されます。

例えば、上図のようなロジックが実行された場合、以下のように過去を指定して待ちに入ってしまいます

  1. clock_gettime() で、2022/03/29 00:00:00 を取得する
  2. 2秒のあいだ条件変数 cond へのシグナル送信を待ち受けるため、tv_sec に 2 を加算
    ⇒つまり、2022/03/29 00:00:02 まで待つ
  3. pthread_cond_timedwait を呼び出すまでに、NTP により 2022/03/29 00:00:05 に時刻補正
  4. 補正の結果、過去の時刻を指定して pthread_cond_timedwait を呼び出す
SANACHAN
SANACHAN
待ち時間を計算してから pthread_cond_timedwait を呼び出すまでの間に割り込む NTP の時刻補正を防ぐことはできません。必ず隙間ができてしまいます。

 

タイムアウトしない現象を防ぐ方法

では、どのようにして防げばよいのでしょうか?

Unix 系の OS は、絶対時刻の他にも様々な時刻情報を持っています。
代表的なものは MONOTONIC と呼ばれる時刻で、カーネルが起動している経過時間を示す時刻です。
dmesg の各行の頭に付いている時刻で、NTP などの時刻補正の影響を受けません。

この MONOTONIC 時刻を使用して、pthread_cond_timedwait の待ち時間を指定します。

これには、pthread_cond_init で条件変数を初期化する際に、pthread_condattr_setclock で
条件変数 cond に時刻の属性を設定する必要があります。

 

サンプルプログラム

 

こちらの記事もよく読まれています

  • この記事を書いた人
  • 最新記事
SANACHAN

SANACHAN

「生涯一エンジニア」を掲げ、大手グローバル企業でSE/PGとして8年勤め、キャリアアップ転職した現役のエンジニアです。世にあるメジャーな全プログラム言語(コボル除く)を自由に扱えます。一児の父。自分のため、家族のため、日々勉強してます。システムエンジニア、プログラミングに関する情報を蓄積している雑記帳です。

-C言語
-, ,