Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-116167: Allow disabling the GIL with PYTHON_GIL=0 or -X gil=0 #116338

Merged
merged 11 commits into from Mar 11, 2024
3 changes: 3 additions & 0 deletions Include/cpython/initconfig.h
Expand Up @@ -181,6 +181,9 @@ typedef struct PyConfig {
int int_max_str_digits;

int cpu_count;
#ifdef Py_GIL_DISABLED
int enable_gil;
#endif

/* --- Path configuration inputs ------------ */
int pathconfig_warnings;
Expand Down
5 changes: 5 additions & 0 deletions Include/internal/pycore_gil.h
Expand Up @@ -20,6 +20,11 @@ extern "C" {
#define FORCE_SWITCHING

struct _gil_runtime_state {
#ifdef Py_GIL_DISABLED
/* Whether or not this GIL is being used. Can change from 0 to 1 at runtime
if, for example, a module that requires the GIL is loaded. */
int enabled;
#endif
/* microseconds (the Python API uses seconds, though) */
unsigned long interval;
/* Last PyThreadState holding / having held the GIL. This helps us
Expand Down
12 changes: 12 additions & 0 deletions Include/internal/pycore_initconfig.h
Expand Up @@ -153,6 +153,18 @@ typedef enum {
_PyConfig_INIT_ISOLATED = 3
} _PyConfigInitEnum;

typedef enum {
/* For now, this means the GIL is enabled.

gh-116329: This will eventually change to "the GIL is disabled but can
be reenabled by loading an incompatible extension module." */
_PyConfig_GIL_DEFAULT,

/* The GIL has been forced off or on, and will not be affected by module loading. */
_PyConfig_GIL_DISABLE,
_PyConfig_GIL_ENABLE,
} _PyConfigGILEnum;

// Export for '_testembed' program
PyAPI_FUNC(void) _PyConfig_InitCompatConfig(PyConfig *config);

Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_embed.py
Expand Up @@ -452,6 +452,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
'hash_seed': 0,
'int_max_str_digits': sys.int_info.default_max_str_digits,
'cpu_count': -1,
'enable_gil': 0,
'faulthandler': 0,
'tracemalloc': 0,
'perf_profiling': 0,
Expand Down
15 changes: 15 additions & 0 deletions Python/ceval_gil.c
Expand Up @@ -219,6 +219,11 @@ drop_gil(PyInterpreterState *interp, PyThreadState *tstate)
// XXX assert(tstate == NULL || !tstate->_status.cleared);

struct _gil_runtime_state *gil = ceval->gil;
#ifdef Py_GIL_DISABLED
if (!gil->enabled) {
return;
}
#endif
if (!_Py_atomic_load_ptr_relaxed(&gil->locked)) {
Py_FatalError("drop_gil: GIL is not locked");
}
Expand Down Expand Up @@ -294,6 +299,11 @@ take_gil(PyThreadState *tstate)
assert(_PyThreadState_CheckConsistency(tstate));
PyInterpreterState *interp = tstate->interp;
struct _gil_runtime_state *gil = interp->ceval.gil;
#ifdef Py_GIL_DISABLED
if (!gil->enabled) {
return;
}
#endif

/* Check that _PyEval_InitThreads() was called to create the lock */
assert(gil_created(gil));
Expand Down Expand Up @@ -438,6 +448,11 @@ static void
init_own_gil(PyInterpreterState *interp, struct _gil_runtime_state *gil)
{
assert(!gil_created(gil));
#ifdef Py_GIL_DISABLED
// gh-116329: Once it is safe to do so, change this condition to
// (enable_gil == _PyConfig_GIL_ENABLE), so the GIL is disabled by default.
gil->enabled = _PyInterpreterState_GetConfig(interp)->enable_gil != _PyConfig_GIL_DISABLE;
#endif
create_gil(gil);
assert(gil_created(gil));
interp->ceval.gil = gil;
Expand Down
22 changes: 22 additions & 0 deletions Python/initconfig.c
Expand Up @@ -93,6 +93,9 @@ static const PyConfigSpec PYCONFIG_SPEC[] = {
SPEC(safe_path, UINT),
SPEC(int_max_str_digits, INT),
SPEC(cpu_count, INT),
#ifdef Py_GIL_DISABLED
SPEC(enable_gil, INT),
#endif
SPEC(pathconfig_warnings, UINT),
SPEC(program_name, WSTR),
SPEC(pythonpath_env, WSTR_OPT),
Expand Down Expand Up @@ -276,6 +279,9 @@ static const char usage_envvars[] =
"PYTHON_CPU_COUNT: Overrides the return value of os.process_cpu_count(),\n"
" os.cpu_count(), and multiprocessing.cpu_count() if set to\n"
" a positive integer.\n"
#ifdef Py_GIL_DISABLED
"PYTHON_GIL : When set to 0, disables the GIL.\n"
#endif
"PYTHONDEVMODE : enable the development mode.\n"
"PYTHONPYCACHEPREFIX: root directory for bytecode cache (pyc) files.\n"
"PYTHONWARNDEFAULTENCODING: enable opt-in EncodingWarning for 'encoding=None'.\n"
Expand Down Expand Up @@ -860,6 +866,9 @@ _PyConfig_InitCompatConfig(PyConfig *config)
config->_is_python_build = 0;
config->code_debug_ranges = 1;
config->cpu_count = -1;
#ifdef Py_GIL_DISABLED
config->enable_gil = _PyConfig_GIL_DEFAULT;
#endif
}


Expand Down Expand Up @@ -1642,6 +1651,19 @@ config_read_env_vars(PyConfig *config)
config->safe_path = 1;
}

const char *gil = config_get_env(config, "PYTHON_GIL");
if (gil != NULL) {
#ifdef Py_GIL_DISABLED
if (strcmp(gil, "0") == 0) {
config->enable_gil = _PyConfig_GIL_DISABLE;
} else if (strcmp(gil, "1") != 0) {
return _PyStatus_ERR("PYTHON_GIL must be \"0\" or \"1\"");
}
#else
return _PyStatus_ERR("PYTHON_GIL is not supported by this build");
#endif
}

return _PyStatus_OK();
}

Expand Down