Provided by: manpages-ja-dev_0.5.0.0.20221215+dfsg-1_all bug

名前

       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)) で設定されていない限り、どのような 結果に
       なるかは不定である。

返り値

       これらの関数は値を返さない。

エラー

       エラーはない。

属性

       この節で使用されている用語の説明については、 attributes(7) を参照。

       ┌────────────────────────┬───────────────┬─────────┐
       │インターフェース属性      │
       ├────────────────────────┼───────────────┼─────────┤
       │pthread_cleanup_push(), │ Thread safety │ MT-Safe │
       │pthread_cleanup_pop()   │               │         │
       └────────────────────────┴───────────────┴─────────┘

準拠

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

注意

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