summaryrefslogtreecommitdiffstats
path: root/Python
diff options
context:
space:
mode:
authorEric Snow <ericsnowcurrently@gmail.com>2023-03-09 16:46:21 (GMT)
committerGitHub <noreply@github.com>2023-03-09 16:46:21 (GMT)
commitcf6e7c5e551b3513817d6a77ba88253dc8473298 (patch)
treeca5e5125a1478a2cbc4c65e4467f8fcf7568f34f /Python
parentb45d14b88611fefc6f054226d3e1117082d322c8 (diff)
downloadcpython-cf6e7c5e551b3513817d6a77ba88253dc8473298.zip
cpython-cf6e7c5e551b3513817d6a77ba88253dc8473298.tar.gz
cpython-cf6e7c5e551b3513817d6a77ba88253dc8473298.tar.bz2
gh-100227: Isolate the Import State to Each Interpreter (gh-101941)
Specific changes: * move the import lock to PyInterpreterState * move the "find_and_load" diagnostic state to PyInterpreterState Note that the import lock exists to keep multiple imports of the same module in the same interpreter (but in different threads) from stomping on each other. Independently, we use a distinct global lock to protect globally shared import state, especially related to loaded extension modules. For now we can rely on the GIL as that lock but with a per-interpreter GIL we'll need a new global lock. The remaining state in _PyRuntimeState.imports will (probably) continue being global. https://github.com/python/cpython/issues/100227
Diffstat (limited to 'Python')
-rw-r--r--Python/import.c98
1 files changed, 54 insertions, 44 deletions
diff --git a/Python/import.c b/Python/import.c
index 57d4eea..1bf4199 100644
--- a/Python/import.c
+++ b/Python/import.c
@@ -56,11 +56,6 @@ static struct _inittab *inittab_copy = NULL;
#define LAST_MODULE_INDEX _PyRuntime.imports.last_module_index
#define EXTENSIONS _PyRuntime.imports.extensions
-#define import_lock _PyRuntime.imports.lock.mutex
-#define import_lock_thread _PyRuntime.imports.lock.thread
-#define import_lock_level _PyRuntime.imports.lock.level
-
-#define FIND_AND_LOAD _PyRuntime.imports.find_and_load
#define PKGCONTEXT (_PyRuntime.imports.pkgcontext)
@@ -85,6 +80,16 @@ static struct _inittab *inittab_copy = NULL;
#define IMPORT_FUNC(interp) \
(interp)->imports.import_func
+#define IMPORT_LOCK(interp) \
+ (interp)->imports.lock.mutex
+#define IMPORT_LOCK_THREAD(interp) \
+ (interp)->imports.lock.thread
+#define IMPORT_LOCK_LEVEL(interp) \
+ (interp)->imports.lock.level
+
+#define FIND_AND_LOAD(interp) \
+ (interp)->imports.find_and_load
+
/*******************/
/* the import lock */
@@ -95,45 +100,45 @@ static struct _inittab *inittab_copy = NULL;
These calls are serialized by the global interpreter lock. */
void
-_PyImport_AcquireLock(void)
+_PyImport_AcquireLock(PyInterpreterState *interp)
{
unsigned long me = PyThread_get_thread_ident();
if (me == PYTHREAD_INVALID_THREAD_ID)
return; /* Too bad */
- if (import_lock == NULL) {
- import_lock = PyThread_allocate_lock();
- if (import_lock == NULL)
+ if (IMPORT_LOCK(interp) == NULL) {
+ IMPORT_LOCK(interp) = PyThread_allocate_lock();
+ if (IMPORT_LOCK(interp) == NULL)
return; /* Nothing much we can do. */
}
- if (import_lock_thread == me) {
- import_lock_level++;
+ if (IMPORT_LOCK_THREAD(interp) == me) {
+ IMPORT_LOCK_LEVEL(interp)++;
return;
}
- if (import_lock_thread != PYTHREAD_INVALID_THREAD_ID ||
- !PyThread_acquire_lock(import_lock, 0))
+ if (IMPORT_LOCK_THREAD(interp) != PYTHREAD_INVALID_THREAD_ID ||
+ !PyThread_acquire_lock(IMPORT_LOCK(interp), 0))
{
PyThreadState *tstate = PyEval_SaveThread();
- PyThread_acquire_lock(import_lock, WAIT_LOCK);
+ PyThread_acquire_lock(IMPORT_LOCK(interp), WAIT_LOCK);
PyEval_RestoreThread(tstate);
}
- assert(import_lock_level == 0);
- import_lock_thread = me;
- import_lock_level = 1;
+ assert(IMPORT_LOCK_LEVEL(interp) == 0);
+ IMPORT_LOCK_THREAD(interp) = me;
+ IMPORT_LOCK_LEVEL(interp) = 1;
}
int
-_PyImport_ReleaseLock(void)
+_PyImport_ReleaseLock(PyInterpreterState *interp)
{
unsigned long me = PyThread_get_thread_ident();
- if (me == PYTHREAD_INVALID_THREAD_ID || import_lock == NULL)
+ if (me == PYTHREAD_INVALID_THREAD_ID || IMPORT_LOCK(interp) == NULL)
return 0; /* Too bad */
- if (import_lock_thread != me)
+ if (IMPORT_LOCK_THREAD(interp) != me)
return -1;
- import_lock_level--;
- assert(import_lock_level >= 0);
- if (import_lock_level == 0) {
- import_lock_thread = PYTHREAD_INVALID_THREAD_ID;
- PyThread_release_lock(import_lock);
+ IMPORT_LOCK_LEVEL(interp)--;
+ assert(IMPORT_LOCK_LEVEL(interp) >= 0);
+ if (IMPORT_LOCK_LEVEL(interp) == 0) {
+ IMPORT_LOCK_THREAD(interp) = PYTHREAD_INVALID_THREAD_ID;
+ PyThread_release_lock(IMPORT_LOCK(interp));
}
return 1;
}
@@ -144,23 +149,23 @@ _PyImport_ReleaseLock(void)
We now acquire the import lock around fork() calls but on some platforms
(Solaris 9 and earlier? see isue7242) that still left us with problems. */
PyStatus
-_PyImport_ReInitLock(void)
+_PyImport_ReInitLock(PyInterpreterState *interp)
{
- if (import_lock != NULL) {
- if (_PyThread_at_fork_reinit(&import_lock) < 0) {
+ if (IMPORT_LOCK(interp) != NULL) {
+ if (_PyThread_at_fork_reinit(&IMPORT_LOCK(interp)) < 0) {
return _PyStatus_ERR("failed to create a new lock");
}
}
- if (import_lock_level > 1) {
+ if (IMPORT_LOCK_LEVEL(interp) > 1) {
/* Forked as a side effect of import */
unsigned long me = PyThread_get_thread_ident();
- PyThread_acquire_lock(import_lock, WAIT_LOCK);
- import_lock_thread = me;
- import_lock_level--;
+ PyThread_acquire_lock(IMPORT_LOCK(interp), WAIT_LOCK);
+ IMPORT_LOCK_THREAD(interp) = me;
+ IMPORT_LOCK_LEVEL(interp)--;
} else {
- import_lock_thread = PYTHREAD_INVALID_THREAD_ID;
- import_lock_level = 0;
+ IMPORT_LOCK_THREAD(interp) = PYTHREAD_INVALID_THREAD_ID;
+ IMPORT_LOCK_LEVEL(interp) = 0;
}
return _PyStatus_OK();
}
@@ -2506,8 +2511,8 @@ import_find_and_load(PyThreadState *tstate, PyObject *abs_name)
PyObject *mod = NULL;
PyInterpreterState *interp = tstate->interp;
int import_time = _PyInterpreterState_GetConfig(interp)->import_time;
-#define import_level FIND_AND_LOAD.import_level
-#define accumulated FIND_AND_LOAD.accumulated
+#define import_level FIND_AND_LOAD(interp).import_level
+#define accumulated FIND_AND_LOAD(interp).accumulated
_PyTime_t t1 = 0, accumulated_copy = accumulated;
@@ -2528,7 +2533,7 @@ import_find_and_load(PyThreadState *tstate, PyObject *abs_name)
* _PyDict_GetItemIdWithError().
*/
if (import_time) {
-#define header FIND_AND_LOAD.header
+#define header FIND_AND_LOAD(interp).header
if (header) {
fputs("import time: self [us] | cumulative | imported package\n",
stderr);
@@ -2867,10 +2872,6 @@ _PyImport_Fini(void)
{
/* Destroy the database used by _PyImport_{Fixup,Find}Extension */
_extensions_cache_clear_all();
- if (import_lock != NULL) {
- PyThread_free_lock(import_lock);
- import_lock = NULL;
- }
/* Use the same memory allocator as _PyImport_Init(). */
PyMemAllocatorEx old_alloc;
@@ -2959,6 +2960,11 @@ _PyImport_FiniCore(PyInterpreterState *interp)
PyErr_WriteUnraisable(NULL);
}
+ if (IMPORT_LOCK(interp) != NULL) {
+ PyThread_free_lock(IMPORT_LOCK(interp));
+ IMPORT_LOCK(interp) = NULL;
+ }
+
_PyImport_ClearCore(interp);
}
@@ -3090,7 +3096,9 @@ static PyObject *
_imp_lock_held_impl(PyObject *module)
/*[clinic end generated code: output=8b89384b5e1963fc input=9b088f9b217d9bdf]*/
{
- return PyBool_FromLong(import_lock_thread != PYTHREAD_INVALID_THREAD_ID);
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ return PyBool_FromLong(
+ IMPORT_LOCK_THREAD(interp) != PYTHREAD_INVALID_THREAD_ID);
}
/*[clinic input]
@@ -3106,7 +3114,8 @@ static PyObject *
_imp_acquire_lock_impl(PyObject *module)
/*[clinic end generated code: output=1aff58cb0ee1b026 input=4a2d4381866d5fdc]*/
{
- _PyImport_AcquireLock();
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ _PyImport_AcquireLock(interp);
Py_RETURN_NONE;
}
@@ -3122,7 +3131,8 @@ static PyObject *
_imp_release_lock_impl(PyObject *module)
/*[clinic end generated code: output=7faab6d0be178b0a input=934fb11516dd778b]*/
{
- if (_PyImport_ReleaseLock() < 0) {
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ if (_PyImport_ReleaseLock(interp) < 0) {
PyErr_SetString(PyExc_RuntimeError,
"not holding the import lock");
return NULL;