リエントラント

リエントラント（、再入可能）およびリエントランシー（、再入可能性）とは、あるプログラムやサブルーチンの実行を完了する前に、割り込みなどにより、同じプログラムやサブルーチンを実行しても安全だという性質を指す。割り込みは分岐や呼び出しなどの内部的な動きによって生じる場合もあるし、ハードウェア割り込みやシグナルなどの外部の動きによって生じる場合もある。割り込みの実行を完了後に、割り込み前の実行に影響を与えずに継続できる。
この定義はシングルスレッドのプログラミング環境が起源であり、ハードウェア割り込みで割り込まれた制御の流れが割り込みサービスルーチン (ISR) に転送されることから生まれた。ISRが使用するサブルーチンは割り込みをきっかけとして実行される可能性があるため、リエントラントでなければならない。OSのカーネルが使用するサブルーチンの多くは、カーネルで確保済みのリソースを超えられない制限がありリエントラントではない。そのためISRでできることは限られている。例えば、一般にISRからファイルシステムにはアクセスできないし、場合によってはヒープ領域も確保できない。
直接または間接に再帰可能なサブルーチンはリエントラントである。しかし、グローバル変数が処理の流れの中でしか変化しないことを前提としているサブルーチンはリエントラントではない。グローバル変数を更新するサブルーチンが再帰的に呼び出されれば、1回のサブルーチン実行の中でグローバル変数は突然変化することになる。
リエントラント性の概念はシングルスレッドの環境に起源があり、マルチスレッド環境でのスレッドセーフという概念とは異なる。リエントラントなサブルーチンはスレッドセーフにすることもできるが、リエントラントだというだけであらゆる状況でスレッドセーフと言えるわけではない。逆にスレッドセーフなコードはリエントラントである必要はない（後述の例を参照）。
例.
次の例の codice_1 関数は、リエントラントではない（同時にスレッドセーフでもない）。したがってこれを割り込みサービスルーチン codice_2 で使用すべきでない。
int t;
void swap(int *x, int *y)
 t = *x;
 *x = *y;
 // ここでハード割り込みが起きて isr() が呼び出される可能性がある。
 *y = t;
void isr()
 int x = 1, y = 2;
 swap(&x, &y);
codice_1 は codice_4 をスレッド局所記憶にすることでスレッドセーフにできる。しかしそのようにしてもリエントラントにはならず、codice_1 実行中に同じスレッドのコンテキストで codice_2 が呼び出されれば問題を生じる可能性が残っている。
次の工夫を加えたswap関数では、実行完了時のグローバルなデータを注意深く一貫性を保つようにしており、完全にリエントラントである。ただし、実行途中のグローバルなデータの一貫性は保証されていないのでスレッドセーフではない。また、codice_7型変数の読み出しおよび書き込みが不可分操作（アトミック）である（読み出し処理および書き込み処理はそれぞれ1命令のみで実行され、処理の最中に割り込みが発生しない）ことが前提である。
int t;
void swap(int *x, int *y)
 int s;
 s = t; // グローバル変数をセーブ
 t = *x;
 *x = *y;
 // ここでハード割り込みが起きて isr() が呼び出される可能性がある。
 *y = t;
 t = s; // グローバル変数をリストア
void isr()
 int x = 1, y = 2;
 swap(&x, &y);
次のswap関数はリエントラントかつスレッドセーフである。
void swap(int *x, int *y)
 int t;
 t = *x;
 *x = *y;
 // ここでハード割り込みが起きて isr() が呼び出される可能性がある。
 *y = t;
void isr()
 int x = 1, y = 2;
 swap(&x, &y);
背景.
リエントラント性と冪等性は同義ではない。冪等な関数は、何度呼び出したとしても1度だけ呼び出したかのように全く同じ出力を生成する。一般化すれば、共有データを使わず、入力データに基づいて出力データを生成する関数である（ただし、入力や出力はオプション）。共有データはいつでも誰でもアクセスできる。データを誰かが更新し、誰も更新を把握していない場合、そのデータが以前と比べて変化したのかどうかさえ誰にもわからない。冪等性はリエントラント性を包含するが、逆は必ずしも真ではない。
データにはスコープという属性がある。グローバルなデータはあらゆる関数のスコープの範囲外にあり、寿命は不定である。一方局所的なデータは関数が呼び出されるたびに生成され、関数から抜けるときに破棄される。
局所的データはルーチン間で共有されず、再入時にも共有されない。したがってリエントラント性を阻害しない。グローバルなデータは任意の関数間で共有でき、あるいは再入時にも共有される。したがってリエントラント性を阻害する。
リエントラント性はスレッドセーフの概念とは異なるが、密接に関連している。関数がスレッドセーフであっても、リエントラントでないことがある。例えば関数全体をミューテックスで囲むと、マルチスレッド環境では問題を防げるが、割り込みサービスルーチンで使用すると再入時にミューテックスの解放を待ち続けることになる。混乱を避ける鍵は、リエントラントが1つのスレッド実行でも問題になるという点である。リエントラント性はマルチタスクOSの存在する前からの概念である。
リエントラントな割り込みハンドラ.
リエントラントな割り込みハンドラは、割り込み処理中に早期に割り込み可能にする割り込みハンドラである。それによってが低減される。一般に割り込みサービスルーチンをプログラミングする際、割り込みハンドラ内でなるべく早期に割り込み可能な状態にすることが推奨される。それによって割り込みを拾い損なうのを防ぎやすくなる。
さらなる例.
以下のコードにある関数 codice_8 も codice_9 もリエントラントではない。
int g_var = 1;
int f()
 g_var = g_var + 2;
 return g_var;
int g()
 return f() + 2;
上記のコードで、codice_8 はグローバル変数 codice_11 に依存している。したがって、2つのプロセス（スレッド）が codice_8 を実行すると、codice_11 に同時並行的にアクセスし、結果はタイミングに依存することになる。したがって、codice_8 はリエントラントではない。その codice_8 を呼び出している codice_9 もリエントラントではない。
これらを若干変更したリエントラントである版を以下に示す:
int f(int i)
 return i + 2;
int g(int i)
 return f(i) + 2;
新しい版では、グローバル変数 codice_11 は使われていない。引数を渡して、それに基づいて処理を行って結果を返す。共有される可能性のあるオブジェクトにはアクセスしないようになっている。その代わり、呼出し側が前回の戻り値を引数として渡してやらなければならない。このように、リエントラントなサブルーチンでは、必要な静的データは呼出し側が管理しなければならない。
次のPthreadsを使ったC言語のコードでは、関数codice_18はスレッドセーフだが、リエントラント（非同期シグナル安全）ではない。Pthreadsのミューテックス関数がリエントラント（非同期シグナル安全）であることは保証されないからである。
void function(pthread_mutex_t mutex)
 pthread_mutex_lock(mutex);
 /* 何らかの処理 */
 pthread_mutex_unlock(mutex);
この codice_18 は複数のスレッドから呼び出されても全く問題はない。しかし、codice_20によるミューテックスの初期化時にcodice_21を設定した属性を使用していて、リエントラントな割り込みハンドラがこの関数を呼び出す場合、2つ目の割り込みがこの関数実行中に発生すると、二度目の呼び出しはミューテックスを獲得できず、永久に停止（デッドロック）する。割り込みサービスでは他の割り込みをディセーブルするので、システム全体がハングアップすることになる。
デッドロックを回避するには、ミューテックス初期化時に、同一スレッドによる複数回のロックを許可するcodice_22を設定した属性を使用する必要がある。ただし、codice_22を設定したからといって、codice_24およびcodice_25が非同期シグナル安全になるとは限らない。なお、ミューテックス初期化時にcodice_26を使用すると、codice_27を設定した既定の属性が使用されることになり、デッドロックに関しては未定義動作となる。
一方、Microsoft Windowsのcodice_28関数は、同一スレッドからの複数回呼び出しはブロッキングなしで実行される。ただし非同期シグナル安全性や再入可能性に関する言及や保証はない。
POSIXのリエントラントと非同期シグナル安全.
POSIX標準には、"_r" の接尾辞が付けられたC言語関数群が用意されている。これらは従来の標準Cライブラリ関数のリエントラントバージョンである。規格では、「リエントラント関数」を以下のように定義している。
つまり、マルチスレッド環境下での実行順序非依存性や並列実行可能性（スレッドセーフ）の意味でリエントラントという用語を規定しており、非同期シグナル安全性に関しては述べられていない。
一方、POSIXでは非同期シグナル安全な118の関数を規定している。シグナルハンドラ内部では非同期シグナル安全でない関数は呼び出してはいけない。