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

第 6 章 Solaris スレッドを使ったプログラミング

この章では、Solaris スレッドと POSIX スレッドのアプリケーションプログラミングインタフェース (API) を比較し、POSIX スレッドにはない Solaris の機能について説明します。この章の内容は次のとおりです。

Solaris スレッドと POSIX スレッドの API の比較

Solaris スレッド API と pthread API は、同じ問題に対する 2 つの解決策です。 それは、アプリケーションソフトウェアへの並列性構築です。どちらの API もそれ自体で完結したものですが、Solaris スレッドの関数と pthread の関数を同じプログラムの中で併用することもできます。

ただし、2 つの API は完全に一致しているわけではありません。Solaris スレッドは pthread にない関数をサポートしていて、pthread には Solaris インタフェースでサポートされない関数が含まれています。同じ関数については、機能が実質的に同じでも使用する引数が異なることがあります。

2 つの API を組み合わせて使用すれば、それぞれ他方にない機能を補い合うことができます。また、同じシステムで、Solaris スレッドだけを使用するアプリケーションを実行する一方で、pthread だけを使用するアプリケーションを実行することもできます。

API の主な相違点

Solaris スレッドと pthread は、API の動作や構文も非常によく似ています。表 6–1 に、主な相違点を示します。

表 6–1 Solaris スレッドと pthread の相違点

Solaris スレッド 

POSIX スレッド 

スレッド関数名の接頭辞が thr_ で、セマフォー関数名の接頭辞が sema_

スレッド関数名の接頭辞が pthread_ で、セマフォー関数名の接頭辞が sem_

デーモンスレッドが生成可能 

取り消しセマンティクス 

スレッドの停止と再開 

スケジューリングポリシー 

関数比較表

次の表は、Solaris スレッドの関数と pthread の関数を比較対照したものです。なお、Solaris スレッドの関数と pthread の関数が本質的に同じものとして並記されている場合でも、その引数は異なっていることがあります。

pthread または Solaris スレッドの側に相当するインタフェースがない場合は、「-」が記入されています。pthread 欄の項目で「(3RT)」が付記されているものは、POSIX.1b リアルタイム拡張機能ライブラリである librt 内の関数であり、pthread の一部ではありません。このライブラリ内の関数は、POSIX.1b リアルタイム拡張機能で指定されているほとんどのインタフェースを提供しています。

表 6–2 Solaris スレッドと POSIX pthread の比較

Solaris スレッド 

pthread 

thr_create()

pthread_create()

thr_exit()

pthread_exit()

thr_join()

pthread_join()

thr_yield()

sched_yield(3RT)()

thr_self()

pthread_self()

thr_kill()

pthread_kill()

thr_sigsetmask()

pthread_sigmask()

thr_setprio()

pthread_setschedparam()

thr_getprio()

pthread_getschedparam()

thr_setconcurrency()

pthread_setconcurrency()

thr_getconcurrency()

pthread_getconcurrency()

thr_suspend()

thr_continue()

thr_keycreate()

pthread_key_create()

pthread_key_delete()

thr_setspecific()

pthread_setspecific()

thr_getspecific()

pthread_getspecific()

pthread_once()

pthread_equal()

pthread_cancel()

pthread_testcancel()

pthread_cleanup_push()

pthread_cleanup_pop()

pthread_setcanceltype()

pthread_setcancelstate()

mutex_lock()

pthread_mutex_lock()

mutex_unlock()

pthread_mutex_unlock()

mutex_trylock()

pthread_mutex_trylock()

mutex_init()

pthread_mutex_init()

mutex_destroy()

pthread_mutex_destroy()

cond_wait()

pthread_cond_wait()

cond_timedwait()

pthread_cond_timedwait()

cond_reltimedwait()

pthread_cond_reltimedwait_np()

cond_signal()

pthread_cond_signal()

cond_broadcast()

pthread_cond_broadcast()

cond_init()

pthread_cond_init()

cond_destroy()

pthread_cond_destroy()

rwlock_init()

pthread_rwlock_init()

rwlock_destroy()

pthread_rwlock_destroy()

rw_rdlock()

pthread_rwlock_rdlock()

rw_wrlock()

pthread_rwlock_wrlock()

rw_unlock()

pthread_rwlock_unlock()

rw_tryrdlock()

pthread_rwlock_tryrdlock()

rw_trywrlock()

pthread_rwlock_trywrlock()

pthread_rwlockattr_init()

pthread_rwlockattr_destroy()

pthread_rwlockattr_getpshared()

pthread_rwlockattr_setpshared()

sema_init()

sem_init(3RT)()

sema_destroy()

sem_destroy(3RT)()

sema_wait()

sem_wait(3RT)()

sema_post()

sem_post(3RT)()

sema_trywait()

sem_trywait(3RT)()

fork1()

fork()

pthread_atfork()

forkall() (複数スレッドコピー)

pthread_mutexattr_init()

pthread_mutexattr_destroy()

mutex_init()type 引数

pthread_mutexattr_setpshared()

pthread_mutexattr_getpshared()

pthread_mutex_attr_settype()

pthread_mutex_attr_gettype()

pthread_condattr_init()

pthread_condattr_destroy()

cond_init()type 引数

pthread_condattr_setpshared()

pthread_condattr_getpshared()

pthread_attr_init()

pthread_attr_destroy()

thr_create()THR_BOUND フラグ

pthread_attr_setscope()

pthread_attr_getscope()

pthread_attr_setguardsize()

pthread_attr_getguardsize()

thr_create()stack_size 引数

pthread_attr_setstacksize()

pthread_attr_getstacksize()

thr_create()stack_addr 引数

pthread_attr_setstack()

pthread_attr_getstack()

thr_create()THR_DETACH フラグ

pthread_attr_setdetachstate()

pthread_attr_getdetachstate()

pthread_attr_setschedparam()

pthread_attr_getschedparam()

pthread_attr_setinheritsched()

pthread_attr_getinheritsched()

pthread_attr_setsschedpolicy()

pthread_attr_getschedpolicy()

この章で説明する Solaris スレッドの関数を使用するには、Solaris スレッドライブラリ (-lthread ) とリンクする必要があります。

Solaris スレッドと pthread では、関数名や引数が異なっていても、実質上の操作は変わりません。正しいインクルードファイルと関数プロトタイプを示した簡単な例を示すだけとします。Solaris スレッドの関数の戻り値が記述されていないものについては、『SunOS リファレンスマニュアル 3 : 基本ライブラリ関数』で調べてください。

Solaris 関連の関数の詳細は、pthread の関連マニュアルで類似した名前の関数を調べてください。

Solaris スレッドの関数で pthread にない機能を持つものについて、詳しく説明しています。

Solaris スレッドに固有の関数

この節では、Solaris スレッドに固有の次の関数について説明します。 スレッドの実行を中断する関数と、中断したスレッドを再開する関数です。

スレッド実行の停止

thr_suspend(3C) は、target_thread で指定されたスレッドの実行をただちに停止します。thr_suspend() が正常終了した時点で、指定のスレッドは実行状態ではありません。

thr_suspend() が目的のスレッドを停止するとき、そのスレッドが保持しているロックについては考慮されません。したがって、thr_suspend() は慎重に使用する必要があります。一時停止したスレッドに保持されているロックを必要とする関数が呼び出されると、デッドロックが発生します。

thr_suspend の構文

#include <thread.h>

int thr_suspend(thread_t tid);

停止しているスレッドに対して再度 thr_suspend() を発行しても効果はありません。停止しているスレッドをシグナルで呼び起こすことはできません。スレッドが実行を再開するまでシグナルは保留状態のままです。

次の例では、pthread で定義されている pthread_t tid と Solaris スレッドの thread_t tid が同じです。tid 値は、代入によっても型変換によっても使用できます。

thread_t tid; /* tid from thr_create() */

/* pthreads equivalent of Solaris tid from thread created */
/* with pthread_create() */
pthread_t ptid; 

int ret;

ret = thr_suspend(tid);

/* using pthreads ID variable with a cast */
ret = thr_suspend((thread_t) ptid); 

thr_suspend の戻り値

thr_suspend()は、正常終了時に 0 を返します。それ以外の戻り値は、エラーが発生したことを示します。以下の条件が検出されると、thr_suspend() は失敗し、対応する値を返します。


ESRCH

説明:

現在のプロセスに tid が存在しません。

停止しているスレッドの再開

thr_continue(3C) は、停止しているスレッドの実行を再開します。再開したスレッドに対して再度 thr_continue() を発行しても効果はありません。

thr_continue の構文

#include <thread.h>

int thr_continue(thread_t tid);

停止しているスレッドは、シグナルでは呼び起こされません。送られたシグナルは、そのスレッドが thr_continue() で再開されるまで保留されます。

pthread で定義されている pthread_t tid と Solaris スレッドの thread_t tid が同じです。tid 値は、代入によっても型変換によっても使用できます。

thread_t tid; /* tid from thr_create()*/

/* pthreads equivalent of Solaris tid from thread created */
/* with pthread_create()*/
pthread_t ptid;

int ret;

ret = thr_continue(tid);

/* using pthreads ID variable with a cast */
ret = thr_continue((thread_t) ptid) 

thr_continue の戻り値

thr_continue()は、正常終了時に 0 を返します。それ以外の戻り値は、エラーが発生したことを示します。以下の条件が検出されると、thr_continue() は失敗し、対応する値を戻します。


ESRCH

説明:

現在のプロセスに tid が存在しません。

pthread に相当するものがある同期関数 — 読み取り/書き込みロック

読み取り/書き込みロックを使用すると、同時に書き込み操作ができるスレッドを 1 つだけに制限する一方、読み取り操作は同時に複数のスレッドからできるようになります。この節の内容は次のとおりです。

すでに読み取りロックを保持しているスレッドがある場合、ほかのスレッドがさらに読み取りロックを獲得できますが、書き込みロックを獲得するときは待たなければなりません。すでに書き込みロックを保持しているスレッドがある場合、あるいは書き込みロックの獲得を待っているスレッドがある場合、ほかのスレッドは読み取りと書き込みのどちらのロックを獲得するときも待たなければなりません。

読み取り/書き込みロックは、相互排他ロックよりも低速です。しかし、読み取り/書き込みロックでは、書き込みの頻度が低く、かつ多数のスレッドから並行的に読み取られるようなデータを保護するときの性能を改善することができます。

このプロセス内のスレッドと別のプロセス内のスレッド間で同期をとるには、読み取り/書き込みロックを使用します。連携するそれらのプロセスの間で共有される書き込み可能なメモリーに、読み取り/書き込みロックの領域を確保します。この動作の読み取り/書き込みロックのマッピングについては、mmap(2) のマニュアルページを参照してください。

複数のスレッドが読み取り/書き込みロックを待っている場合のロックの獲得順序は、特に指定しなければ不定です。ただし、書き込み側がいつまでもロックを獲得できないような事態を回避するため、Solaris スレッドパッケージでは書き込み側が同じ優先順位の読み取り側より優先されます。

読み取り/書き込みロックは、使用する前に初期化する必要があります。

読み取り/書き込みロックの初期化

rwlp が指す読み取り/書き込みロックを初期化して、ロック状態を解除するには、rwlock_init(3C) を使用します。

rwlock_init の構文

#include <synch.h>  (or #include <thread.h>)

int rwlock_init(rwlock_t *rwlp, int type, void * 
arg);

type は、次のいずれかになります。

複数のスレッドから同じ読み取り/書き込みロックを同時に初期化してはいけません。0 に初期化したメモリーに領域を確保することによって、読み取り/書き込みロックを初期化することもできます。その場合は、type に USYNC_THREAD を指定したものとみなされます。一度初期化した読み取り/書き込みロックは、ほかのスレッドで使われている可能性があるので再初期化してはいけません。

POSIX スレッドの場合については、pthread_rwlock_init の構文」を参照してください。

プロセス内スコープでの読み取り/書き込みロックの初期化

#include <thread.h> 
rwlock_t rwlp; 
int ret; 
/* to be used within this process only */ 
ret = rwlock_init(&rwlp, USYNC_THREAD, 0); 

プロセス間スコープでの読み取り/書き込みロックの初期化

#include <thread.h> 
rwlock_t rwlp; 
int ret; 
/* to be used among all processes */ 
ret = rwlock_init(&rwlp, USYNC_PROCESS, 0); 

rwlock_init の戻り値

rwlock_init()は、正常終了時に 0 を返します。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると、この関数は失敗し、対応する値を返します。


EINVAL

説明:

引数が無効です。


EFAULT

説明:

rwlp または arg が不当なアドレスを指しています。

読み取りロックの獲得

rwlp が指す読み取り/書き込みロック上で読み取りロックを獲得するには、rw_rdlock(3C) を使用します。

rw_rdlock の構文

#include <synch.h> (or #include <thread.h>)

int rw_rdlock(rwlock_t *rwlp);

指定した読み取り/書き込みロックが書き込み用にすでにロックされている場合、呼び出しスレッドは書き込みロックが解放されるまでブロックされます。そうでなければ、呼び出しスレッドは読み取りロックを獲得します。POSIX スレッドの場合については、pthread_rwlock_rdlock の構文」を参照してください。

rw_rdlock の戻り値

rw_rdlock()は、正常終了時に 0 を返します。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると、この関数は失敗し、対応する値を返します。


EINVAL

説明:

引数が無効です。


EFAULT

説明:

rwlp が指すアドレスが正しくありません。

読み取りロックの獲得の試行

rwlp が指す読み取り/書き込みロック上に読み取りロックを獲得しようとするには、rw_tryrdlock(3C) を使用します。

rw_tryrdlock の構文

#include <synch.h>  (or #include <thread.h>)

int rw_tryrdlock(rwlock_t *rwlp);

読み取り/書き込みロックが書き込み用にロックされているとき、rw_tryrdlock() はエラーを返します。そうでなければ、呼び出しスレッドは読み取りロックを獲得します。POSIX スレッドの場合については、pthread_rwlock_tryrdlock の構文」を参照してください。

rw_tryrdlock の戻り値

rw_tryrdlock()は、正常終了時に 0 を返します。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると、この関数は失敗し、対応する値を返します。


EINVAL

説明:

引数が無効です。


EFAULT

説明:

rwlp が指すアドレスが正しくありません。


EBUSY

説明:

rwlp が指す読み取り/書き込みロックがすでにロックされています。

書き込みロックの獲得

rwlp が指す読み取り/書き込みロック上で書き込みロックを獲得するには、rw_wrlock(3C) を使用します。

rw_wrlock の構文

#include <synch.h>  (or #include <thread.h>)

int rw_wrlock(rwlock_t *rwlp);

指定した読み取り/書き込みロックが、読み取りまたは書き込み用にすでにロックされている場合、呼び出しスレッドは、すべての読み取りロックと書き込みロックが解放されるまでブロックされます。読み取り/書き込みロックの書き込みロックを保持できるスレッドは一度に 1 つに限られます。POSIX スレッドの場合については、pthread_rwlock_wrlock の構文」を参照してください。

rw_wrlock の戻り値

rw_wrlock()は、正常終了時に 0 を返します。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると、この関数は失敗し、対応する値を返します。


EINVAL

説明:

引数が無効です。


EFAULT

説明:

rwlp が指すアドレスが正しくありません。

書き込みロックの獲得の試行

rwlp が指す読み取り/書き込みロック上に読み取りロックを獲得しようとするには、rw_trywrlock(3C) を使用します。

rw_trywrlock の構文

#include <synch.h>  (or #include <thread.h>)

int rw_trywrlock(rwlock_t *rwlp);

読み取り/書き込みロックが読み取りまたは書き込み用にロックされているとき、rw_trywrlock() はエラーを返します。POSIX スレッドの場合については、pthread_rwlock_trywrlock の構文」を参照してください。

rw_trywrlock の戻り値

rw_trywrlock()は、正常終了時に 0 を返します。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると、この関数は失敗し、対応する値を返します。


EINVAL

説明:

引数が無効です。


EFAULT

説明:

rwlp が指すアドレスが正しくありません。


EBUSY

説明:

rwlp が指す読み取り/書き込みロックがすでにロックされています。

読み取り/書き込みロックの解除

rwlp が指す読み取り/書き込みロックを解除するには、rw_unlock(3C) を使用します。

rw_unlock の構文

#include <synch.h>  (or #include <thread.h>)

int rw_unlock(rwlock_t *rwlp);

解除の対象となる読み取り/書き込みロックは、ロックされていて、呼び出しスレッドが読み取り用または書き込み用に保持しているものでなければなりません。その読み取り/書き込みロックが使用可能になるのを待っているスレッドがほかにある場合は、そのスレッドのうちの 1 つがブロック解除されます。POSIX スレッドの場合については、pthread_rwlock_unlock の構文」を参照してください。

rw_unlock の戻り値

rw_unlock()は、正常終了時に 0 を返します。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると、この関数は失敗し、対応する値を返します。


EINVAL

説明:

引数が無効です。


EFAULT

説明:

rwlp が指すアドレスが正しくありません。

読み取り/書き込みロックの削除

rwlp が指す読み取り/書き込みロックに関連付けられた状態を削除するには、rwlock_destroy(3C) を使用します。

rwlock_destroy の構文

#include <synch.h>  (or #include <thread.h>)

int rwlock_destroy(rwlock_t *rwlp);

読み取り/書き込みロックの記憶領域は解放されません。POSIX スレッドの場合については、pthread_rwlock_destroy の構文」を参照してください。

例 6–1 では、銀行口座の例を使って、読み取り/書き込みロックについて説明します。口座残高に対して複数のスレッドが並行的に読み取り専用アクセスできますが、書き込みは 1 つのスレッドだけに制限されます。get_balance() 関数中のロックは、当座預金の残高 (checking_balance) と普通預金の残高 (saving_balance) を合計する演算が、不可分操作によって確実に行われるために必要です。


例 6–1 銀行口座の読み取り/書き込み

rwlock_t account_lock;
float checking_balance = 100.0;
float saving_balance = 100.0;
...
rwlock_init(&account_lock, 0, NULL);
...

float
get_balance() {
    float bal;

    rw_rdlock(&account_lock);
    bal = checking_balance + saving_balance;
    rw_unlock(&account_lock);
    return(bal);
}

void
transfer_checking_to_savings(float amount) {
    rw_wrlock(&account_lock);
    checking_balance = checking_balance - amount;
    saving_balance = saving_balance + amount;
    rw_unlock(&account_lock);
}

rwlock_destroy の戻り値

rwlock_destroy()は、正常終了時に 0 を返します。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると、この関数は失敗し、対応する値を返します。


EINVAL

説明:

引数が無効です。


EFAULT

説明:

rwlp が指すアドレスが正しくありません。

Solaris スレッドに類似した関数

表 6–3 Solaris スレッドに類似した関数

操作 

参照先 

スレッドの生成 

thr_create の構文」

最小のスタックサイズの取得 

thr_min_stack の構文」

スレッド識別子の取得 

thr_self の構文」

スレッドの実行明け渡し 

thr_yield の構文」

スレッドへのシグナルの送信 

thr_kill の構文」

呼び出しスレッドのシグナルマスクのアクセス 

thr_sigsetmask の構文」

スレッドの終了 

thr_exit の構文」

スレッドの終了待ち 

thr_join の構文」

スレッド固有データ用キーの作成 

thr_keycreate の構文」

スレッド固有データの設定 

thr_setspecific の構文」

スレッド固有データの取得 

thr_getspecific の構文」

スレッド優先順位の設定 

thr_setprio の構文」

スレッド優先順位の取得 

thr_getprio の構文」

スレッドの生成

thr_create(3C) ルーチンは、Solaris スレッドインタフェースに含まれるルーチンのうち、もっとも精巧なルーチンの 1 つです。

現在のプロセスに新しい制御スレッドを追加するときは、thr_create(3C) を使用します。POSIX スレッドの場合については、pthread_create の構文」を参照してください。

thr_create の構文

#include <thread.h>

int thr_create(void *stack_base, size_t stack_size,
          void *(*start_routine) (void *), void *arg, 
          long flags,
          thread_t *new_thread);

size_t thr_min_stack(void);

新しいスレッドは保留中のシグナルは継承しませんが、優先順位とシグナルマスクを継承します。

stack_base 。新しいスレッドが使用するスタックのアドレスを指定します。NULL を指定すると、新しいスレッドに stack_size バイト以上のサイズをもつスタックが割り当てられます。()

stack_size。新しいスレッドが使用するスタックのバイト数を指定します。stack_size が 0 の場合、デフォルトのサイズが使用されます。通常は 0 を指定してください。stack_size が 0 以外の場合は、thr_min_stack() の戻り値よりも大きい値を指定してください。

通常、スレッド用にスタック空間を割り当てる必要はありません。システムが、各スレッドのスタック用に 1M バイトの仮想メモリーをスワップ空間の予約なしで割り当てます。システムは、mmap(2)-MAP_NORESERVE オプションを使って割り当てを行います。

start_routine — 新しいスレッドが実行を開始するために使用する関数を指定します。start_routine() で指定した関数が終了すると、スレッドはその関数の戻り値を終了状態に設定して終了します。thr_exit の構文」を参照してください。

argvoid で記述される任意の変数で、通常は 4 バイト値です。それよりも大きな値を渡す場合は、引数が変数を指す方法で間接的に渡す必要があります。

引数は 1 つしか指定できません。複数の引数をとらせるためには、それらを構造体に入れるなどして、1 つのものとしてコーディングします。

flags — 生成されるスレッドの属性を指定します。通常は 0 を指定してください。

flags の値は、以下に示す引数のビット単位の論理和となります。


注 –

明示的な同期が割り当てられていないとき、停止していない切り離されたスレッドは失敗することがあります。失敗すると、そのスレッドの生成元が thr_create() から復帰する前に、そのスレッド ID が別の新しいスレッドに割り当てられます。


new_threadnew_threadNULL 以外の場合には、thr_create() が正常終了したときに新しいスレッドの ID が格納された場所を指しています。この引数が指す記憶領域は、呼び出し側の責任で確保しなければなりません。このスレッド識別子は、呼び出し側のプロセス内でだけ有効です。

スレッド識別子が特に必要でなければ、new_thread に NULL を指定してください。

thr_create の戻り値

thr_create() は、正常終了時に 0 を返します。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると、thr_create() は失敗し、対応する値を戻します。


EAGAIN

説明:

システム制限を超えました。たとえば、生成する LWP が多すぎます。


ENOMEM

説明:

新しいスレッドを生成するための十分なメモリーがありません。


EINVAL

説明:

stack_baseNULL でなく、しかも stack_sizethr_min_stack() の戻り値より小さな値を指定しました。

最小のスタックサイズの取得

スレッドの最小スタックサイズを取得するには、thr_min_stack(3C) を使用します。

Solaris スレッドでのスタックの動作は、通常は pthread の場合と同じです。スタックの設定と操作の詳細は、「スタックについて」を参照してください。

thr_min_stack の構文

#include <thread.h>

size_t thr_min_stack(void);

thr_min_stack() は、NULL スレッドの実行に必要な空間の量を返します。NULL スレッドは、NULL 手続きを実行するために生成されたスレッドです。実用的なスレッドに必要なスタック空間は、スタックの絶対最小サイズより大きいので、スタックサイズを小さくするときは十分注意してください。

スレッドが NULL 手続きでなく通常の手続きを実行する場合は、thr_min_stack() の戻り値よりも大きなスタックサイズを割り当てなければなりません。

ユーザーが指定したスタックを使用してスレッドが作成される場合には、このスレッドの実行に必要な空間量を確保する必要があります。動的にリンクされる実行環境の場合には、スレッドの最小スタック要件を決定するのがさらにむずかしくなります。

カスタムスタックは、次の 2 つの方法で指定できます。1 つは、thr_create() でスタックアドレスを NULL に指定し、スタック空間の割り当てをスレッドライブラリに任せる方法です。スタックサイズを指定するパラメータには、希望のサイズを指定します。

もう 1 つの方法は、thr_create() にスタックのポインタを渡して、スタックをすべて自分で管理する方法です。この場合は、スタック空間の割り当てだけでなく、割り当ての解放もユーザー自身で行う必要があります。つまり、スレッドを終了するときに、そのスタックをユーザーが解放しなければなりません。

独自のスタックを割り当てる場合は、mprotect(2) を呼び出して、スタックの最後に必ずレッドゾーンを付加してください。

通常、ユーザー独自のスタックが必要になることはまれです。実際、アプリケーション側が実行環境を完全に制御するなどのごく限られた状況でしか必要になりません。

ユーザーは、システムにスタックの割り当てを任せることができます。システムのデフォルトのスタックは、すべてのスレッドの要求を満たします。

thr_min_stack の戻り値

エラーが未定義です。

スレッド識別子の獲得

呼び出しスレッドの ID を取得するには、thr_self(3C) を使用します。POSIX スレッドの場合については、pthread_self の構文」を参照してください。

thr_self の構文

#include <thread.h>

thread_t thr_self(void);

thr_self の戻り値

エラーが未定義です。

スレッドの実行明け渡し

thr_yield(3C) は、現在のスレッドから、同じ優先順位かより高い優先順位をもつ別のスレッドに実行を譲ります。thr_yield() には、それ以外の効果はありません。ただし、thr_yield() の呼び出しスレッドが実行権を必ず譲るとは限りません。

thr_yield の構文

#include <thread.h>

void thr_yield(void);

thr_yield の戻り値

thr_yield() は何も返さず、errno を設定しません。

スレッドへのシグナルの送信

thr_kill(3C) は、スレッドにシグナルを送ります。POSIX スレッドの場合については、pthread_kill の構文」を参照してください。

thr_kill の構文

#include <thread.h>
#include <signal.h>
int thr_kill(thread_t target_thread, int sig);

thr_kill の戻り値

thr_kill() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、thr_kill() は失敗し、対応する値を返します。障害が発生したときには、シグナルは送信されません。


ESRCH

説明:

スレッド ID (thread) で指定されたスレッドに関連付けられたスレッドがありません。


EINVAL

説明:

sig 引数の値が 0 以外です。sig が無効であるか、サポートされていないシグナル番号です。

呼び出しスレッドのシグナルマスクのアクセス

呼び出しスレッドのシグナルマスクを変更または検査するには、thr_sigsetmask(3C) を使用します。

thr_sigsetmask の構文

#include <thread.h>
#include <signal.h>
int thr_sigsetmask(int how, const sigset_t *set, 
          sigset_t *oset);

thr_sigsetmask() は、呼び出しスレッドのシグナルマスクを変更または検査します。各スレッドは、スレッド専用のシグナルマスクを持っています。新しいスレッドは、呼び出しスレッドのシグナルマスクと優先順位を継承します。ただし、保留中のシグナルは継承の対象にはなりません。保留中のシグナルは、新しいスレッドでは空になります。

引数 set の値が NULL 以外の場合、set は、現在ブロックされているシグナルセットを変更するシグナルセットになります。set の値が NULL の場合、how の値は意味を持たず、スレッドのシグナルマスクは変更されません。この動作を利用して、現在ブロックされているシグナルについて問い合わせることができます。

how の値には、セットの変更方法を指定します。how は、次のいずれかの値をとります。

thr_sigsetmask の戻り値

thr_sigsetmask () は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、thr_sigsetmask() は失敗し、対応する値を返します。


EINVAL

説明:

set が NULL 以外なのに、how の値が未定義です。

スレッドの終了

スレッドを終了するには、thr_exit(3C) を使用します。POSIX スレッドの場合については、pthread_exit Syntax」を参照してください。

thr_exit の構文

#include <thread.h>

void thr_exit(void *status);

thr_exit の戻り値

thr_exit() は呼び出し側に値を返しません。

スレッドの終了待ち

ターゲットスレッドの終了を待つには、thr_join(3C) を使用します。POSIX スレッドの場合については、pthread_join の構文」を参照してください。

thr_join の構文

#include <thread.h>

int thr_join(thread_t tid, thread_t *departedid, void **status);

ターゲットスレッドは、現在のプロセスのメンバーでなければなりません。また、切り離されたスレッドやデーモンスレッドであってはなりません。

複数のスレッドが、同じスレッドの終了を待つことはできません。1 つのスレッドだけが正常に終了します。ほかのスレッドは、ESRCH エラーを発行して終了します。

thr_join() は、ターゲットスレッドがすでに終了している場合に、呼び出しスレッドの処理をブロックすることはありません。

thr_join、指定したスレッドの終了待ち

#include <thread.h>

thread_t tid;
thread_t departedid;
int ret;
void *status;

/* waiting to join thread "tid" with status */
ret = thr_join(tid, &departedid, &status);

/* waiting to join thread "tid" without status */
ret = thr_join(tid, &departedid, NULL);

/* waiting to join thread "tid" without return id and status */
ret = thr_join(tid, NULL, NULL); 

tid(thread_t) 0 の場合は、thr_join() はプロセス内の切り離されていない任意のスレッドの終了を待ちます。つまり、スレッド識別子を指定しなければ、切り離されていないスレッドのどれかが終了すると thr_join() が復帰します。

thr_join、任意のスレッドの終了待ち

#include <thread.h>

thread_t tid;
thread_t departedid;
int ret;
void *status;

/* waiting to join any non-detached thread with status */
ret = thr_join(0, &departedid, &status); 

thr_join() でスレッド識別子としてゼロを指定すると、プロセス内の切り離されていない任意のスレッドの終了を待ちます。departedid には、終了したスレッドのスレッド識別子が格納されます。

thr_join の戻り値

thr_join() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、thr_join() は失敗し、対応する値を返します。


ESRCH

説明:

ターゲットスレッド ID に対応する、切り離されていないスレッドが見つかりません。


EDEADLK

説明:

デッドロックが検出されたか、ターゲットスレッドの値に呼び出しスレッドが指定されています。

スレッド固有データ用キーの作成

thr_keycreate(3C) は、プロセス内のスレッド固有データを識別するために使用されるキーを割り当てます。このキーは、プロセス内のすべてのスレッドに大域的なキーです。各スレッドは、このキーの生成時に値を 1 つずつ割り当てます。

スレッド固有のデータは、POSIX スレッドの場合も Solaris スレッドの場合も、関数名と引数を除いて同じです。この節では、Solaris の関数の概要を説明します。POSIX スレッドの場合については、pthread_key_create の構文」を参照してください。

thr_keycreate の構文

#include <thread.h>

int thr_keycreate(thread_key_t *keyp,
    void (*destructor) (void *value));

keyp は、各結合スレッド固有の値を個別に管理します。各スレッドは、スレッド固有のデータへのアクセスを許可するために、最初に固有要素の keyp に結合されます。キーが生成されるときに、すべてのアクティブなスレッドに対して、新しいキーに値 NULL が割り当てられます。また、スレッドが生成されるときに、新しいスレッドのすでに生成されているすべてのキーには、値 NULL が割り当てられます。

オプションの destructor 関数は、各 keyp に関連付けることができます。スレッドが終了するときに、keyp に NULL 以外 destructor が関連付けられ、そのスレッドの keyp に NULL 以外の value が関連付けられている場合には、現在関連付けられている value を使用して destructor が呼び出されます。スレッドが終了するときに 1 つ以上の destructor が存在する場合には、デストラクタは不特定に呼び出されます。

thr_keycreate の戻り値

thr_keycreate() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、thr_keycreate() は失敗し、対応する値を返します。


EAGAIN

説明:

追加のスレッド固有のデータキーを生成するために必要なリソースがありません。または、キーの数が、PTHREAD_KEYS_MAX に指定されたプロセスごとの最大キー数を超えています。


ENOMEM

説明:

valuekeyp を関連付けるために必要なメモリーが不足しています。

スレッド固有データの値の設定

thr_setspecific(3C) は、呼び出しスレッドに対して、スレッド固有データキー keyvalue を結合します。POSIX スレッドの場合については、pthread_setspecific の構文」を参照してください。

thr_setspecific の構文

#include <thread.h>

int thr_setspecific(thread_key_t key, void *value);

thr_setspecific の戻り値

thr_setspecific() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、thr_setspecific () は失敗し、対応する値を返します。


ENOMEM

説明:

valuekeyp を関連付けるために必要なメモリーが不足しています。


EINVAL

説明:

keyp が無効です。

スレッド固有データの値の取得

thr_getspecific(3C) は、呼び出しスレッドの key に結合された現在の値を valuep が指す場所に格納します。POSIX スレッドの場合については、pthread_getspecific の構文」を参照してください。

thr_getspecific の構文

#include <thread.h>

int thr_getspecific(thread_key_t key, void **valuep);

thr_getspecific の戻り値

thr_getspecific() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、thr_getspecific () は失敗し、対応する値を返します。


ENOMEM

説明:

valuekeyp を関連付けるために必要なメモリーが不足しています。


EINVAL

説明:

keyp が無効です。

スレッド優先順位の設定

Solaris スレッドでは、優先順位が親と異なるスレッドを生成する場合、SUSPEND モードで生成します。スレッドの優先順位は、停止している間に thr_setprio(3C) 関数を呼び出して変更されます。thr_setprio() が終了すると、スレッドは実行を再開します。

優先順位の高いスレッドは、同期オブジェクトの競合を考慮しながら、優先順位の低いスレッドより優先されます。

thr_setprio の構文

thr_setprio(3C) は、tid に指定されたスレッドの優先順位を、現在のプロセス内で newprio に指定された優先順位に変更します。POSIX スレッドの場合については、pthread_setschedparam の構文」を参照してください。

#include <thread.h>

int thr_setprio(thread_t tid, int newprio)

スレッドの有効な優先順位の範囲は、スケジューリングポリシーによって異なります。

thread_t tid;
int ret;
int newprio = 20;

/* suspended thread creation */
ret = thr_create(NULL, NULL, func, arg, THR_SUSPENDED, &tid);

/* set the new priority of suspended child thread */
ret = thr_setprio(tid, newprio);

/* suspended child thread starts executing with new priority */
ret = thr_continue(tid);

thr_setprio の戻り値

thr_setprio() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、thr_setprio() は失敗し、対応する値を返します。


ESRCH

説明:

tid で指定した値が既存のスレッドを表していません。


EINVAL

説明:

priority の値は、指定されたスレッドのスケジューリングポリシーには無効です。


EPERM

説明:

呼び出し元は、優先順位を指定された値に設定するための適切なアクセス権を持っていません。

スレッド優先順位の取得

スレッドの現在の優先順位を取得するには、thr_getprio(3C) を使用します。各スレッドは生成側の優先順位を継承します。thr_getprio() は、tid で指定されたスレッドの現在の優先順位を、newprio が指している位置に格納します。POSIX スレッドの場合については、pthread_getschedparam の構文」を参照してください。

thr_getprio の構文

#include <thread.h>

int thr_getprio(thread_t tid, int *newprio)

thr_getprio の戻り値

thr_getprio() は、正常終了時に 0 を返します。次の条件が検出された場合、thr_getprio() は失敗し、対応する値を返します。


ESRCH

説明:

tid で指定した値が既存のスレッドを表していません。

pthread に相当するものがある同期関数 — 相互排他ロック

mutex の初期化

mp が指す mutex を初期化するには、mutex_init(3C) を使用します。POSIX スレッドの場合については、「mutex の初期化」を参照してください。

mutex_init(3C) の構文

#include <synch.h> 
#include <thread.h>

int mutex_init(mutex_t *mp, int type, void *arg)); 

type は、次のいずれかになります。

USYNC_PROCESS ロックした状態でプロセスが異常終了すると、次にそのロックを要求したスレッドは滞ります。これは、クライアントプロセスとロックを共有するシステムで起こる問題で、クライアントプロセスが強制的に終了されることがあり得るからです。ロックしたままプロセスが終了する問題を回避するには、USYNC_PROCESS_ROBUST で mutex をロックします。USYNC_PROCESS_ROBUST には次の 2 つの機能があります。

0 に初期化されたメモリーに領域を確保することによって mutex を初期化することもできます。その場合は typeUSYNC_THREAD を指定したものと仮定されます。

複数のスレッドから同じ mutex を同時に初期化してはいけません。相互排他ロックは、ほかのスレッドで使われている可能性があるので再初期化してはいけません。

プロセス内スコープでの mutex

#include <thread.h>

mutex_t mp;
int ret;

/* to be used within this process only */
ret = mutex_init(&mp, USYNC_THREAD, 0); 

プロセス間スコープでの mutex

#include <thread.h>

mutex_t mp;
int ret;

/* to be used among all processes */
ret = mutex_init(&mp, USYNC_PROCESS, 0); 

プロセス間スコープの確実な mutex

#include <thread.h>

mutex_t mp;
int ret;

/* to be used among all processes */
ret = mutex_init(&mp, USYNC_PROCESS_ROBUST, 0); 

mutex_init の戻り値

mutex_init() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、mutex_init() は失敗し、対応する値を返します。


EFAULT

説明:

mp が指すアドレスが正しくありません。


EINVAL

説明:

mpで指定された値が無効です。


ENOMEM

説明:

mutex の初期化に必要なメモリーが不足しています。


EAGAIN

説明:

mutex の初期化に必要なリソースが不足しています。


EBUSY

説明:

アクティブな mutex の再初期化を試行していることが検出されました。

mutex の削除

mp が指す mutex に関連付けられた状態を削除するには、mutex_destroy(3C) を使用します。mutex の記憶領域は解放されません。POSIX スレッドの場合については、pthread_mutex_destroy の構文」を参照してください。

mutex_destroy の構文

#include <thread.h>

int mutex_destroy (mutex_t *mp);

mutex_destroy の戻り値

mutex_destroy() は、正常終了時に 0 を返します。次の条件が検出された場合、mutex_destroy() は失敗し、対応する値を返します。


EFAULT

説明:

mp が指すアドレスが正しくありません。

mutex の獲得

mp が指す mutex をロックするには、mutex_lock(3C) を使用します。mutex がすでにロックされている場合、呼び出しスレッドは mutex が使用可能になるまでブロックされます。ブロックされたスレッドは、優先順位の高いキューで待機します。POSIX スレッドの場合については、pthread_mutex_lock の構文」を参照してください。

mutex_lock の構文

#include <thread.h>

int mutex_lock(mutex_t *mp);

mutex_lock の戻り値

mutex_lock() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、mutex_lock() は失敗し、対応する値を返します。


EFAULT

説明:

mp が指すアドレスが正しくありません。


EDEADLK

説明:

mutex はすでにロックされていて、呼び出しスレッドに保持されています。

mutex の解除

mp が指す mutex のロックを解除するには、mutex_unlock(3C) を使用します。この mutex はロックされていなければなりません。呼び出しスレッドは、最後にこの mutex をロックしたスレッド、すなわち所有者でなければなりません。POSIX スレッドの場合については、pthread_mutex_unlock の構文」を参照してください。

mutex_unlock の構文

#include <thread.h>

int mutex_unlock(mutex_t *mp);

mutex_unlock の戻り値

mutex_unlock() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、mutex_unlock() は失敗し、対応する値を返します。


EFAULT

説明:

mp が指すアドレスが正しくありません。


EPERM

説明:

呼び出しスレッドは mutex を所有していません。

mutex の獲得の試行

mp が指す mutex のロックを試行するには、mutex_trylock(3C) を使用します。この関数はブロックしない点を除いて、mutex_lock() と同じ働きをします。POSIX スレッドの場合については、pthread_mutex_trylock の構文」を参照してください。

mutex_trylock の構文

#include <thread.h>

int mutex_trylock(mutex_t *mp);

mutex_trylock の戻り値

mutex_trylock() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、mutex_trylock() は失敗し、対応する値を返します。


EFAULT

説明:

mp が指すアドレスが正しくありません。


EBUSY

説明:

アクティブな mutex の再初期化を試行していることが検出されました。

pthread に相当するものがある同期関数 — 条件変数

条件変数の初期化

cv が指す条件変数を初期化するには、cond_init(3C) を使用します。

cond_init の構文

#include <thread.h>

int cond_init(cond_t *cv, int type, int arg);

type は、次のいずれかになります。

0 に初期化されたメモリーに領域を確保することによって、条件変数を初期化することもできます。その場合は、type に USYNC_THREAD を指定したものと仮定されます。

複数のスレッドから、同じ条件変数を同時に初期化してはいけません。一度初期化した条件変数はほかのスレッドが使用している可能性があるので、再初期化してはいけません。

POSIX スレッドの場合については、pthread_condattr_init の構文」を参照してください。

プロセス内スコープでの条件変数

#include <thread.h>

cond_t cv;
int ret;

/* to be used within this process only */
ret = cond_init(cv, USYNC_THREAD, 0); 

プロセス間スコープでの条件変数

#include <thread.h>

cond_t cv;
int ret;

/* to be used among all processes */
ret = cond_init(&cv, USYNC_PROCESS, 0); 

cond_init の戻り値

cond_init() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、cond_init() は失敗し、対応する値を返します。


EFAULT

説明:

cv が指すアドレスが正しくありません。


EINVAL

説明:

type を認識できません。

条件変数の削除

cv が指す条件変数に関連付けられた状態を削除するには、cond_destroy(3C) を使用します。条件変数を格納する領域は解放されません。POSIX スレッドの場合については、pthread_condattr_destroy の構文」を参照してください。

cond_destroy の構文

#include <thread.h>

int cond_destroy(cond_t *cv);

cond_destroy の戻り値

cond_destroy() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、cond_destroy() は失敗し、対応する値を返します。


EFAULT

説明:

cv が指すアドレスが正しくありません。


EBUSY

説明:

アクティブな条件変数の削除を試行していることが検出されました。

条件変数によるブロック

mp が指す mutex を不可分的に解放することにより、cv が指す条件変数上で呼び出しスレッドをブロックするには、cond_wait(3C) を使用します。ブロックされたスレッドを呼び起こすには、cond_signal()cond_broadcast() を使います。また、スレッドはシグナルや fork() の割り込みによっても呼び起こすことができます。

cond_wait() が戻るときは、たとえエラーを戻したときでも、常に mutex は呼び出しスレッドがロックし保持している状態にあります。

cond_wait の構文

#include <thread.h>

int cond_wait(cond_t *cv, mutex_t *mp);

cond_wait の戻り値

cond_wait() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、cond_wait() は失敗し、対応する値を返します。


EFAULT

説明:

cv が指すアドレスが正しくありません。


EINTR

説明:

シグナルにより、待機中に割り込みが発生しました。

条件変数による指定時刻付きブロック

cond_timedwait(3C) は、cond_wait() と非常によく似ています。ただし、cond_timedwait() は、abstime で指定された時刻を過ぎるとブロック状態を解除します。POSIX スレッドの場合については、pthread_cond_timedwait の構文」を参照してください。

cond_timedwait の構文

#include <thread.h>

int cond_timedwait(cond_t *cv, mutex_t *mp, timestruct_t abstime);

cond_timedwait() が戻るときは、たとえエラーを戻したときでも、常に mutex は呼び出しスレッドがロックし保持している状態にあります。

cond_timedwait() のブロック状態が解除されるのは、条件変数にシグナルが送られたときか、最後の引数で指定した時刻を過ぎたときです。時間切れの指定は時刻で行うため、時間切れの時刻を再計算する必要がないので、効率的に条件を再評価できます。

cond_timedwait の戻り値

cond_timedwait() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、cond_timedwait() は失敗し、対応する値を返します。


EFAULT

説明:

cv が指すアドレスが正しくありません。


ETIME

説明:

abstime で指定された時刻を過ぎています。


EINVAL

説明:

abstime が無効です。

時間間隔によるブロック

cond_reltimedwait(3C) は、3 番目の引数の値を除いて、cond_timedwait() と非常によく似ています。cond_reltimedwait() の 3 番目の引数には、絶対的な時刻ではなく、相対的な時間間隔値を指定します。POSIX スレッドの場合については、pthread_cond_reltimedwait_np(3C) のマニュアルページを参照してください。

cond_reltimedwait() が戻るときは、たとえエラーを戻したときでも、常に mutex は呼び出しスレッドがロックし保持している状態にあります。cond_reltimedwait() のブロック状態が解除されるのは、条件変数にシグナルが送られたときか、最後の引数で指定した時間間隔が経過したときです。

cond_reltimedwait の構文

#include <thread.h>

int cond_reltimedwait(cond_t *cv, mutex_t *mp,
    timestruct_t reltime);

cond_reltimedwait の戻り値

cond_reltimedwait() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、cond_reltimedwait () は失敗し、対応する値を返します。


EFAULT

説明:

cv が指すアドレスが正しくありません。


ETIME

説明:

reltime で指定された時間を過ぎています。

特定のスレッドのブロック解除

cv が指す条件変数でブロックされている 1 つのスレッドのブロックを解除するには、cond_signal(3C) を使用します。スレッドがブロックされていない条件変数に対して cond_signal() を実行しても無視されます。

cond_signal の構文

#include <thread.h>

int cond_signal(cond_t *cv);

cond_signal の戻り値

cond_signal() は、正常終了時に 0 を返します。次の条件が検出された場合、cond_signal() は失敗し、対応する値を返します。


EFAULT

説明:

cv が指すアドレスが正しくありません。

全スレッドのブロック解除

cv が指す条件変数でブロックされているすべてのスレッドのブロックを解除するには、cond_broadcast(3C) を使用します。スレッドがブロックされていない条件変数に対して cond_broadcast() を実行しても無視されます。

cond_broadcast の構文

#include <thread.h>

int cond_broadcast(cond_t *cv);

cond_broadcast の戻り値

cond_broadcast() は、正常終了時に 0 を返します。次の条件が検出された場合、cond_broadcast() は失敗し、対応する値を返します。


EFAULT

説明:

cv が指すアドレスが正しくありません。

pthread に相当するものがある同期関数 — セマフォー

セマフォーの操作は Solaris オペレーティング環境と POSIX 環境の両方で同じです。関数名は、Solaris オペレーティング環境で sema_ だった関数名が pthread では sem_ に変わっています。この節の内容は次のとおりです。

セマフォーの初期化

sp が指すセマフォー変数を count の値で初期化するには、sema_init(3C) を使用します。

sema_init の構文

#include <thread.h>

int sema_init(sema_t *sp, unsigned int count, int type,
    void *arg);

type は、次のいずれかになります。

複数のスレッドから同時に同じセマフォーを初期化してはいけません。一度初期化したセマフォーは、ほかのスレッドが使用している可能性があるので、再初期化してはいけません。

プロセス内スコープでのセマフォー

#include <thread.h>

sema_t sp;
int ret;
int count;
count = 4;

/* to be used within this process only */
ret = sema_init(&sp, count, USYNC_THREAD, 0); 

プロセス間スコープでのセマフォー

#include <thread.h>

sema_t sp;
int ret;
int count;
count = 4;

/* to be used among all the processes */
ret = sema_init (&sp, count, USYNC_PROCESS, 0); 

sema_init の戻り値

sema_init() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、sema_init() は失敗し、対応する値を返します。


EINVAL

説明:

sp が無効なセマフォーを参照しています。


EFAULT

説明:

sp または arg が無効なアドレスを指しています。

セマフォーの加算

sp が指すセマフォーを不可分的に加算するには、sema_post(3C) を使用します。そのセマフォーでブロックされているスレッドがある場合は、そのスレッドのうちの 1 つのスレッドがブロック解除されます。

sema_post の構文

#include <thread.h>

int sema_post(sema_t *sp);

sema_post の戻り値

sema_post() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、sema_post() は失敗し、対応する値を返します。


EINVAL

説明:

sp が無効なセマフォーを参照しています。


EFAULT

説明:

sp が指すアドレスが正しくありません。


EOVERFLOW

説明:

sp が指すセマフォーの値が SEM_VALUE_MAX を超えています。

セマフォーの値によるブロック

sp が指すセマフォー内のカウントが 0 より大きくなるまで呼び出しスレッドをブロックするには、sema_wait(3C) を使用します。カウントが 0 より大きくなると、不可分的に減算します。

sema_wait の構文

#include <thread.h>

int sema_wait(sema_t *sp);

sema_wait の戻り値

sema_wait() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、sema_wait() は失敗し、対応する値を返します。


EINVAL

説明:

sp が無効なセマフォーを参照しています。


EINTR

説明:

シグナルにより、待機中に割り込みが発生しました。

セマフォーの減算

カウントが 0 より大きくなったとき、sp が指すセマフォー内のカウントを不可分的に減算するには、sema_trywait(3C) を使用します。この関数はブロックしない点を除いて、sema_wait() と同じ働きをします。

sema_trywait の構文

#include <thread.h>

int sema_trywait(sema_t *sp);

sema_trywait の戻り値

sema_trywait() は、正常終了時に 0 を返します。次のいずれかの条件が検出された場合、sema_trywait() は失敗し、対応する値を返します。


EINVAL

説明:

sp が無効なセマフォーを参照しています。


EBUSY

説明:

sp が指すセマフォーのカウントが 0 です。

セマフォーの削除

sp が指すセマフォーに関連付けられた状態を削除するには、sema_destroy(3C) を使用します。セマフォーの記憶領域は解放されません。

sema_destroy(3C) の構文

#include <thread.h>

int sema_destroy(sema_t *sp);

sema_destroy(3C) の戻り値

sema_destroy() は、正常終了時に 0 を返します。次の条件が検出された場合、sema_destroy() は失敗し、対応する値を返します。


EINVAL

説明:

sp が無効なセマフォーを参照しています。

プロセスの境界を越えた同期

今までに説明した 4 種類の同期プリミティブは、プロセスの境界を越えて使用するように設定できます。この境界を越えた設定を行うには、まず、その同期変数が共有メモリーセグメントに確保されるようにします。次に、USYNC_PROCESS 型に設定された適切な初期化ルーチン (init) を呼び出します。

型が USYNC_PROCESS に設定されている場合、同期変数の操作は、typeUSYNC_THREAD であるときと同様に行われます。

mutex_init(&m, USYNC_PROCESS, 0);
rwlock_init(&rw, USYNC_PROCESS, 0);
cond_init(&cv, USYNC_PROCESS, 0);
sema_init(&s, count, USYNC_PROCESS, 0);

「生産者 / 消費者」問題の例

例 6–2 に、「生産者 / 消費者」問題の生産者と消費者をそれぞれ別のプロセスで示します。メインルーチンは、子プロセスと共有されているメモリーを 0 で初期化し、それを自分のアドレス空間にマッピングします。mutex_init()cond_init() を呼び出さなければならないのは、それらの同期変数のタイプが USYNC_PROCESS だからです。

子プロセスが 1 つ生成され、消費者の処理が実行されます。親プロセスは生産者の処理を実行します。

この例では、生産者と消費者を呼び出す各駆動ルーチンも示しています。producer_driver は、stdin から文字を読み込み、producer を呼び出します。consumer_driver は、consumer を呼び出して文字を受け取り、stdout に書き出します。

例 6–2 のデータ構造は、条件変数によるソリューションで使用したものと同じです。「片方向リンクリストの入れ子のロック」を参照してください。


例 6–2 USYNC_PROCESS を使用した「生産者 / 消費者」問題

main() {
    int zfd;
    buffer_t *buffer;

    zfd = open(“/dev/zero”, O_RDWR);
    buffer = (buffer_t *)mmap(NULL, sizeof(buffer_t),
        PROT_READ|PROT_WRITE, MAP_SHARED, zfd, 0);
    buffer->occupied = buffer->nextin = buffer->nextout = 0;

    mutex_init(&buffer->lock, USYNC_PROCESS, 0);
    cond_init(&buffer->less, USYNC_PROCESS, 0);
    cond_init(&buffer->more, USYNC_PROCESS, 0);
    if (fork() == 0)
        consumer_driver(buffer);
    else
        producer_driver(buffer);
}

void producer_driver(buffer_t *b) {
    int item;

    while (1) {
        item = getchar();
        if (item == EOF) {
            producer(b, `\0');
            break;
        } else
            producer(b, (char)item);
    }
}

void consumer_driver(buffer_t *b) {
    char item;

    while (1) {
        if ((item = consumer(b)) == '\0')
            break;
        putchar(item);
    }
}

子プロセスが 1 つ生成され、消費者の処理が実行されます。親プロセスは生産者の処理を実行します。

fork() と Solaris スレッドに関する問題

Solaris 10 リリースより前のリリースでは、Solaris スレッドと POSIX スレッドによる fork() の定義が異なっていました。fork() の問題の詳細は、「プロセスの作成: execexit の問題」を参照してください。

Solaris libthread は、fork()fork1() の両方をサポートしていました。fork() 呼び出しには、「汎用 fork」セマンティクスがあります。fork() は、スレッド、LWPを含めて、プロセス内のすべてを複製し、親の完全なクローンを生成しました。一方、fork1() 呼び出しで作成されるクローンはスレッドを 1 つしかもちませんでした。プロセスの状態とアドレス空間は複製されますが、スレッドについては呼び出しスレッドが複製されるだけでした。

POSIX libpthread は 、fork() のみをサポートしていました。そのセマンティクスは、Solaris スレッドにおける fork1() と同じです。

fork() のセマンティクスが「汎用 fork」と「fork1」のどちらになるかは、どちらのライブラリを使用するかで決まりました。-lthread を使ってリンクすれば、fork() に「汎用 fork」セマンティクスが割り当てられます。-lpthread を使ってリンクすれば、fork() に「fork1」セマンティクスが割り当てられます。

Solaris 10 リリースから、Solaris スレッドと POSIX スレッドの fork() のセマンティクスが共通になりました。つまり、 fork1() セマンティクスが呼び出し側だけを複製するようになりました。すべてを複製するセマンティクスが必要なアプリケーションには、新しい関数 forkall () が提供されています。

詳細は、「マルチスレッドプログラムのコンパイルとリンク」を参照してください。