# 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
    /* code that only runs in the free-threaded build */
    #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.