C API 拡張のフリースレッド対応
******************************

CPython 3.13 から、*global interpreter lock* (GIL) を無効にして実行す
る *free threading* という構成がサポートされています。 このドキュメン
トでは、C API を使う拡張をフリースレッドに対応させる方法を説明します。


C からフリースレッドビルドを識別する
====================================

CPython の C API は "Py_GIL_DISABLED" マクロを公開しています。 フリー
スレッドビルドでは "1" に定義され、通常のビルドでは定義されません。 こ
れを使って、フリースレッドビルドでだけ実行するコードを有効にできます。

   #ifdef Py_GIL_DISABLED
   /* フリースレッドビルドでだけ実行するコード */
   #endif

注釈:

  Windows ではこのマクロは自動的には定義されないため、ビルド時にコンパ
  イラへ指定する必要があります。 実行中のインタープリタでこのマクロが
  定義されていたかは、"sysconfig.get_config_var()" 関数で確認できます
  。


モジュールの初期化
==================

拡張モジュールは、GIL を無効にした実行に対応していることを明示する必要
があります。 そうしなければ、拡張のインポート時に警告が出され、実行時
に GIL が有効になります。

GIL を無効にした実行に対応していることを示す方法は、拡張が複数フェーズ
初期化と単一フェーズ初期化のどちらを使うかによって、2 通りあります。


複数フェーズ初期化
------------------

複数フェーズ初期化 ("PyModuleDef_Init()"、"PyModExport_*" エクスポート
フック、"PyModule_FromSlotsAndSpec()" など) を使う拡張では、モジュール
定義に "Py_mod_gil" スロットを追加してください。 古い CPython バージョ
ンにも対応する場合は、"PY_VERSION_HEX" を確認し、対応するバージョンで
だけこのスロットを追加してください。

   static struct PyModuleDef_Slot module_slots[] = {
       ...
   #if PY_VERSION_HEX >= 0x030D0000
       {Py_mod_gil, Py_MOD_GIL_NOT_USED},
   #endif
       {0, NULL}
   };


単一フェーズ初期化
------------------

従来の 単一フェーズ初期化 (つまり "PyModule_Create()") を使う拡張では
、"PyUnstable_Module_SetGIL()" を呼び出して、GIL を無効にした実行に対
応していることを示してください。 この関数はフリースレッドビルドでのみ
定義されるため、通常のビルドでのコンパイルエラーを避けるには、呼び出し
を "#ifdef Py_GIL_DISABLED" で囲んでください。

   static struct PyModuleDef moduledef = {
       PyModuleDef_HEAD_INIT,
       ...
   };

   PyMODINIT_FUNC
   PyInit_mymodule(void)
   {
       PyObject *m = PyModule_Create(&moduledef);
       if (m == NULL) {
           return NULL;
       }
   #ifdef Py_GIL_DISABLED
       PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED);
   #endif
       return m;
   }


API 全般の指針
==============

C API の大部分はスレッドセーフですが、例外もあります。

* **構造体のフィールド**: Python C API のオブジェクトや構造体のフィー
  ルドが並行して変更される可能性がある場合、そのフィールドへの直接アク
  セスはスレッドセーフではありません。

* **マクロ**: "PyList_GET_ITEM"、"PyList_SET_ITEM" などのアクセス用マ
  クロや、"PySequence_Fast()" が返すオブジェクトを使う
  "PySequence_Fast_GET_SIZE" などのマクロは、エラーチェックもロックも
  行いません。 コンテナオブジェクトが並行して変更される可能性がある場
  合、これらのマクロはスレッドセーフではありません。

* **借用参照**: *借用参照* を返す C API 関数は、参照先を保持するオブジ
  ェクトが並行して変更される場合、スレッドセーフでないことがあります。
  詳しくは 借用参照の節 を参照してください。


コンテナのスレッド安全性
------------------------

"PyListObject"、"PyDictObject"、"PySetObject" などのコンテナは、フリー
スレッドビルドでは内部でロックを使います。 例えば、"PyList_Append()"
は要素を追加する前にリストをロックします。


"PyDict_Next"
~~~~~~~~~~~~~

注意すべき例外は "PyDict_Next()" で、この関数は辞書をロックしません。
辞書が並行して変更される可能性がある場合は、
"Py_BEGIN_CRITICAL_SECTION" を使い、イテレート中の辞書を保護してくださ
い。

   Py_BEGIN_CRITICAL_SECTION(dict);
   PyObject *key, *value;
   Py_ssize_t pos = 0;
   while (PyDict_Next(dict, &pos, &key, &value)) {
       ...
   }
   Py_END_CRITICAL_SECTION();


借用参照
========

一部の C API 関数は *借用参照* を返します。 参照先を保持するオブジェク
トが並行して変更される場合、これらの API はスレッドセーフではありませ
ん。 例えば、リストが並行して変更される可能性がある状況で
"PyList_GetItem()" を使うのは安全ではありません。

次の表は、借用参照を返す API の一部と、代わりに使える *強参照* を返す
API を示します。

+-------------------------------------+-------------------------------------+
| 借用参照を返す API                  | 強参照を返す API                    |
|=====================================|=====================================|
| "PyList_GetItem()"                  | "PyList_GetItemRef()"               |
+-------------------------------------+-------------------------------------+
| "PyList_GET_ITEM()"                 | "PyList_GetItemRef()"               |
+-------------------------------------+-------------------------------------+
| "PyDict_GetItem()"                  | "PyDict_GetItemRef()"               |
+-------------------------------------+-------------------------------------+
| "PyDict_GetItemWithError()"         | "PyDict_GetItemRef()"               |
+-------------------------------------+-------------------------------------+
| "PyDict_GetItemString()"            | "PyDict_GetItemStringRef()"         |
+-------------------------------------+-------------------------------------+
| "PyDict_SetDefault()"               | "PyDict_SetDefaultRef()"            |
+-------------------------------------+-------------------------------------+
| "PyDict_Next()"                     | なし (PyDict_Next を参照)           |
+-------------------------------------+-------------------------------------+
| "PyWeakref_GetObject()"             | "PyWeakref_GetRef()"                |
+-------------------------------------+-------------------------------------+
| "PyWeakref_GET_OBJECT()"            | "PyWeakref_GetRef()"                |
+-------------------------------------+-------------------------------------+
| "PyImport_AddModule()"              | "PyImport_AddModuleRef()"           |
+-------------------------------------+-------------------------------------+
| "PyCell_GET()"                      | "PyCell_Get()"                      |
+-------------------------------------+-------------------------------------+

借用参照を返すすべての API が問題になるわけではありません。 例えば、タ
プルはイミュータブルなので "PyTuple_GetItem()" は安全です。 また、上記
API の使い方がすべて問題になるわけでもありません。 例えば、
"PyDict_GetItem()" は関数呼び出しのキーワード引数辞書の解析によく使わ
れます。 この辞書は実質的にプライベートで、ほかのスレッドからアクセス
できないため、その状況で借用参照を使うのは安全です。

これらの関数の一部は Python 3.13 で追加されました。 古い Python バージ
ョン向けには、pythoncapi-compat パッケージでこれらの関数の実装を利用で
きます。


メモリ割り当て API
==================

Python のメモリ管理 C API は、raw、mem、object という 3 つの 割り当て
ドメイン の関数を提供します。 フリースレッドビルドではスレッド安全性の
ため、object ドメインで割り当てるのは Python オブジェクトだけでなけれ
ばならず、すべての Python オブジェクトをこのドメインで割り当てる必要が
あります。 以前の Python では、これは推奨事項にすぎず、必須条件ではあ
りませんでした。

注釈:

  拡張内の "PyObject_Malloc()" の使用箇所を探し、割り当てたメモリを
  Python オブジェクトに使っているか確認してください。 バッファの割り当
  てには、"PyObject_Malloc()" ではなく "PyMem_Malloc()" を使ってくださ
  い。


スレッド状態と GIL の API
=========================

Python は、スレッド状態と GIL を管理するために、次のような関数やマクロ
を提供しています。

* "PyThreadState_Ensure()"、"PyThreadState_EnsureFromView()"、
  "PyThreadState_Release()"

* "PyGILState_Ensure()" と "PyGILState_Release()"

* "PyEval_SaveThread()" と "PyEval_RestoreThread()"

* "Py_BEGIN_ALLOW_THREADS" と "Py_END_ALLOW_THREADS"

フリースレッドビルドで *GIL* が無効な場合も、スレッド状態の管理にはこ
れらの関数を引き続き使う必要があります。 例えば、Python の外部で作成し
たスレッドから Python API を呼び出す場合は、先に
"PyThreadState_Ensure()" を呼び出し、有効な Python スレッド状態を持つ
ことを保証しなければなりません。

I/O やロック取得などのブロッキング操作の前後では、引き続き
"PyEval_SaveThread()" や "Py_BEGIN_ALLOW_THREADS" を使ってください。
これにより、ほかのスレッドが *循環ガベージコレクタ* を実行できるように
なります。


拡張の内部状態の保護
====================

拡張には、以前は GIL によって保護されていた内部状態があるかもしれませ
ん。 その状態を保護するためにロックを追加する必要がある場合があります
。 方法は拡張によりますが、よくあるパターンには次のものがあります。

* **キャッシュ**: グローバルなキャッシュは、共有状態が生まれる典型的な
  場所です。 ロックでキャッシュを保護するか、性能上不可欠でなければ、
  フリースレッドビルドではキャッシュを無効にすることを検討してください
  。

* **グローバルな状態**: グローバルな状態には、ロックによる保護やスレッ
  ドローカルストレージへの移動が必要な場合があります。 C11 と C++11 は
  、スレッドローカルストレージ のために "thread_local" または
  "_Thread_local" を提供しています。


クリティカルセクション
======================

CPython のフリースレッドビルドには、通常なら GIL が保護するデータを保
護する「クリティカルセクション」という仕組みがあります。 拡張の開発者
がその内部実装を直接扱うことはなくても、特定の C API 関数を使うときや
共有状態を管理するときには、動作を理解しておくことが重要です。


クリティカルセクションとは
--------------------------

概念的には、クリティカルセクションは単純なミューテックスの上に設けられ
た、デッドロックを回避するための層です。 各スレッドは、有効なクリティ
カルセクションのスタックを保持します。 スレッドがクリティカルセクショ
ンのロックを取得する必要があると、その基盤となるミューテックスの取得を
試みます。 これは、"PyDict_SetItem()" のようなスレッドセーフな C API
関数の呼び出しで暗黙に行われる場合も、マクロで明示的に行われる場合もあ
ります。


クリティカルセクションの使い方
------------------------------

クリティカルセクションを使う主な API は次のとおりです。

* "Py_BEGIN_CRITICAL_SECTION" と "Py_END_CRITICAL_SECTION" — 1 つのオ
  ブジェクトをロックします。

* "Py_BEGIN_CRITICAL_SECTION2" と "Py_END_CRITICAL_SECTION2" — 2 つの
  オブジェクトを同時にロックします。

これらのマクロは新しいローカルスコープを作るため、必ず対応する組で使い
、同じ C のスコープに置く必要があります。 非フリースレッドビルドでは何
もしないので、両方のビルドに対応するコードにも安全に追加できます。

クリティカルセクションのよくある用途は、オブジェクトの内部属性へのアク
セス中に、そのオブジェクトをロックすることです。 例えば、拡張型に内部
的なカウントのフィールドがあれば、その読み書きにクリティカルセクション
を使えます。

   // read the count, returns new reference to internal count value
   PyObject *result;
   Py_BEGIN_CRITICAL_SECTION(obj);
   result = Py_NewRef(obj->count);
   Py_END_CRITICAL_SECTION();
   return result;

   // write the count, consumes reference from new_count
   Py_BEGIN_CRITICAL_SECTION(obj);
   obj->count = new_count;
   Py_END_CRITICAL_SECTION();


クリティカルセクションの仕組み
------------------------------

通常のロックとは異なり、クリティカルセクションはその期間全体にわたる排
他的アクセスを保証しません。 クリティカルセクション内で別のロックの取
得や I/O などによってスレッドがブロックする場合、クリティカルセクショ
ンは一時的に中断され、すべてのロックが解放されます。 ブロッキング操作
が完了すると再開されます。

これは、スレッドがブロッキング呼び出しを行うときの GIL の動作に似てい
ます。 主な違いは次のとおりです。

* クリティカルセクションは全体で 1 つではなく、オブジェクト単位で動作
  します。

* クリティカルセクションは各スレッド内でスタックの順序に従います。 開
  始マクロと終了マクロは同じスコープ内で対にする必要があるため、この順
  序が強制されます。

* クリティカルセクションは、ブロックする可能性のある操作の前後で、自動
  的にロックを解放して再取得します。


デッドロックの回避
------------------

クリティカルセクションは、次の 2 つの方法でデッドロックの回避を助けま
す。

1. スレッドが別のスレッドに保持されているロックを取得しようとすると、
   まず自身の有効なクリティカルセクションをすべて中断し、それらのロッ
   クを一時的に解放します。

2. ブロッキング操作が完了すると、最初に最上位のクリティカルセクション
   だけが再取得されます。

このため、複数のオブジェクトを同時にロックする目的で、クリティカルセク
ションの入れ子を使うことはできません。 内側のクリティカルセクションが
外側を中断する可能性があるためです。 2 つのオブジェクトを同時にロック
するには、代わりに "Py_BEGIN_CRITICAL_SECTION2" を使ってください。

ここで説明したロックは、"PyMutex" に基づくロックだけです。 クリティカ
ルセクションの実装は、POSIX ミューテックスなどのほかのロック機構を認識
せず、影響も与えません。 また、どの "PyMutex" でブロックしてもクリティ
カルセクションは中断されますが、解放されるのはクリティカルセクションの
一部であるミューテックスだけです。 クリティカルセクションを使わずに取
得した "PyMutex" は解放されないため、同じデッドロック回避の効果は得ら
れません。


重要な注意点
------------

* クリティカルセクションは一時的にロックを解放することがあるため、ほか
  のスレッドが保護対象のデータを変更できます。 ブロックする可能性のあ
  る操作の後で、データの状態を仮定する際には注意してください。

* ロックが一時的に解放 (中断) される可能性があるため、クリティカルセク
  ションに入っても、その期間全体にわたって保護対象のリソースへ排他的に
  アクセスできるとは限りません。 クリティカルセクション内で、別のロッ
  クの取得やブロッキング I/O などを行う関数を呼び、その処理がブロック
  すると、そのスレッドがクリティカルセクションを通じて保持するロックは
  すべて解放されます。 これは、ブロッキング呼び出し中に GIL が解放され
  ることがあるのと同様です。

* ある時点で保持が保証されるのは、最後に入った最上位のクリティカルセク
  ションのロックだけです。 入れ子になった外側のクリティカルセクション
  のロックは、中断されている可能性があります。

* これらの API で同時にロックできるオブジェクトは最大 2 つです。 それ
  以上をロックする必要があれば、コードの構成を見直す必要があります。

* クリティカルセクションで同じオブジェクトを 2 回ロックしようとしても
  デッドロックにはなりませんが、この用途に特化した再入可能ロックほど効
  率的ではありません。

* "Py_BEGIN_CRITICAL_SECTION2" では、実装がデッドロックを回避するため
  、オブジェクトの順序は正しさに影響しません。 ただし、常に一貫した順
  序でロックするのがよい習慣です。

* クリティカルセクションのマクロは主に、上述のようなデッドロックが起こ
  り得る CPython 内部の操作に関与する *Python オブジェクト* へのアクセ
  スを保護するものです。 拡張だけが使う内部状態の保護には、標準のミュ
  ーテックスなどの同期プリミティブのほうが適している場合があります。


オブジェクトごとのロック ("ob_mutex")
-------------------------------------

フリースレッドビルドでは、各 Python オブジェクトに "PyMutex" 型の
"ob_mutex" フィールドがあります。 このミューテックスは **クリティカル
セクション API 専用** です ("Py_BEGIN_CRITICAL_SECTION" /
"Py_END_CRITICAL_SECTION")。

警告:

  "PyMutex_Lock(&obj->ob_mutex)" で "ob_mutex" を直接ロック **しないで
  ください**。 同じミューテックスに対して、"PyMutex_Lock" の直接呼び出
  しとクリティカルセクション API を混在させると、デッドロックが起こる
  可能性があります。

自分のコードでは特定の型のオブジェクトにクリティカルセクションを使って
いなくても、**CPython 内部ではどの Python オブジェクトにもクリティカル
セクション API を使う可能性があります**。

拡張型に独自のロックが必要な場合は、オブジェクトの構造体に別の
"PyMutex" フィールド、またはほかの同期プリミティブを追加してください。
"PyMutex" は非常に軽量なので、追加で 1 つ持ってもコストはほぼ無視でき
ます。


フリースレッドビルド向けの拡張のビルド
======================================

C API extensions need to be built specifically for the free-threaded
build. The wheels, shared libraries, and binaries are indicated by a
"t" suffix.

* pypa/manylinux は、"python3.14t" のように "t" サフィックスを付けたフ
  リースレッドビルドに対応しています。

* pypa/cibuildwheel は、Python 3.14 以降のフリースレッドビルド向け
  wheel の作成に対応しています。


Limited C API と Stable ABI
---------------------------

The free-threaded build does not currently support the Limited C API
or the stable ABI.  If you use setuptools to build your extension and
currently set "py_limited_api=True" you can use "py_limited_api=not
sysconfig.get_config_var("Py_GIL_DISABLED")" to opt out of the limited
API when building with the free-threaded build.

注釈:

  You will need to build separate wheels specifically for the free-
  threaded build.  If you currently use the stable ABI, you can
  continue to build a single wheel for multiple non-free-threaded
  Python versions.


Windows
-------

公式 Windows インストーラの制限により、ソースから拡張をビルドする際は
、"Py_GIL_DISABLED=1" を手動で定義する必要があります。

参考:

  Porting Extension Modules to Support Free-Threading: 拡張の開発者向
  けにコミュニティが保守している移植ガイド。
