Suporte a extensões da API C para threads livres
************************************************

A partir da versão 3.13, o CPython tem suporte experimental para
execução com a *trava global do interpretador* (GIL) desabilitada em
uma configuração chamada *threads livres*. Este documento descreve
como adaptar extensões da API C para ter suporte a threads livres.


Identificando a construção com threads livres em C
==================================================

A API C do CPython expõe a macro "Py_GIL_DISABLED": na construção com
threads livres ela está definida como "1", e na construção regular ela
não está definida. Você pode usá-la para ativar o código que é
executado apenas na construção de threads livres:

   #ifdef Py_GIL_DISABLED
   /* código que só vai ser executado na construção de threads livres */
   #endif


Inicialização de módulo
=======================

Os módulos de extensão precisam indicar explicitamente que têm suporte
à execução com a GIL desabilitada; caso contrário, importar a extensão
levantará um aviso e ativará a GIL em tempo de execução.

Há duas maneiras de indicar que um módulo de extensão tem suporte à
execução com a GIL desabilitada, dependendo se a extensão usa
inicialização monofásica ou multifásica.


Inicialização multifásica
-------------------------

Extensões que usam inicialização multifásica (ou seja,
"PyModuleDef_Init()") devem adicionar um slot "Py_mod_gil" na
definição do módulo. Se sua extensão tem suporte a versões mais
antigas do CPython, você deve proteger o slot com uma verificação de
"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}
   };

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


Inicialização monofásica
------------------------

Extensões que usam inicialização monofásica (ou seja,
"PyModule_Create()") devem chamar "PyUnstable_Module_SetGIL()" para
indicar que têm suporte à execução com a GIL desabilitada. A função é
definida apenas na construção com threads livres, então você deve
proteger a chamada com "#ifdef Py_GIL_DISABLED" para evitar erros de
compilação na construção regular.

   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;
   }


Diretrizes gerais de API
========================

A maior parte da API C é segura para thread, mas há algumas exceções.

* **Campos de structs**: acessar campos em objetos ou structs da API C
  do Python diretamente não é seguro para thread se o campo puder ser
  modificado simultaneamente.

* **Macros**: Macros de acesso como "PyList_GET_ITEM" e
  "PyList_SET_ITEM" não executam nenhuma verificação por erros e
  nenhum travamento. Essas macros não são seguras para thread se o
  objeto contêiner puder ser modificado simultaneamente.

* **Referências emprestadas**: As funções da API C que retornam
  *referências emprestadas* podem não ser seguras para thread se o
  objeto que as contêm for modificado simultaneamente. Veja a seção
  sobre referências emprestadas para mais informações.


Segurança de threads de contêineres
-----------------------------------

Contêineres como "PyListObject", "PyDictObject" e "PySetObject"
executam uma trava interna na construção com threads livres. Por
exemplo, "PyList_Append()" irá travar a lista antes de anexar um item.


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

Uma exceção notável é "PyDict_Next()", que não trava o dicionário.
Você deve usar "Py_BEGIN_CRITICAL_SECTION" para proteger o dicionário
enquanto itera sobre ele se o dicionário puder ser modificado
simultaneamente:

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


Referências emprestadas
=======================

Algumas funções da API C retornam *referências emprestadas*. Essas
APIs não são seguras para thread se o objeto que as contém for
modificado simultaneamente. Por exemplo, não é seguro usar
"PyList_GetItem()" se a lista puder ser modificada simultaneamente.

A tabela a seguir lista algumas APIs de referências emprestadas e suas
substituições que retornam *referências fortes*.

+-------------------------------------+-------------------------------------+
| API de referências emprestadas      | API de referências fortes           |
|=====================================|=====================================|
| "PyList_GetItem()"                  | "PyList_GetItemRef()"               |
+-------------------------------------+-------------------------------------+
| "PyDict_GetItem()"                  | "PyDict_GetItemRef()"               |
+-------------------------------------+-------------------------------------+
| "PyDict_GetItemWithError()"         | "PyDict_GetItemRef()"               |
+-------------------------------------+-------------------------------------+
| "PyDict_GetItemString()"            | "PyDict_GetItemStringRef()"         |
+-------------------------------------+-------------------------------------+
| "PyDict_SetDefault()"               | "PyDict_SetDefaultRef()"            |
+-------------------------------------+-------------------------------------+
| "PyDict_Next()"                     | nenhuma (veja PyDict_Next)          |
+-------------------------------------+-------------------------------------+
| "PyWeakref_GetObject()"             | "PyWeakref_GetRef()"                |
+-------------------------------------+-------------------------------------+
| "PyWeakref_GET_OBJECT()"            | "PyWeakref_GetRef()"                |
+-------------------------------------+-------------------------------------+
| "PyImport_AddModule()"              | "PyImport_AddModuleRef()"           |
+-------------------------------------+-------------------------------------+

Nem todas as APIs que retornam referências emprestadas são
problemáticas. Por exemplo, "PyTuple_GetItem()" é segura porque as
tuplas são imutáveis. Da mesma forma, nem todos os usos das APIs acima
são problemáticos. Por exemplo, "PyDict_GetItem()" é frequentemente
usado para analisar dicionários de argumentos nomeados em chamadas de
função; esses dicionários de argumentos nomeados efetivamente privados
(não acessíveis por outros threads), portanto, usar referências
emprestadas nesse contexto é seguro.

Algumas dessas funções foram adicionadas no Python 3.13. Você pode
usar o pacote pythoncapi-compat para fornecer implementações dessas
funções para versões mais antigas do Python.


APIs de alocação de memória
===========================

A API C de gerenciamento de memória do Python fornece funções em três
domínios de alocação diferentes: "raw", "mem" e "object". Para
segurança de thread, a construção de threads livres requer que apenas
objetos Python sejam alocados usando o domínio do objeto e que todos
os objetos Python sejam alocados usando esse domínio. Isso difere das
versões anteriores do Python, onde era apenas uma melhor prática e não
um requisito estrito.

Nota:

  Procure usos de "PyObject_Malloc()" em sua extensão e verifique se a
  memória alocada é usada para objetos Python. Use "PyMem_Malloc()"
  para alocar buffers em vez de "PyObject_Malloc()".


APIs da GIL e de estado de threads
==================================

Python fornece um conjunto de funções e macros para gerenciar o estado
de threads e a GIL, como:

* "PyGILState_Ensure()" e "PyGILState_Release()"

* "PyEval_SaveThread()" e "PyEval_RestoreThread()"

* "Py_BEGIN_ALLOW_THREADS" e "Py_END_ALLOW_THREADS"

Estas funções ainda devem ser usadas na construção com threads livres
para gerenciar o estado de threads mesmo quando a *GIL* está
desabilitada. Por exemplo, se você criar uma thread fora do Python,
você deve chamar "PyGILState_Ensure()" antes de chamar a API Python
para garantir que a thread tenha um estado de thread válido para o
Python.

Você deve continuar a chamar "PyEval_SaveThread()" ou
"Py_BEGIN_ALLOW_THREADS" em torno de operações de travamento, como E/S
ou aquisições de trava, para permitir que outras threads executem o
*coletor de lixo cíclico*.


Protegendo o estado interno das extensões
=========================================

Sua extensão pode ter um estado interno que foi previamente protegido
pela GIL. Talvez seja necessário adicionar uma trava para proteger
esse estado. A abordagem dependerá da sua extensão, mas alguns padrões
comuns incluem:

* **Caches**: caches globais são uma fonte comum de estado
  compartilhado. Considere usar uma trava para proteger o cache ou
  desativá-lo na construção com threads livres se o cache não for
  crítico para o desempenho.

* **Estado global**: o estado global pode precisar ser protegido por
  uma trava ou movido para o armazenamento local do thread. C11 e
  C++11 fornecem "thread_local" ou "_Thread_local" para armazenamento
  local de thread.


Construindo extensões para a construção com threads livres
==========================================================

As extensões da API C precisam ser construídas especificamente para a
construção com threads livres. As wheels, bibliotecas compartilhadas e
binários são indicados por um sufixo "t".

* pypa/manylinux oferece suporte à construção com threads livres, com
  o sufixo "t", como "python3.13t".

* pypa/cibuildwheel tem suporte à construção com threads livres se
  você definir CIBW_FREE_THREADED_SUPPORT.


API C limitada e ABI estável
----------------------------

A construção com threads livres não tem suporte atualmente à API C
limitada ou à ABI estável. Se você usar setuptools para construir sua
extensão e atualmente definir "py_limited_api=True", você pode usar
"py_limited_api=not sysconfig.get_config_var("Py_GIL_DISABLED")" para
não usar a API limitada ao construir com a construção com threads
livres.

Nota:

  Você precisará construir wheels separadas especificamente para a
  construção com threads livres. Se você usa atualmente a ABI estável,
  poderá continuar a construir uma única wheel para várias versões do
  Python sem threads livres.


Windows
-------

Devido a uma limitação do instalador oficial do Windows, você
precisará definir manualmente "Py_GIL_DISABLED=1" ao construir
extensões a partir do código-fonte.

Ver também:

  Portando módulos de extensão para oferecer suporte a threads livres
  (em inglês): Um guia de portabilidade mantido pela comunidade para
  autores de extensões.
