マルチスレッドのプログラミング

条件変数上で割り込まれた待機

マスクされていない捕獲されたシグナルが条件変数上で待機しているスレッドに配信され、そのスレッドがシグナルハンドラから戻ると、そのスレッドは条件の待機状態からは戻りますが、これは誤って呼び起こされたもの (ほかのスレッドから送られた条件シグナルによらないもの) です。この場合、Solaris スレッドのインタフェース (cond_wait() cond_timedwait()) は EINTR を返します。POSIX スレッドのインタフェース (pthread_cond_wait() pthread_cond_timedwait()) は 0 を返します。 どちらのスレッドも、条件の待機状態から戻る前に、関連付けられている相互排他ロックを再度獲得します。

これは、スレッドがシグナルハンドラを実行しているときに相互排他ロックを獲得しているという意味ではありません。シグナルハンドラ内では、相互排他ロックの状態は不定です。

Solaris 9 より前のリリースに実装されている libthread では、シグナルハンドラ内の相互排他ロックの保持が保証されます。従来の動作に依存するアプリケーションは、Solaris 9 以降のリリースに合わせて修正する必要があります。

ハンドラのクリーンアップについて、例 5–4を使用して説明します。


例 5–4 条件変数と割り当てまれた割り込み


int sig_catcher() {
    sigset_t set;
    void hdlr();

    mutex_lock(&mut);

    sigemptyset(&set);
    sigaddset(&set, SIGINT);
    sigsetmask(SIG_UNBLOCK, &set, 0);

    if (cond_wait(&cond, &mut) == EINTR) {
        /* シグナルが発生し、ロックが保持される */
        cleanup();
        mutex_unlock(&mut);
        return(0);
    }
    normal_processing();
    mutex_unlock(&mut);
    return(1);
}

void hdlr() {
    /* ロックの状態は未定義 */
    ...
}

sig_catcher() が呼び出された時点では、すべてのスレッドで SIGINT シグナルがブロックされているものとします。さらに、sigaction(2) によって hdlr()SIGINT シグナルのシグナルハンドラとして設定されているものとします。シグナルマスクが解除されて、キャッチされた SIGINT シグナルのインスタンスがスレッドに送信されたときに、スレッドが cond_wait() の状態だとします。スレッドは hdlr() を呼び出し、cond_wait() 関数に戻ります。このとき、相互排他ロックを必要に応じて再度獲得し、cond_wait() EINTR を返します。

sigaction()SA_RESTART フラグを指定したとしても、ここでは意味がないことに注意してください。cond_wait(3THR) はシステムコールではないため、自動的に再呼び出しされないからです。cond_wa でスレッドがブロックされているときにシグナルが送られてくると、cond_wait() は常に EINTR エラーで戻ります。