名前

sem_wait, sem_timedwait, sem_trywait - セマフォをロックする

書式

#include <semaphore.h>
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
-pthread とリンクする。
glibc 向けの機能検査マクロの要件 ( feature_test_macros(7) 参照):
sem_timedwait(): _POSIX_C_SOURCE >= 200112L

説明

sem_wait() は sem が指すセマフォの値を 1 減らす (ロックする)。 セマフォの値が 0 より大きい場合、減算が実行され、関数は直ちに復帰する。 セマフォの現在値が 0 の場合には、減算を実行できるようになる (つまり、セマフォの値が 0 より大きな値になる) まで、もしくは シグナルハンドラーによって呼び出しが中断されるまで、 関数呼び出しは停止 (block) する。
sem_trywait() は sem_wait() と同じだが、セマフォ値の減算をすぐに実行できなかった場合に、 停止 (block) するのではなくエラーで復帰する ( errnoEAGAIN がセットされる) 点が異なる。
sem_timedwait() は sem_wait() と同じだが、セマフォ値の減算をすぐに実行できなかった場合に 関数呼び出しが停止する時間の上限を abs_timeout で指定する点が異なる。 abs_timeout 引数は、タイムアウト時刻を指定する構造体へのポインターである。 この構造体には、タイムアウト時刻を時刻紀元 (Epoch; 1970-01-01 00:00:00 +0000 (UTC)) からの 経過時間 (秒+ナノ秒) で指定する。 構造体は以下のように定義されている:

struct timespec {
    time_t tv_sec;      /* Seconds */
    long   tv_nsec;     /* Nanoseconds [0 .. 999999999] */
};

関数呼び出し時点ですでにタイムアウトに指定した時刻が過ぎており、 かつセマフォをすぐにロックできなかった場合は、 sem_timedwait() はタイムアウトエラー ( errnoETIMEDOUT がセットされる) で失敗する。
セマフォ操作がすぐに実行できるときは、 abs_timeout がどんな値であっても sem_timedwait() が失敗することは決してない。さらにいうと、この場合には abs_timeout の正当性の検査は行われない。

返り値

成功すると、これらの関数は 0 を返す。 エラーの場合、セマフォの値を変更せずに、-1 を返し、 errno にエラーを示す値をセットする。

エラー

EINTR
呼び出しはシグナルハンドラーにより中断された。 signal(7) 参照。
EINVAL
sem は有効なセマフォではない。
sem_trywait() の場合には、上記に加えて以下のエラーも起こる。
EAGAIN
停止 (block) せずにロック操作を完了できなかった (つまり、 セマフォの現在の値が 0 であった)。
sem_timedwait() の場合、以下のエラーも起こる。
EINVAL
abs_timeout.tv_nsecs の値が 0 未満、もしくは 1,000,000,000 以上である。
ETIMEDOUT
セマフォのロックに成功する前に時間切れとなった。

属性

この節で使用されている用語の説明については、 attributes(7) を参照。
インターフェース 属性
sem_wait(), sem_trywait(), sem_timedwait() Thread safety MT-Safe

準拠

POSIX.1-2001, POSIX.1-2008.

以下に示す (ちょっとした) プログラムは名前なしセマフォの操作を行う。 プログラムはコマンドライン引数を 2 つ取る。 最初の引数には、 SIGALRM シグナルを生成するためのアラームタイマーの設定に使われる値を 秒単位で指定する。このシグナルハンドラーは、 main() 内で sem_timedwait() を使って待っているセマフォを、 sem_post(3) を使って加算する。 2番目の引数には、 sem_timedwait() に渡すタイムアウトまでの時間を秒単位で指定する。

$  ./a.out 2 3
About to call sem_timedwait()
sem_post() from handler
sem_timedwait() succeeded
$  ./a.out 2 1
About to call sem_timedwait()
sem_timedwait() timed out

プログラムのソース

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <time.h>
#include <assert.h>
#include <errno.h>
#include <signal.h>
sem_t sem;
#define handle_error(msg) \ do { perror(msg); exit(EXIT_FAILURE); } while (0)
static void handler(int sig) { write(STDOUT_FILENO, "sem_post() from handler\n", 24); if (sem_post(&sem) == -1) { write(STDERR_FILENO, "sem_post() failed\n", 18); _exit(EXIT_FAILURE); } }
int main(int argc, char *argv[]) { struct sigaction sa; struct timespec ts; int s;
if (argc != 3) { fprintf(stderr, "Usage: %s <alarm-secs> <wait-secs>\n", argv[0]); exit(EXIT_FAILURE); }
if (sem_init(&sem, 0, 0) == -1) handle_error("sem_init");
/* Establish SIGALRM handler; set alarm timer using argv[1] */
sa.sa_handler = handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGALRM, &sa, NULL) == -1) handle_error("sigaction");
alarm(atoi(argv[1]));
/* Calculate relative interval as current time plus number of seconds given argv[2] */
if (clock_gettime(CLOCK_REALTIME, &ts) == -1) handle_error("clock_gettime");
ts.tv_sec += atoi(argv[2]);
printf("main() about to call sem_timedwait()\n"); while ((s = sem_timedwait(&sem, &ts)) == -1 && errno == EINTR) continue; /* Restart if interrupted by handler */
/* Check what happened */
if (s == -1) { if (errno == ETIMEDOUT) printf("sem_timedwait() timed out\n"); else perror("sem_timedwait"); } else printf("sem_timedwait() succeeded\n");
exit((s == 0) ? EXIT_SUCCESS : EXIT_FAILURE); }

関連項目

clock_gettime(2), sem_getvalue(3), sem_post(3), sem_overview(7), time(7)

この文書について

この man ページは Linux man-pages プロジェクトのリリース 5.10 の一部である。プロジェクトの説明とバグ報告に関する情報は https://www.kernel.org/doc/man-pages/ に書かれている。

Recommended readings

Pages related to sem_timedwait you should read also: