#include <pthread.h> void pthread_cleanup_push(void (*routine)(void *), void *arg); void pthread_cleanup_pop(int execute); -pthread でコンパイルしてリンクする。
pthread_cleanup_push() 関数は、 routine をクリーンアップ ハンドラーのスタックの一番上にプッシュする。 routine が後で 起動される際には、 arg が関数の引数と渡される。
pthread_cleanup_pop() 関数は、クリーンアップハンドラーの スタックの一番上のルーチンを削除する。 execute が 0 以外の場合にはそのルーチンを追加で実行する。
キャンセルクリーンアップハンドラーは、以下に示す場合に スタックから取り出され実行される。
POSIX.1 では、 pthread_cleanup_push() と pthread_cleanup_pop() を それぞれ '{' と '}' を含むテキストに展開するマクロと して実装することを許容している。 このため、呼び出し側では、これらの関数の呼び出しが同じ関数の中で対と なり、かつ文法的に同じネストレベル (nesting level) になることを保証 しなければならない。 (言い換えると、クリーンアップハンドラーは、コード の特定のセクションの実行の中でのみ設定するものであると言える。)
longjmp(3) (siglongjmp(3)) の呼び出しは、 pthread_cleanup_push() や pthread_cleanup_pop() の呼び出しが対と なる呼び出しがない状態で行われた場合には、どのような結果になるかは不定 である。これは jump バッファーは setjmp(3) (sigsetjmp(3)) により設 定されるからである。同様に、クリーンアップハンドラー内からの longjmp(3) (siglongjmp(3)) の呼び出しも、jump バッファーがハンドラー 内で setjmp(3) (sigsetjmp(3)) で設定されていない限り、どのような 結果になるかは不定である。
インターフェース | 属性 | 値 |
pthread_cleanup_push(), pthread_cleanup_pop() | Thread safety | MT-Safe |
POSIX.1 には、括弧を含む pthread_cleanup_push() と pthread_cleanup_pop() のブロックをそのままにしたままで、 return, break, continue, goto を使った場合の影響は 不定であると書かれている。 移植性が必要なアプリケーションではこれを行うのは避けるべきである。
以下のシェルセッションでは、メインスレッドはもう一つのスレッドに キャンセル要求を送信する。
$ ./a.out New thread started cnt = 0 cnt = 1 Canceling thread Called clean-up handler Thread was canceled; cnt = 0
上記の実行例から、スレッドがキャンセルされ、 キャンセルクリーンアップハンドラーが呼び出され、 グローバル変数 cnt の値が 0 にリセットされていることが確認できる。
次の実行例では、メインプログラムはグローバル変数を設定して、 もう一つのスレッドが正常終了するようにしている。
$ ./a.out x New thread started cnt = 0 cnt = 1 Thread terminated normally; cnt = 2
上記では、 (cleanup_pop_arg が 0 なので) クリーンアップハンドラーは 実行されておらず、その結果 cnt の値はリセットされていないことが 分かる。
次の実行例では、メインプログラムはグローバル変数を設定して、 もう一つのスレッドが正常終了するようにし、さらに cleanup_pop_arg に 0 以外の値を渡している。
$ ./a.out x 1 New thread started cnt = 0 cnt = 1 Called clean-up handler Thread terminated normally; cnt = 0
上記では、スレッドはキャンセルされていないが、クリーンアップハンドラーが実行されたことが分かる。これは pthread_cleanup_pop() の引数に 0 以外を渡したからである。
#define handle_error_en(en, msg) \
do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
static int done = 0; static int cleanup_pop_arg = 0; static int cnt = 0;
static void
cleanup_handler(void *arg)
{
printf("Called clean-up handler\n");
cnt = 0;
}
static void *
thread_start(void *arg)
{
time_t start, curr;
printf("New thread started\n");
pthread_cleanup_push(cleanup_handler, NULL);
curr = start = time(NULL);
while (!done) {
pthread_testcancel(); /* A cancellation point */
if (curr < time(NULL)) {
curr = time(NULL);
printf("cnt = %d\n", cnt); /* A cancellation point */
cnt++;
}
}
pthread_cleanup_pop(cleanup_pop_arg);
return NULL;
}
int
main(int argc, char *argv[])
{
pthread_t thr;
int s;
void *res;
s = pthread_create(&thr, NULL, thread_start, NULL);
if (s != 0)
handle_error_en(s, "pthread_create");
sleep(2); /* Allow new thread to run a while */
if (argc > 1) {
if (argc > 2)
cleanup_pop_arg = atoi(argv[2]);
done = 1;
} else {
printf("Canceling thread\n");
s = pthread_cancel(thr);
if (s != 0)
handle_error_en(s, "pthread_cancel");
}
s = pthread_join(thr, &res);
if (s != 0)
handle_error_en(s, "pthread_join");
if (res == PTHREAD_CANCELED)
printf("Thread was canceled; cnt = %d\n", cnt);
else
printf("Thread terminated normally; cnt = %d\n", cnt);
exit(EXIT_SUCCESS);
}