]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Add a single Python-wide (!) lock on import. Only one thread at a
authorGuido van Rossum <guido@python.org>
Tue, 3 Mar 1998 22:26:50 +0000 (22:26 +0000)
committerGuido van Rossum <guido@python.org>
Tue, 3 Mar 1998 22:26:50 +0000 (22:26 +0000)
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."

Python/import.c

index 3aa110c35bdabb11fa467f0225cd96c5fde01276..ef8243948815b3471bc135f6027185b24fc82599 100644 (file)
@@ -113,6 +113,61 @@ _PyImport_Fini()
 }
 
 
+/* Locking primitives to prevent parallel imports of the same module
+   in different threads to return with a partially loaded module.
+   These calls are serialized by the global interpreter lock. */
+
+#ifdef WITH_THREAD
+
+#include "thread.h"
+
+static type_lock import_lock = 0;
+static long import_lock_thread = -1;
+static int import_lock_level = 0;
+
+static void
+lock_import()
+{
+       long me = get_thread_ident();
+       if (me == -1)
+               return; /* Too bad */
+       if (import_lock == NULL)
+               import_lock = allocate_lock();
+       if (import_lock_thread == me) {
+               import_lock_level++;
+               return;
+       }
+       if (import_lock_thread != -1 || !acquire_lock(import_lock, 0)) {
+               PyThreadState *tstate = PyEval_SaveThread();
+               acquire_lock(import_lock, 1);
+               PyEval_RestoreThread(tstate);
+       }
+       import_lock_thread = me;
+       import_lock_level = 1;
+}
+
+static void
+unlock_import()
+{
+       long me = get_thread_ident();
+       if (me == -1)
+               return; /* Too bad */
+       if (import_lock_thread != me)
+               Py_FatalError("unlock_import: not holding the import lock");
+       import_lock_level--;
+       if (import_lock_level == 0) {
+               import_lock_thread = -1;
+               release_lock(import_lock);
+       }
+}
+
+#else
+
+#define lock_import()
+#define unlock_import()
+
+#endif
+
 /* Helper for sys */
 
 PyObject *
@@ -1259,14 +1314,8 @@ static PyObject * import_submodule Py_PROTO((PyObject *mod,
 
 /* The Magnum Opus of dotted-name import :-) */
 
-/* XXX TO DO:
-   - Remember misses in package directories so package submodules
-     that all import the same toplevel module don't keep hitting on the
-     package directory first
-*/
-
-PyObject *
-PyImport_ImportModuleEx(name, globals, locals, fromlist)
+static PyObject *
+import_module_ex(name, globals, locals, fromlist)
        char *name;
        PyObject *globals;
        PyObject *locals;
@@ -1315,6 +1364,20 @@ PyImport_ImportModuleEx(name, globals, locals, fromlist)
        return tail;
 }
 
+PyObject *
+PyImport_ImportModuleEx(name, globals, locals, fromlist)
+       char *name;
+       PyObject *globals;
+       PyObject *locals;
+       PyObject *fromlist;
+{
+       PyObject *result;
+       lock_import();
+       result = import_module_ex(name, globals, lock_import, fromlist);
+       unlock_import();
+       return result;
+}
+
 static PyObject *
 get_parent(globals, buf, p_buflen)
        PyObject *globals;