Skip to content

Commit f546dbf

Browse files
committedApr 23, 2023
Enable/disable the GIL at runtime
The GIL is controlled by the environment variable PYTHONGIL or the flag "-X nogil". The GIL is enabled by default. The interpreter will currently crash when running multi-threaded programs without the GIL.
1 parent 4ab5757 commit f546dbf

File tree

9 files changed

+66
-9
lines changed

9 files changed

+66
-9
lines changed
 

‎Include/cpython/initconfig.h

+3
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ typedef struct PyPreConfig {
119119
environment variable. */
120120
int dev_mode;
121121

122+
int disable_gil; /* Disable the global interpreter lock */
123+
122124
/* Memory allocator: PYTHONMALLOC env var.
123125
See PyMemAllocatorName for valid values. */
124126
int allocator;
@@ -137,6 +139,7 @@ typedef struct PyConfig {
137139
int isolated;
138140
int use_environment;
139141
int dev_mode;
142+
int disable_gil;
140143
int install_signal_handlers;
141144
int use_hash_seed;
142145
unsigned long hash_seed;

‎Include/internal/pycore_gil.h

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ extern "C" {
2121
#define FORCE_SWITCHING
2222

2323
struct _gil_runtime_state {
24+
/* Should we use the GIL? */
25+
int enabled;
2426
/* microseconds (the Python API uses seconds, though) */
2527
unsigned long interval;
2628
/* Last PyThreadState holding / having held the GIL. This helps us

‎Include/internal/pycore_initconfig.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,15 @@ typedef struct {
103103
int use_environment; /* -E option */
104104
int dev_mode; /* -X dev and PYTHONDEVMODE */
105105
int warn_default_encoding; /* -X warn_default_encoding and PYTHONWARNDEFAULTENCODING */
106+
int disable_gil; /* -X nogil and PYTHONGIL=0 */
106107
} _PyPreCmdline;
107108

108109
#define _PyPreCmdline_INIT \
109110
(_PyPreCmdline){ \
110111
.use_environment = -1, \
111112
.isolated = -1, \
112-
.dev_mode = -1}
113+
.dev_mode = -1, \
114+
.disable_gil = -1}
113115
/* Note: _PyPreCmdline_INIT sets other fields to 0/NULL */
114116

115117
extern void _PyPreCmdline_Clear(_PyPreCmdline *cmdline);

‎Lib/subprocess.py

+2
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,8 @@ def _args_from_interpreter_flags():
345345
# -X options
346346
if dev_mode:
347347
args.extend(('-X', 'dev'))
348+
if sys.flags.nogil:
349+
args.extend(('-X', 'nogil'))
348350
for opt in ('faulthandler', 'tracemalloc', 'importtime',
349351
'showrefcount', 'utf8'):
350352
if opt in xoptions:

‎Lib/test/test_embed.py

+3
Original file line numberDiff line numberDiff line change
@@ -426,11 +426,13 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
426426
use_environment=0,
427427
utf8_mode=0,
428428
dev_mode=0,
429+
disable_gil=0,
429430
coerce_c_locale=0,
430431
)
431432

432433
COPY_PRE_CONFIG = [
433434
'dev_mode',
435+
'disable_gil',
434436
'isolated',
435437
'use_environment',
436438
]
@@ -440,6 +442,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
440442
'isolated': 0,
441443
'use_environment': 1,
442444
'dev_mode': 0,
445+
'disable_gil': 0,
443446

444447
'install_signal_handlers': 1,
445448
'use_hash_seed': 0,

‎Python/ceval_gil.c

+23-8
Original file line numberDiff line numberDiff line change
@@ -235,14 +235,16 @@ static int gil_created(struct _gil_runtime_state *gil)
235235
static void create_gil(struct _gil_runtime_state *gil)
236236
{
237237
MUTEX_INIT(gil->mutex);
238+
if (gil->enabled) {
238239
#ifdef FORCE_SWITCHING
239-
MUTEX_INIT(gil->switch_mutex);
240+
MUTEX_INIT(gil->switch_mutex);
240241
#endif
241-
COND_INIT(gil->cond);
242+
COND_INIT(gil->cond);
242243
#ifdef FORCE_SWITCHING
243-
COND_INIT(gil->switch_cond);
244+
COND_INIT(gil->switch_cond);
244245
#endif
245-
_Py_atomic_store_relaxed(&gil->last_holder, 0);
246+
_Py_atomic_store_relaxed(&gil->last_holder, 0);
247+
}
246248
_Py_ANNOTATE_RWLOCK_CREATE(&gil->locked);
247249
_Py_atomic_store_explicit(&gil->locked, 0, _Py_memory_order_release);
248250
}
@@ -252,12 +254,14 @@ static void destroy_gil(struct _gil_runtime_state *gil)
252254
/* some pthread-like implementations tie the mutex to the cond
253255
* and must have the cond destroyed first.
254256
*/
255-
COND_FINI(gil->cond);
256-
MUTEX_FINI(gil->mutex);
257+
if (gil->enabled) {
258+
COND_FINI(gil->cond);
259+
MUTEX_FINI(gil->mutex);
257260
#ifdef FORCE_SWITCHING
258-
COND_FINI(gil->switch_cond);
259-
MUTEX_FINI(gil->switch_mutex);
261+
COND_FINI(gil->switch_cond);
262+
MUTEX_FINI(gil->switch_mutex);
260263
#endif
264+
}
261265
_Py_atomic_store_explicit(&gil->locked, -1,
262266
_Py_memory_order_release);
263267
_Py_ANNOTATE_RWLOCK_DESTROY(&gil->locked);
@@ -277,6 +281,9 @@ drop_gil(struct _ceval_runtime_state *ceval, struct _ceval_state *ceval2,
277281
PyThreadState *tstate)
278282
{
279283
struct _gil_runtime_state *gil = &ceval->gil;
284+
if (!gil->enabled) {
285+
return;
286+
}
280287
if (!_Py_atomic_load_relaxed(&gil->locked)) {
281288
Py_FatalError("drop_gil: GIL is not locked");
282289
}
@@ -362,6 +369,10 @@ take_gil(PyThreadState *tstate)
362369
struct _ceval_state *ceval2 = &interp->ceval;
363370
struct _gil_runtime_state *gil = &ceval->gil;
364371

372+
if (!gil->enabled) {
373+
return;
374+
}
375+
365376
/* Check that _PyEval_InitThreads() was called to create the lock */
366377
assert(gil_created(gil));
367378

@@ -498,6 +509,7 @@ _PyEval_InitGIL(PyThreadState *tstate)
498509
}
499510

500511
struct _gil_runtime_state *gil = &tstate->interp->runtime->ceval.gil;
512+
gil->enabled = !_PyRuntime.preconfig.disable_gil;
501513
assert(!gil_created(gil));
502514

503515
PyThread_init_thread();
@@ -569,6 +581,9 @@ PyEval_ReleaseLock(void)
569581
void
570582
_PyEval_ReleaseLock(PyThreadState *tstate)
571583
{
584+
if (!_PyRuntime.ceval.gil.enabled) {
585+
return;
586+
}
572587
struct _ceval_runtime_state *ceval = &tstate->interp->runtime->ceval;
573588
struct _ceval_state *ceval2 = &tstate->interp->ceval;
574589
drop_gil(ceval, ceval2, tstate);

‎Python/initconfig.c

+6
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,7 @@ config_check_consistency(const PyConfig *config)
656656
assert(config->isolated >= 0);
657657
assert(config->use_environment >= 0);
658658
assert(config->dev_mode >= 0);
659+
assert(config->disable_gil >= 0);
659660
assert(config->install_signal_handlers >= 0);
660661
assert(config->use_hash_seed >= 0);
661662
assert(config->hash_seed <= MAX_HASH_SEED);
@@ -763,6 +764,7 @@ _PyConfig_InitCompatConfig(PyConfig *config)
763764
config->isolated = -1;
764765
config->use_environment = -1;
765766
config->dev_mode = -1;
767+
config->disable_gil = -1;
766768
config->install_signal_handlers = 1;
767769
config->use_hash_seed = -1;
768770
config->faulthandler = -1;
@@ -848,6 +850,7 @@ PyConfig_InitIsolatedConfig(PyConfig *config)
848850
config->use_environment = 0;
849851
config->user_site_directory = 0;
850852
config->dev_mode = 0;
853+
config->disable_gil = 0;
851854
config->install_signal_handlers = 0;
852855
config->use_hash_seed = 0;
853856
config->faulthandler = 0;
@@ -959,6 +962,7 @@ _PyConfig_Copy(PyConfig *config, const PyConfig *config2)
959962
COPY_ATTR(isolated);
960963
COPY_ATTR(use_environment);
961964
COPY_ATTR(dev_mode);
965+
COPY_ATTR(disable_gil);
962966
COPY_ATTR(install_signal_handlers);
963967
COPY_ATTR(use_hash_seed);
964968
COPY_ATTR(hash_seed);
@@ -1071,6 +1075,7 @@ _PyConfig_AsDict(const PyConfig *config)
10711075
SET_ITEM_INT(isolated);
10721076
SET_ITEM_INT(use_environment);
10731077
SET_ITEM_INT(dev_mode);
1078+
SET_ITEM_INT(disable_gil);
10741079
SET_ITEM_INT(install_signal_handlers);
10751080
SET_ITEM_INT(use_hash_seed);
10761081
SET_ITEM_UINT(hash_seed);
@@ -2329,6 +2334,7 @@ _PyConfig_Write(const PyConfig *config, _PyRuntimeState *runtime)
23292334
preconfig->isolated = config->isolated;
23302335
preconfig->use_environment = config->use_environment;
23312336
preconfig->dev_mode = config->dev_mode;
2337+
preconfig->disable_gil = config->disable_gil;
23322338

23332339
if (_Py_SetArgcArgv(config->orig_argv.length,
23342340
config->orig_argv.items) < 0)

‎Python/preconfig.c

+22
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ precmdline_get_preconfig(_PyPreCmdline *cmdline, const PyPreConfig *config)
138138
COPY_ATTR(isolated);
139139
COPY_ATTR(use_environment);
140140
COPY_ATTR(dev_mode);
141+
COPY_ATTR(disable_gil);
141142

142143
#undef COPY_ATTR
143144
}
@@ -152,6 +153,7 @@ precmdline_set_preconfig(const _PyPreCmdline *cmdline, PyPreConfig *config)
152153
COPY_ATTR(isolated);
153154
COPY_ATTR(use_environment);
154155
COPY_ATTR(dev_mode);
156+
COPY_ATTR(disable_gil);
155157

156158
#undef COPY_ATTR
157159
}
@@ -171,6 +173,7 @@ _PyPreCmdline_SetConfig(const _PyPreCmdline *cmdline, PyConfig *config)
171173
COPY_ATTR(isolated);
172174
COPY_ATTR(use_environment);
173175
COPY_ATTR(dev_mode);
176+
COPY_ATTR(disable_gil);
174177
COPY_ATTR(warn_default_encoding);
175178
return _PyStatus_OK();
176179

@@ -267,9 +270,24 @@ _PyPreCmdline_Read(_PyPreCmdline *cmdline, const PyPreConfig *preconfig)
267270
cmdline->warn_default_encoding = 1;
268271
}
269272

273+
/* GIL */
274+
if (cmdline->disable_gil < 0) {
275+
const char* env = NULL;
276+
if (_Py_get_xoption(&cmdline->xoptions, L"nogil")) {
277+
cmdline->disable_gil = 1;
278+
}
279+
else if ((env = _Py_GetEnv(cmdline->use_environment, "PYTHONGIL"))) {
280+
cmdline->disable_gil = (strcmp(env, "0") == 0);
281+
}
282+
else {
283+
cmdline->disable_gil = 0;
284+
}
285+
}
286+
270287
assert(cmdline->use_environment >= 0);
271288
assert(cmdline->isolated >= 0);
272289
assert(cmdline->dev_mode >= 0);
290+
assert(cmdline->disable_gil >= 0);
273291
assert(cmdline->warn_default_encoding >= 0);
274292

275293
return _PyStatus_OK();
@@ -300,6 +318,7 @@ _PyPreConfig_InitCompatConfig(PyPreConfig *config)
300318
config->coerce_c_locale_warn = 0;
301319

302320
config->dev_mode = -1;
321+
config->disable_gil = -1;
303322
config->allocator = PYMEM_ALLOCATOR_NOT_SET;
304323
#ifdef MS_WINDOWS
305324
config->legacy_windows_fs_encoding = -1;
@@ -386,6 +405,7 @@ preconfig_copy(PyPreConfig *config, const PyPreConfig *config2)
386405
COPY_ATTR(use_environment);
387406
COPY_ATTR(configure_locale);
388407
COPY_ATTR(dev_mode);
408+
COPY_ATTR(disable_gil);
389409
COPY_ATTR(coerce_c_locale);
390410
COPY_ATTR(coerce_c_locale_warn);
391411
COPY_ATTR(utf8_mode);
@@ -433,6 +453,7 @@ _PyPreConfig_AsDict(const PyPreConfig *config)
433453
SET_ITEM_INT(legacy_windows_fs_encoding);
434454
#endif
435455
SET_ITEM_INT(dev_mode);
456+
SET_ITEM_INT(disable_gil);
436457
SET_ITEM_INT(allocator);
437458
return dict;
438459

@@ -456,6 +477,7 @@ _PyPreConfig_GetConfig(PyPreConfig *preconfig, const PyConfig *config)
456477
COPY_ATTR(isolated);
457478
COPY_ATTR(use_environment);
458479
COPY_ATTR(dev_mode);
480+
COPY_ATTR(disable_gil);
459481

460482
#undef COPY_ATTR
461483
}

‎Python/sysmodule.c

+2
Original file line numberDiff line numberDiff line change
@@ -2726,6 +2726,7 @@ static PyStructSequence_Field flags_fields[] = {
27262726
{"hash_randomization", "-R"},
27272727
{"isolated", "-I"},
27282728
{"dev_mode", "-X dev"},
2729+
{"nogil", "-X nogil"},
27292730
{"utf8_mode", "-X utf8"},
27302731
{"warn_default_encoding", "-X warn_default_encoding"},
27312732
{"safe_path", "-P"},
@@ -2775,6 +2776,7 @@ set_flags_from_config(PyInterpreterState *interp, PyObject *flags)
27752776
SetFlag(config->use_hash_seed == 0 || config->hash_seed != 0);
27762777
SetFlag(config->isolated);
27772778
SetFlagObj(PyBool_FromLong(config->dev_mode));
2779+
SetFlagObj(PyBool_FromLong(config->disable_gil));
27782780
SetFlag(preconfig->utf8_mode);
27792781
SetFlag(config->warn_default_encoding);
27802782
SetFlagObj(PyBool_FromLong(config->safe_path));

0 commit comments

Comments
 (0)
Please sign in to comment.