EN JA
PTHREAD_CLEANUP(3)
PTHREAD_CLEANUP(3) FreeBSD Library Functions Manual PTHREAD_CLEANUP(3)
 
 

名前

pthread_cleanup_push, pthread_cleanup_pop, pthread_cleanup_push_defer_np, pthread_cleanup_pop_restore_np -クリーンアップハンドラを登録および削除する
 

書式

#include <pthread.h>
 
void pthread_cleanup_push(void (* routine ) (void *), void * arg );
 
void pthread_cleanup_pop(int execute );
 
void pthread_cleanup_push_defer_np(void (* routine ) (void *), void * arg );
 
void pthread_cleanup_pop_restore_np(int execute );
 

説明

クリーンアップハンドラは、 pthread_exit(3) が呼び出されたり、取り消しされたりしてスレッドが終了するときに呼び出される関数である。クリーンアップハンドラはスタック風の規則にならって登録および削除される。
 
クリーンアップハンドラの目的は、スレッドが終了するときに保持しているかもしれない資源を解放することである。殊に、スレッドがロック中の mutex を保持したまま終了したり取り消しされたりすると、その mutex は永久にロックされたままで、ほかのスレッドが正常に実行できなくなってしまう。このことを防ぐ最もよい方法は、 mutex をロックする直前に、 mutex のロックを解除するためのクリーンアップハンドラを登録することである。同じように、クリーンアップハンドラはスレッドの終了時に malloc(3) で確保されたメモリブロックを解放したりファイルディスクリプターをクローズしたりするのに使用できる。
 
pthread_cleanup_push は関数 routine を引数 arg とともにクリーンアップハンドラとして登録する。この時点から対応する pthread_cleanup_pop までの間、そのスレッドが pthread_exit(3) または取り消しによって終了する時に、関数 routine が引数 arg をともなって呼び出されるようになる。終了する時点で複数のクリーンアップハンドラが有効になっている場合は、クリーンアップハンドラは LIFO 順に呼び出される: すなわち、最後に登録されたハンドラが最初に呼び出される。
 
pthread_cleanup_pop は、最後に登録されたクリーンアップハンドラを削除する。引数 execute が 0 でない場合、 pthread_cleanup_pop はハンドラを実行する。すなわち、関数 routine を引数 arg をともなって呼び出す。引数 execute が 0 の場合は、ハンドラが削除されるだけで、実行されることはない。
 
対応する pthread_cleanup_pushpthread_cleanup_pop の対は、同じ関数内の、同じブロック階層になければならない。実際、 pthread_cleanup_pushpthread_cleanup_pop はマクロであり、 pthread_cleanup_push のマクロ展開には開き括弧 { が含まれていて、それに対応する閉じ括弧 } は、対応する pthread_cleanup_pop のマクロ展開に含まれている。
 
pthread_cleanup_push_defer_np は、 pthread_cleanup_pushpthread_setcanceltype(3) を組み合わせた、ポータブルでない拡張である。 pthread_cleanup_push とまったく同じようにクリーンアップハンドラを登録するが、同時にその時点の取り消し型を保存し、取り消し型を遅延 (deferred) に変更する。これによって、スレッドの取り消し型が非同期 (asynchronous) であってもクリーンアップ機構が有効になることが保証される。
 
pthread_cleanup_pop_restore_nppthread_cleanup_push_defer_np によって登録されたはクリーンアップハンドラを削除し、取り消し型を pthread_cleanup_push_defer_np が呼び出された時点の値に戻す。
 
pthread_cleanup_push_defer_nppthread_cleanup_pop_restore_np は対になっていなければならず、ともに同じブロック階層になければならない。
 
 
 

pthread_cleanup_push_defer_np(routine, arg);
pthread_cleanup_pop_defer_np(execute);

 
のような流れは機能的に次のものと同等 (だがよりコンパクトでより効率的) である。
 
 

{ int oldtype;
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);
pthread_cleanup_push(routine, arg);
...
pthread_cleanup_pop(execute);
pthread_setcanceltype(oldtype, NULL);
}

 

返り値

なし。
 

エラー

なし。
 

著者

Xavier Leroy <Xavier.Leroy@inria.fr>
 

関連項目

pthread_exit(3), pthread_cancel(3), pthread_setcanceltype(3).
 

次の例は、 mutex mut をロック中にスレッドが取り消しされたらロックを解除するように、 mutex mut をロックする方法である:
 
 

pthread_cleanup_push(pthread_mutex_unlock, (void *) &mut);
pthread_mutex_lock(&mut);
/* 何かをする */
pthread_mutex_unlock(&mut);
pthread_cleanup_pop(0);

 
最後の 2 行は次のものと同等で、置き換えが可能である:
 
 

pthread_cleanup_pop(1);

 
上のコードは取り消し型が遅延 (deferred) である場合に限って安全であることに注意すること ( pthread_setcanceltype(3) を参照 ) 。取り消し型が非同期 (asynchronous) の場合には、スレッドの取り消しが pthread_cleanup_pushpthread_mutex_lock の間や、 pthread_mutex_unlockpthread_cleanup_pop の間で起こる可能性があり、どちらの場合にもスレッドはカレントスレッドでロックしていない mutex をロック解除しようとしてしまう。このことは非同期取り消しが使いにくいことの主な理由である。
 
上のコードが非同期取り消し型でも動作しなければならない場合、 mutex のロックおよびロック解除のために、取り消し型を遅延 (deferred) に変更しなければならない:
 
 

pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype);
pthread_cleanup_push(pthread_mutex_unlock, (void *) &mut);
pthread_mutex_lock(&mut);
/* do some work */
pthread_cleanup_pop(1);
pthread_setcanceltype(oldtype, NULL);

 
上のコードは、ポータブルでない関数 pthread_cleanup_push_defer_nppthread_cleanup_pop_restore_np を使うことで、よりコンパクトでより効率的な方法に書き直すことができる:
 
 

pthread_cleanup_push_restore_np(pthread_mutex_unlock, (void *) &mut);
pthread_mutex_lock(&mut);
/* do some work */
pthread_cleanup_pop_restore_np(1);

LinuxThreads