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-116322: Add Py_mod_gil module slot #116882

Merged
merged 24 commits into from
May 3, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
fa92cb4
Add Py_mod_gil module slot
swtaarrs Mar 12, 2024
7edf796
Merge remote-tracking branch 'upstream/main' into cpython-mod-gil
swtaarrs Mar 22, 2024
450aa41
Some review comments from Eric
swtaarrs Mar 27, 2024
6bbd281
Merge remote-tracking branch 'upstream/main' into cpython-mod-gil
swtaarrs Apr 3, 2024
47b9e26
Fix enabling the GIL, also support disabling the GIL
swtaarrs Apr 15, 2024
f7c3e50
Merge remote-tracking branch 'upstream/main' into cpython-mod-gil
swtaarrs Apr 15, 2024
6c198e4
Fix module size in test_objecttypes
swtaarrs Apr 15, 2024
554c5b4
Add missing : in Py_mod_gil documentation
swtaarrs Apr 15, 2024
cd187a0
Merge remote-tracking branch 'upstream/main' into cpython-mod-gil
swtaarrs Apr 23, 2024
8057478
From Eric: better loop, move _PyImport_CheckGILForModule
swtaarrs Apr 23, 2024
a8f0943
Merge remote-tracking branch 'upstream/main' into cpython-mod-gil
swtaarrs Apr 26, 2024
bbb949e
Remove code to enable/disable the GIL (it will go in a different PR)
swtaarrs Apr 30, 2024
7f1205e
Don't put PyModule_SetGIL() in the limited API
swtaarrs Apr 26, 2024
f8b02b3
Set m->md_gil in PyModule_FromDefAndSpec2, rename/guard PyModule_SetG…
swtaarrs May 1, 2024
d2bad05
Fix limited API version for Py_mod_gil
swtaarrs May 1, 2024
d7d59f0
Update NEWS entry for behavioral changes
swtaarrs Apr 30, 2024
ccd6e00
Mark all modules as not using GIL
swtaarrs May 1, 2024
8846586
Merge remote-tracking branch 'upstream/main' into HEAD
swtaarrs May 1, 2024
99bc5cb
Fix 'gil_slot set but not used' warning
swtaarrs May 1, 2024
6882c13
Patch generator for Python-ast.c instead of just the file itself
swtaarrs May 1, 2024
da63df1
Update limited API version for _scproxy.c
swtaarrs May 1, 2024
2998def
Fix _testconsole.c for Windows build
swtaarrs May 1, 2024
77d1652
Fix winsound.c for Windows build
swtaarrs May 2, 2024
d1fe0cc
Mark more modules as not using the GIL
swtaarrs May 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions Doc/c-api/module.rst
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,31 @@ The available slot types are:

.. versionadded:: 3.12

.. c:macro: Py_mod_gil

Specifies one of the following values:

.. c:macro:: Py_MOD_GIL_USED

The module assumes the presence of the global interpreter lock (GIL), and
may access global state without synchronization.
swtaarrs marked this conversation as resolved.
Show resolved Hide resolved

.. c:macro:: Py_MOD_GIL_NOT_USED

The module is safe to run without an active GIL.
swtaarrs marked this conversation as resolved.
Show resolved Hide resolved

This slot is only used by Python builds configured with
:option:`--disable-gil`, and determines whether or not importing this module
swtaarrs marked this conversation as resolved.
Show resolved Hide resolved
will cause the GIL to be automatically enabled. See :envvar:`PYTHON_GIL` and
:option:`-X gil <-X>` for more detail.

Multiple ``Py_mod_gil`` slots may not be specified in one module definition.

If ``Py_mod_gil`` is not specified, the import machinery defaults to
``Py_MOD_GIL_USED``.

.. versionadded: 3.13

See :PEP:`489` for more details on multi-phase initialization.

Low-level module creation functions
Expand Down
9 changes: 9 additions & 0 deletions Include/internal/pycore_ceval.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,15 @@ extern void _PyEval_FiniGIL(PyInterpreterState *interp);
extern void _PyEval_AcquireLock(PyThreadState *tstate);
extern void _PyEval_ReleaseLock(PyInterpreterState *, PyThreadState *);

#ifdef Py_GIL_DISABLED
// Enable the GIL for the given thread's interpreter. This may affect other
// interpreters, if the GIL is shared.
//
// Returns 1 if this call enabled the GIL, or 0 if it was already enabled. The
// caller will hold the GIL upon return.
extern int _PyEval_EnableGIL(PyThreadState *tstate);
#endif

extern void _PyEval_DeactivateOpCache(void);


Expand Down
12 changes: 11 additions & 1 deletion Include/moduleobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,13 @@ struct PyModuleDef_Slot {
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030c0000
# define Py_mod_multiple_interpreters 3
#endif
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030e0000
# define Py_mod_gil 4
#endif


#ifndef Py_LIMITED_API
#define _Py_mod_LAST_SLOT 3
#define _Py_mod_LAST_SLOT 4
#endif

#endif /* New in 3.5 */
Expand All @@ -90,6 +94,12 @@ struct PyModuleDef_Slot {
# define Py_MOD_PER_INTERPRETER_GIL_SUPPORTED ((void *)2)
#endif

/* for Py_mod_gil: */
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030e0000
# define Py_MOD_GIL_USED ((void *)0)
# define Py_MOD_GIL_NOT_USED ((void *)1)
#endif

struct PyModuleDef {
PyModuleDef_Base m_base;
const char* m_name;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Multi-phase init extension modules may indicate to the runtime that they can
run without the GIL by providing ``Py_MOD_GIL_NOT_USED`` for the ``Py_mod_gil``
slot. In ``--disable-gil`` builds, loading extensions that do not provide this
slot will enable the GIL for the remainder of the current interpreter, unless
the GIL was explicitly disabled by ``PYTHON_GIL=0`` or ``-Xgil=0``.
37 changes: 37 additions & 0 deletions Objects/moduleobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

#include "Python.h"
#include "pycore_call.h" // _PyObject_CallNoArgs()
#include "pycore_ceval.h" // _PyEval_EnableGIL()
#include "pycore_initconfig.h" // _PyConfig_GIL_DEFAULT
#include "pycore_interp.h" // PyInterpreterState.importlib
#include "pycore_modsupport.h" // _PyModule_CreateInitialized()
#include "pycore_moduleobject.h" // _PyModule_GetDef()
Expand Down Expand Up @@ -249,6 +251,8 @@
PyObject *m = NULL;
int has_multiple_interpreters_slot = 0;
void *multiple_interpreters = (void *)0;
int has_gil_slot = 0;
void *gil_slot = (void *)Py_MOD_GIL_USED;

Check warning on line 255 in Objects/moduleobject.c

View workflow job for this annotation

GitHub Actions / Address sanitizer

variable ‘gil_slot’ set but not used [-Wunused-but-set-variable]

Check warning on line 255 in Objects/moduleobject.c

View workflow job for this annotation

GitHub Actions / Ubuntu SSL tests with OpenSSL (1.1.1w)

variable ‘gil_slot’ set but not used [-Wunused-but-set-variable]

Check warning on line 255 in Objects/moduleobject.c

View workflow job for this annotation

GitHub Actions / Hypothesis tests on Ubuntu

variable ‘gil_slot’ set but not used [-Wunused-but-set-variable]

Check warning on line 255 in Objects/moduleobject.c

View workflow job for this annotation

GitHub Actions / Ubuntu SSL tests with OpenSSL (3.0.13)

variable ‘gil_slot’ set but not used [-Wunused-but-set-variable]

Check warning on line 255 in Objects/moduleobject.c

View workflow job for this annotation

GitHub Actions / Ubuntu SSL tests with OpenSSL (3.1.5)

variable ‘gil_slot’ set but not used [-Wunused-but-set-variable]

Check warning on line 255 in Objects/moduleobject.c

View workflow job for this annotation

GitHub Actions / Ubuntu SSL tests with OpenSSL (3.2.1)

variable ‘gil_slot’ set but not used [-Wunused-but-set-variable]

Check warning on line 255 in Objects/moduleobject.c

View workflow job for this annotation

GitHub Actions / Ubuntu / build and test

variable ‘gil_slot’ set but not used [-Wunused-but-set-variable]
swtaarrs marked this conversation as resolved.
Show resolved Hide resolved
int has_execution_slots = 0;
const char *name;
int ret;
Expand Down Expand Up @@ -303,6 +307,17 @@
multiple_interpreters = cur_slot->value;
has_multiple_interpreters_slot = 1;
break;
case Py_mod_gil:
if (has_gil_slot) {
PyErr_Format(
PyExc_SystemError,
"module %s has more than one 'gil' slot",
name);
goto error;
}
gil_slot = cur_slot->value;
has_gil_slot = 1;
break;
default:
assert(cur_slot->slot < 0 || cur_slot->slot > _Py_mod_LAST_SLOT);
PyErr_Format(
Expand Down Expand Up @@ -333,6 +348,27 @@
goto error;
}

#ifdef Py_GIL_DISABLED
PyThreadState *tstate = _PyThreadState_GET();
const PyConfig *config = _PyInterpreterState_GetConfig(interp);
if (gil_slot == Py_MOD_GIL_USED && config->enable_gil == _PyConfig_GIL_DEFAULT) {
ericsnowcurrently marked this conversation as resolved.
Show resolved Hide resolved
if (_PyEval_EnableGIL(tstate)) {
swtaarrs marked this conversation as resolved.
Show resolved Hide resolved
PyErr_WarnFormat(
PyExc_RuntimeWarning,
1,
"The global interpreter lock (GIL) has been enabled to load "
"module '%s', which has not declared that it can run safely "
"without the GIL. To override this behavior and keep the GIL "
"disabled (at your own risk), run with PYTHON_GIL=0 or -Xgil=0.",
name
);
ericsnowcurrently marked this conversation as resolved.
Show resolved Hide resolved
}
if (config->verbose) {
PySys_FormatStderr("# loading module '%s', which requires the GIL\n", name);
}
}
#endif

if (create) {
m = create(spec, def);
if (m == NULL) {
Expand Down Expand Up @@ -458,6 +494,7 @@
}
break;
case Py_mod_multiple_interpreters:
case Py_mod_gil:
/* handled in PyModule_FromDefAndSpec2 */
break;
default:
Expand Down
45 changes: 45 additions & 0 deletions Python/ceval_gil.c
Original file line number Diff line number Diff line change
Expand Up @@ -966,6 +966,51 @@ _PyEval_InitState(PyInterpreterState *interp)
_gil_initialize(&interp->_gil);
}

#ifdef Py_GIL_DISABLED
int
_PyEval_EnableGIL(PyThreadState *tstate)
{
struct _gil_runtime_state *gil = tstate->interp->ceval.gil;

// gil->enabled only transitions from 0 to 1, and only while the world is
// stopped, so this can be read without any synchronization.
if (gil->enabled) {
return 0;
}

// Enabling the GIL changes what it means to be an "attached" thread. To
// safely make this transition, we:
// 1. Detach the current thread.
// 2. Stop the world to detach (and suspend) all other threads.
// 3. Enable the GIL, if nobody else did between our check above and when
// our stop-the-world begins.
// 4. Start the world.
// 5. Attach the current thread. Other threads may attach and hold the GIL
// before this thread, which is harmless.
_PyThreadState_Detach(tstate);
swtaarrs marked this conversation as resolved.
Show resolved Hide resolved

// This could be an interpreter-local stop-the-world in situations where we
// know that this interpreter's GIL is not shared (and that it won't become
// shared before the stop-the-world begins). This operation will happen at
// most once per interpreter, though, so we always stop the whole world to
// keep things simpler.
_PyEval_StopTheWorldAll(&_PyRuntime);

int this_thread_enabled = 1;
if (gil->enabled) {
// A different thread enabled the GIL since our check above.
this_thread_enabled = 0;
} else {
gil->enabled = 1;
}

_PyEval_StartTheWorldAll(&_PyRuntime);
_PyThreadState_Attach(tstate);

return this_thread_enabled;
}
#endif


/* Do periodic things, like check for signals and async I/0.
* We need to do reasonably frequently, but not too frequently.
Expand Down
Loading