Skip to content

Commit

Permalink
Add -X gil option, add to sys.flags, modify test to cover env var…
Browse files Browse the repository at this point in the history
… and option
  • Loading branch information
swtaarrs committed Mar 7, 2024
1 parent 6d62fbb commit 971a9a1
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 46 deletions.
9 changes: 9 additions & 0 deletions Doc/using/cmdline.rst
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,9 @@ Miscellaneous options
:mod:`__main__`. This can be used to execute code early during Python
initialization. Python needs to be :ref:`built in debug mode <debug-build>`
for this option to exist. See also :envvar:`PYTHON_PRESITE`.
* :samp:`-X gil={0,1}` forces the GIL to be disabled or enabled,
respectively. Only available in builds configured with
:option:`--disable-gil`. See also :envvar:`PYTHON_GIL`.

It also allows passing arbitrary values and retrieving them through the
:data:`sys._xoptions` dictionary.
Expand Down Expand Up @@ -601,6 +604,9 @@ Miscellaneous options
.. versionchanged:: 3.13
Added the ``-X cpu_count`` and ``-X presite`` options.

.. versionadded:: 3.13
The ``-X gil`` option.

.. _using-on-controlling-color:

Controlling color
Expand Down Expand Up @@ -1143,6 +1149,9 @@ conflict.
If this variable is set to ``1``, the global interpreter lock (GIL) will be
forced on. Setting it to ``0`` forces the GIL off.

See also the :option:`-X gil` command-line option, which takes precedence
over this variable.

Needs Python configured with the :option:`--disable-gil` build option.

.. versionadded:: 3.13
Expand Down
6 changes: 3 additions & 3 deletions Include/internal/pycore_initconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,11 @@ typedef enum {
gh-116329: This will eventually change to "the GIL is disabled but can
be reenabled by loading an incompatible extension module." */
_PyConfig_GIL_DEFAULT,
_PyConfig_GIL_DEFAULT = -1,

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

// Export for '_testembed' program
Expand Down
2 changes: 1 addition & 1 deletion Lib/subprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ def _args_from_interpreter_flags():
if dev_mode:
args.extend(('-X', 'dev'))
for opt in ('faulthandler', 'tracemalloc', 'importtime',
'frozen_modules', 'showrefcount', 'utf8'):
'frozen_modules', 'showrefcount', 'utf8', 'gil'):
if opt in xoptions:
value = xoptions[opt]
if value is True:
Expand Down
9 changes: 9 additions & 0 deletions Lib/test/_test_embed_set_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import os
import sys
import unittest
from test import support
from test.support import MS_WINDOWS


Expand Down Expand Up @@ -211,6 +212,14 @@ def test_flags(self):
self.set_config(use_hash_seed=1, hash_seed=123)
self.assertEqual(sys.flags.hash_randomization, 1)

if support.Py_GIL_DISABLED:
self.set_config(enable_gil=-1)
self.assertEqual(sys.flags.gil, None)
self.set_config(enable_gil=0)
self.assertEqual(sys.flags.gil, 0)
self.set_config(enable_gil=1)
self.assertEqual(sys.flags.gil, 1)

def test_options(self):
self.check(warnoptions=[])
self.check(warnoptions=["default", "ignore"])
Expand Down
59 changes: 30 additions & 29 deletions Lib/test/test_cmd_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -870,36 +870,37 @@ def test_pythondevmode_env(self):
self.assertEqual(proc.returncode, 0, proc)

@unittest.skipUnless(support.Py_GIL_DISABLED,
"PYTHON_GIL only supported in Py_GIL_DISABLED builds")
def test_python_gil_env(self):
code = """if 1:
import _testinternalcapi
print(_testinternalcapi.get_configs()['config'].get('enable_gil'))
"""
args = [sys.executable, '-c', code]
env = dict(os.environ)
env.pop('PYTHON_GIL', None)

def run():
return subprocess.run(args, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, text=True, env=env)

proc = run()
self.assertEqual(proc.returncode, 0, proc)
self.assertEqual(proc.stdout.rstrip(), '0')
self.assertEqual(proc.stderr, '')

env['PYTHON_GIL'] = '0'
proc = run()
self.assertEqual(proc.returncode, 0, proc)
self.assertEqual(proc.stdout.rstrip(), '1')
self.assertEqual(proc.stderr, '')
"PYTHON_GIL and -X gil only supported in Py_GIL_DISABLED builds")
def test_python_gil(self):
cases = [
# (env, opt, expected, msg)
(None, None, 'None', "no options set"),
('0', None, '0', "PYTHON_GIL=0"),
('1', None, '1', "PYTHON_GIL=1"),
('1', '0', '0', "-X gil=0 overrides PYTHON_GIL=1"),
(None, '0', '0', "-X gil=0"),
(None, '1', '1', "-X gil=1"),
]

env['PYTHON_GIL'] = '1'
proc = run()
self.assertEqual(proc.returncode, 0, proc)
self.assertEqual(proc.stdout.rstrip(), '2')
self.assertEqual(proc.stderr, '')
code = "import sys; print(sys.flags.gil)"
environ = dict(os.environ)

for env, opt, expected, msg in cases:
with self.subTest(msg, env=env, opt=opt):
environ.pop('PYTHON_GIL', None)
if env is not None:
environ['PYTHON_GIL'] = env
extra_args = []
if opt is not None:
extra_args = ['-X', f'gil={opt}']

proc = subprocess.run([sys.executable, *extra_args, '-c', code],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True, env=environ)
self.assertEqual(proc.returncode, 0, proc)
self.assertEqual(proc.stdout.rstrip(), expected)
self.assertEqual(proc.stderr, '')

@unittest.skipUnless(sys.platform == 'win32',
'bpo-32457 only applies on Windows')
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_embed.py
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
if support.Py_DEBUG:
CONFIG_COMPAT['run_presite'] = None
if support.Py_GIL_DISABLED:
CONFIG_COMPAT['enable_gil'] = 0
CONFIG_COMPAT['enable_gil'] = -1
if MS_WINDOWS:
CONFIG_COMPAT.update({
'legacy_windows_stdio': 0,
Expand Down
43 changes: 31 additions & 12 deletions Python/initconfig.c
Original file line number Diff line number Diff line change
Expand Up @@ -1583,6 +1583,24 @@ config_wstr_to_int(const wchar_t *wstr, int *result)
return 0;
}

static PyStatus
config_read_gil(PyConfig *config, size_t len, wchar_t first_char)
{
#ifdef Py_GIL_DISABLED
if (len == 1 && first_char == L'0') {
config->enable_gil = _PyConfig_GIL_DISABLE;
}
else if (len == 1 && first_char == L'1') {
config->enable_gil = _PyConfig_GIL_ENABLE;
}
else {
return _PyStatus_ERR("PYTHON_GIL / -X gil must be \"0\" or \"1\"");
}
return _PyStatus_OK();
#else
return _PyStatus_ERR("PYTHON_GIL / -X gil are not supported by this build");
#endif
}

static PyStatus
config_read_env_vars(PyConfig *config)
Expand Down Expand Up @@ -1663,19 +1681,11 @@ config_read_env_vars(PyConfig *config)

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) {
config->enable_gil = _PyConfig_GIL_ENABLE;
}
else {
return _PyStatus_ERR("PYTHON_GIL must be \"0\" or \"1\"");
size_t len = strlen(gil);
status = config_read_gil(config, len, gil[0]);
if (_PyStatus_EXCEPTION(status)) {
return status;
}
#else
return _PyStatus_ERR("PYTHON_GIL is not supported by this build");
#endif
}

return _PyStatus_OK();
Expand Down Expand Up @@ -2233,6 +2243,15 @@ config_read(PyConfig *config, int compute_path_config)
config->show_ref_count = 1;
}

const wchar_t *x_gil = config_get_xoption_value(config, L"gil");
if (x_gil != NULL) {
size_t len = wcslen(x_gil);
status = config_read_gil(config, len, x_gil[0]);
if (_PyStatus_EXCEPTION(status)) {
return status;
}
}

#ifdef Py_STATS
if (config_get_xoption(config, L"pystats")) {
config->_pystats = 1;
Expand Down
11 changes: 11 additions & 0 deletions Python/sysmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -3048,6 +3048,7 @@ static PyStructSequence_Field flags_fields[] = {
{"warn_default_encoding", "-X warn_default_encoding"},
{"safe_path", "-P"},
{"int_max_str_digits", "-X int_max_str_digits"},
{"gil", "-X gil"},
{0}
};

Expand Down Expand Up @@ -3097,6 +3098,16 @@ set_flags_from_config(PyInterpreterState *interp, PyObject *flags)
SetFlag(config->warn_default_encoding);
SetFlagObj(PyBool_FromLong(config->safe_path));
SetFlag(config->int_max_str_digits);
#ifdef Py_GIL_DISABLED
if (config->enable_gil == _PyConfig_GIL_DEFAULT) {
SetFlagObj(Py_NewRef(Py_None));
}
else {
SetFlag(config->enable_gil);
}
#else
SetFlagObj(PyLong_FromLong(1));
#endif
#undef SetFlagObj
#undef SetFlag
return 0;
Expand Down

0 comments on commit 971a9a1

Please sign in to comment.