Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 75acc9c

Browse files
committed
Add a single Python-wide (!) lock on import. Only one thread at a
time can be in PyImport_ImportModuleEx(). Recursive calls from the same thread are okay. Potential problems: - The lock should really be part of the interpreter state rather than global, but that would require modifying more files, and I first want to figure out whether this works at all. - One could argue that the lock should be per module -- however that would be complicated to implement. We would have to have a linked list of locks per module name, *or* invent a new object type to represent a lock, so we can store the locks in the module or in a separate dictionary. Both seem unwarranted. The one situation where this can cause problems is when loading a module takes a long time, e.g. when the module's initialization code interacts with the user -- during that time, no other threads can run. I say, "too bad."
1 parent 7853570 commit 75acc9c

1 file changed

Lines changed: 71 additions & 8 deletions

File tree

Python/import.c

Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,61 @@ _PyImport_Fini()
113113
}
114114

115115

116+
/* Locking primitives to prevent parallel imports of the same module
117+
in different threads to return with a partially loaded module.
118+
These calls are serialized by the global interpreter lock. */
119+
120+
#ifdef WITH_THREAD
121+
122+
#include "thread.h"
123+
124+
static type_lock import_lock = 0;
125+
static long import_lock_thread = -1;
126+
static int import_lock_level = 0;
127+
128+
static void
129+
lock_import()
130+
{
131+
long me = get_thread_ident();
132+
if (me == -1)
133+
return; /* Too bad */
134+
if (import_lock == NULL)
135+
import_lock = allocate_lock();
136+
if (import_lock_thread == me) {
137+
import_lock_level++;
138+
return;
139+
}
140+
if (import_lock_thread != -1 || !acquire_lock(import_lock, 0)) {
141+
PyThreadState *tstate = PyEval_SaveThread();
142+
acquire_lock(import_lock, 1);
143+
PyEval_RestoreThread(tstate);
144+
}
145+
import_lock_thread = me;
146+
import_lock_level = 1;
147+
}
148+
149+
static void
150+
unlock_import()
151+
{
152+
long me = get_thread_ident();
153+
if (me == -1)
154+
return; /* Too bad */
155+
if (import_lock_thread != me)
156+
Py_FatalError("unlock_import: not holding the import lock");
157+
import_lock_level--;
158+
if (import_lock_level == 0) {
159+
import_lock_thread = -1;
160+
release_lock(import_lock);
161+
}
162+
}
163+
164+
#else
165+
166+
#define lock_import()
167+
#define unlock_import()
168+
169+
#endif
170+
116171
/* Helper for sys */
117172

118173
PyObject *
@@ -1259,14 +1314,8 @@ static PyObject * import_submodule Py_PROTO((PyObject *mod,
12591314

12601315
/* The Magnum Opus of dotted-name import :-) */
12611316

1262-
/* XXX TO DO:
1263-
- Remember misses in package directories so package submodules
1264-
that all import the same toplevel module don't keep hitting on the
1265-
package directory first
1266-
*/
1267-
1268-
PyObject *
1269-
PyImport_ImportModuleEx(name, globals, locals, fromlist)
1317+
static PyObject *
1318+
import_module_ex(name, globals, locals, fromlist)
12701319
char *name;
12711320
PyObject *globals;
12721321
PyObject *locals;
@@ -1315,6 +1364,20 @@ PyImport_ImportModuleEx(name, globals, locals, fromlist)
13151364
return tail;
13161365
}
13171366

1367+
PyObject *
1368+
PyImport_ImportModuleEx(name, globals, locals, fromlist)
1369+
char *name;
1370+
PyObject *globals;
1371+
PyObject *locals;
1372+
PyObject *fromlist;
1373+
{
1374+
PyObject *result;
1375+
lock_import();
1376+
result = import_module_ex(name, globals, lock_import, fromlist);
1377+
unlock_import();
1378+
return result;
1379+
}
1380+
13181381
static PyObject *
13191382
get_parent(globals, buf, p_buflen)
13201383
PyObject *globals;

0 commit comments

Comments
 (0)