スレッドセーフを有効にして PHP をビルドするときには、エンジン内で個々のコンテキストを分離する方法が必要になります。 つまり、プロセス内の各スレッドが、互いに影響を及ぼさずに個別のリクエストを処理できるようにするということです。 TSRM は PHP における全能の神です。拡張モジュールの作者は、ほとんど何もしなくても、 自作のモジュールをスレッドセーフ環境と非スレッドセーフ環境の両方に対応させることができます。
例1 モジュールグローバルへのアクセス用マクロ
#ifdef ZTS #define COUNTER_G(v) TSRMG(counter_globals_id, zend_counter_globals *, v) #else #define COUNTER_G(v) (counter_globals.v) #endif
このコード片は、拡張モジュールの作者がグローバルアクセサ—を定義する方法を示すものです。 TSRMG マクロは、識別子と型キャストそして要素名を受け取ります。 識別子は、モジュールの初期化時に TSRM が代入します。 グローバルアクセサをこのように宣言しておくと、その拡張モジュールは、 スレッドセーフと非スレッドセーフのどちらのアーキテクチャでも同じロジックで安全に扱えるようになります。
TSRM は、PHP 内部のすべてのグローバル構造 (実行環境のグローバルから拡張モジュールのグローバルまで) の分離や安全性を管理し、隔離したストレージへのポインタをほとんどの API 関数に渡します。 TSRMLS_C と TSRMLS_CC の二つのマクロは、それぞれ "thread safe local storage"、"thread safe local storage prefixed with a comma" を表します。
関数で TSRM へのポインタが必要な場合は、マクロ TSRMLS_D あるいは TSRMLS_DC を関数のプロトタイプで使います。 これらはそれぞれ、"thread safe local storage only" と "thread safe local storage prefixed with a comma" を表します。 エンジン内のマクロの多くは TSRM を参照しているので、ほとんどの場合、TSRM を受け付けるようにしておくほうが得策です。 そうしておけば、もし TSRM を利用することになったとしても、実行中にポインタを取得する必要がなくなります。
TSRM はスレッドローカルであり、中には互換性の関係で TSRM を直接使えない関数もあります。 そのため、TSRMLS_FETCH マクロが用意されました。これは、TSRM にスレッドローカルストレージへのポインタを取得させるようリクエストします。 可能な限り、このマクロは使わないようにしましょう。スレッドセーフ環境では、実行のコストがかかってしまうからです。
注意: 拡張モジュールを開発していると、"tsrm_ls is undefined" を含むエラーやその影響を受けたエラーが発生することがあります。 これは、そのスコープで TSRMLS が定義されていないことに起因するものです。 このエラーを修正するには、関数の宣言時に適切なマクロを使って TSRMLS を受け付けるようにします。 関数のプロトタイプを変更できない場合は、関数の中で TSRMLS_FETCH を呼ばなければいけません。
これ以降で説明する機能は、TSRM を高度に使いこなすためのものです。 普通は、拡張モジュールが直接 TSRM を操作することはありません。 拡張モジュールのプログラマーは、TSRM の上位にある API (たとえば モジュールグローバル API) を使うべきです。
プロトタイプ | 説明 |
---|---|
typedef int ts_rsrc_id |
リソースを数値 ID で表すための型定義。 |
int tsrm_startup(int expected_threads, int expected_resources, int debug_level, char *debug_filename) |
expected_threads と expected_resources は、ハードリミットではありません。
debug_level と debug_filename が効果を発揮するのは、
Windows 上でデバッグモードの場合か TSRM_DEBUG を定義した場合だけです。
サーバーの立ち上げ時に、ZTS モードのプロセスごとに呼ばれます。
|
void tsrm_shutdown(void) |
プロセスの最後に呼ぶ必要があります。 ZTS モードのすべての TSRM ストレージを破棄して解放します。 |
ts_rsrc_id ts_allocate_id(ts_rsrc_id *rsrc_id, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor) |
size バイトのスレッドセーフポインタを確保してポインタ上で
ctor を呼び、id を割り当てて rsrc_id に代入し、
リソースを解放したときには dtor を呼びます。
ZTS モードでは、この API を使ってモジュールグローバルを分離します。
|
void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id) |
id で割り当てられたリソースを、
スレッド th_id が作ったエントリから取得します。
|
void *ts_resource(ts_rsrc_id id)
|
事前に確保したリソース id を、呼び出し元スレッドが作ったエントリから取得します。
|
void ts_free_thread(void) |
呼び出し元スレッドのエントリを破棄して解放します。 現時点では、この API を使うのは ISAPI モジュールだけです。 |
void ts_free_id(ts_rsrc_id id)
|
事前に確保したリソース id を破棄します。
この API を使うと、ZTS モードでのモジュールグローバルの後始末もします。
|
注意: これら以外にも、インタプリタのコンテキストを作ったり破棄したりする TSRM API 関数があります。 しかし、これらの機能についてはこのマニュアルで扱う範囲を超えるので、 詳細は TSRM/TSRM.h を参照ください。
プロトタイプ | 説明 |
---|---|
MUTEX_T tsrm_mutex_alloc(void) |
その環境用の適切な MUTEX_T を確保して、それを返します。 |
int tsrm_mutex_lock(MUTEX_T mutexp) |
mutexp を、呼び出し側スレッド用にロックします。
|
int tsrm_mutex_unlock(MUTEX_T mutexp) |
mutexp の呼び出し側スレッド用のロックを解除します。
|
void tsrm_mutex_free(MUTEX_T mutexp); |
事前に確保して、既にロック解除済みの mutexp を、破棄して解放します。
|
注意: ミューテックス API は、TSRM が公開するものです。 しかし、その内部的な利用法は、ここで扱うにはあまりにも幅広すぎます。 そのため、基本的な機能とプロトタイプだけの紹介にとどめます。