Provided by: manpages-ja-dev_0.5.0.0.20210215+dfsg-1_all
名前
pthread_cleanup_push, pthread_cleanup_pop - スレッドの キャンセルクリーンアップハンドラー の push/pop を行う
書式
#include <pthread.h> void pthread_cleanup_push(void (*routine)(void *), void *arg); void pthread_cleanup_pop(int execute); -pthread でコンパイルしてリンクする。
説明
これらの関数は、呼び出したスレッドのスレッドキャンセル時のクリーンアッ プハンドラーのス タックの操作を行う。クリーンアップハンドラーは、スレッドが キャンセルされた場合 (や以下で 説明する他の種々の状況において) 自動的に 実行される関数である。例えば、mutex のロック解除 を行い、プロセス内の 他のスレッドが利用できるようにする関数などが考えられる。 pthread_cleanup_push() 関数は、 routine をクリーンアップ ハンドラーのスタックの一番上に プッシュする。 routine が後で 起動される際には、 arg が関数の引き数と渡される。 pthread_cleanup_pop() 関数は、クリーンアップハンドラーの スタックの一番上のルーチンを削除 する。 execute が 0 以外の場合にはそのルーチンを追加で実行する。 キャンセルクリーンアップハンドラーは、以下に示す場合に スタックから取り出され実行される。 1. スレッドがキャンセルされた際に、スタックに登録された全てのクリーン アップハンドラーが取 り出されて、実行される。クリーンアップハンドラーの 実行は、スタックに登録されたのと逆の 順序で行われる。 2. スレッドが pthread_exit(3) を呼び出して終了する際に、全てのクリーン アップハンドラーが 上の項目で述べたのと同様に実行される。 (スレッドがスレッド開始関数からの return の実行 により終了する場合に は、クリーンアップハンドラーは呼び出されない。) 3. スレッドが 0 以外の execute 引き数で pthread_cleanup_pop() を 呼び出した際に、スタック の一番上のクリーンアップハンドラーが取り出されて 実行される。 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)) で設定されていない限り、どのような 結果に なるかは不定である。
返り値
これらの関数は値を返さない。
エラー
エラーはない。
準拠
POSIX.1-2001.
注意
Linux では、関数 pthread_cleanup_push() と pthread_cleanup_pop() は、それぞれ '{' と '}' を含むテキストに展開する マクロとして実装されている。このことは、これらの関数を対で呼び出 した スコープ内で宣言された変数は、そのスコープの中でしか参照できない ということを意味して いる。 POSIX.1 には、括弧を含む pthread_cleanup_push() と pthread_cleanup_pop() のブロックをその ままにしたままで、 return, break, continue, goto を使った場合の影響は 不定であると書かれて いる。 移植性が必要なアプリケーションではこれを行うのは避けるべきである。
例
以下のプログラムは、このページで説明した関数の簡単な使用例を示すもので ある。このプログラ ムは pthread_cleanup_push() と pthread_cleanup_pop() で囲まれたループを実行するスレッドを 作成する。 このループではグローバル変数 cnt を 1 秒に 1 ずつ増やしていく。 指定されたコマ ンドライン引き数の内容に基づいて、メインスレッドはもう一 つのスレッドにキャンセル要求を 送ったり、もう一つのスレッドがループを 抜けて (return を呼び出して) 正常終了するようにグ ローバル変数を 設定したりする。 以下のシェルセッションでは、メインスレッドはもう一つのスレッドに キャンセル要求を送信す る。 $ ./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 以外を渡したからである。 プログラムのソース #include <pthread.h> #include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #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); }
関連項目
pthread_cancel(3), pthread_cleanup_push_defer_np(3), pthread_setcancelstate(3), pthread_testcancel(3), pthreads(7)
この文書について
この man ページは Linux man-pages プロジェクトのリリース 3.79 の一部 である。プロジェクト の説明とバグ報告に関する情報は http://www.kernel.org/doc/man-pages/ に書かれている。