key list data structure in the thread startup path.
This change is a companion to r60148 which already successfully dealt with a
similar issue on thread shutdown.
In particular this loop has been observed happening from this call path:
#0 in find_key ()
#1 in PyThread_set_key_value ()
#2 in _PyGILState_NoteThreadState ()
#3 in PyThreadState_New ()
#4 in t_bootstrap ()
#5 in pthread_start_thread ()
I don't know how this happens but it does, *very* rarely. On more than
one hardware platform. I have not been able to reproduce it manually.
(A flaky mutex implementation on the system in question is one hypothesis).
As with r60148, the spinning we managed to observe in the wild was due to a
single list element pointing back upon itself.
PyThreadState_Delete() to avoid an infinite loop when the tstate list
is messed up and has somehow becomes circular and does not contain the
current thread.
I don't know how this happens but it does, *very* rarely. On more than
one hardware platform. I have not been able to reproduce it manually.
Attaching to a process where its happening: it has always been in an
infinite loop over a single element tstate list that is not the tstate
we're looking to delete. It has been in t_bootstrap()'s call to
PyThreadState_DeleteCurrent() as a pthread is exiting.
Building with HP's cc on HP-UX turned up a couple of problems.
_PyGILState_NoteThreadState was declared as static inconsistently.
Make it static as it's not necessary outside of this module.
Some tests failed because errno was reset to 0. (I think the tests
that failed were at least: test_fcntl and test_mailbox).
Ensure that errno doesn't change after a call to Py_END_ALLOW_THREADS.
This only affected debug builds.
PyThreadState_SetAsyncExc(): internal correctness changes wrt
refcount safety and deadlock avoidance. Also added a basic test
case (relying on ctypes) and repaired the docs.
Moved the code for _PyThread_CurrentFrames() up, so it's no longer
in a huge "#ifdef WITH_THREAD" block (I didn't realize it /was/ in
one).
Changed test_sys's test_current_frames() so it passes with or without
thread supported compiled in.
Note that test_sys fails when Python is compiled without threads,
but for an unrelated reason (the old test_exit() fails with an
indirect ImportError on the `thread` module). There are also
other unrelated compilation failures without threads, in extension
modules (like ctypes); at least the core compiles again.
Do we really support --without-threads? If so, there are several
problems remaining.
PyThreadState_Delete(): if the auto-GIL-state machinery knows about
the thread state, forget it (since the thread state is being deleted,
continuing to remember it can't help, but can hurt if another thread
happens to get created with the same thread id).
I'll backport to 2.4 next.
PyGILState_Ensure(): The fix in 2.4a3 for bug 1010677 reintroduced thread
shutdown race bug 225673. Repaired by (once again) ensuring the GIL is
held whenever deleting a thread state.
Alas, there's no useful test case for this shy bug. Four years ago, only
Guido could provoke it, on his box, and today only Armin can provoke it
on his box. I've never been able to provoke it (but not for lack of
trying!).
This is a critical fix for 2.3.5 too, since the fix for 1010677 got
backported there already and so also reintroduced 225673. I don't intend to
backport this fix. For whoever (if anyone) does, there are other thread
fixes in 2.4 that need backporting too, and I bet they need to happen first
for this patch to apply cleanly.
thread's id can't get duplicated, because (of course!) the current thread
is still running. The code should work either way, but reverting the
gratuitous change should make backporting easier, and gets the bad
reasoning out of 2.35's new comments.
can fail, check its return value, and die if it does fail.
_PyGILState_Init(): Assert that the thread doesn't already have an
association for autoTLSkey. If it does, PyThread_set_key_value() will
ignore the attempt to (re)set the association, which the code clearly
doesn't want.
A new API (only accessible from C) to interrupt a thread by sending it
an exception. This is not always effective, but might help some people.
Requested by Just van Rossum and Alex Martelli. It is intentional
that you have to write your own C extension to call it from Python.
Docs will have to wait.
even farther down, to just before the call to
_PyObject_DebugMallocStats(). This required the following changes:
- pystate.c, PyThreadState_GetDict(): changed not to raise an
exception or issue a fatal error when no current thread state is
available, but simply return NULL without raising an exception
(ever).
- object.c, Py_ReprEnter(): when PyThreadState_GetDict() returns NULL,
don't raise an exception but return 0. This means that when
printing a container that's recursive, printing will go on and on
and on. But that shouldn't happen in the case we care about (see
first bullet).
- Updated Misc/NEWS and Doc/api/init.tex to reflect changes to
PyThreadState_GetDict() definition.
variables to store internal data. As a result, any atempts to use the
unicode system with multiple active interpreters, or successive
interpreter executions, would fail.
Now that information is stored into members of the PyInterpreterState
structure.
path (with no profile/trace function) through eval_code2() and
eval_frame() avoids several checks.
In the common cases of calls, returns, and exception propogation,
eval_code2() and eval_frame() used to test two values in the
thread-state: the profiling function and the tracing function. With
this change, a flag is set in the thread-state if either of these is
active, allowing a single check to suffice when both are NULL. This
also simplifies the code needed when either function is in use but is
already active (to avoid profiling/tracing the profiler/tracer); the
flag is set to 0 when the profile/trace code is entered, allowing the
same check to suffice for "already in the tracer" for call/return/
exception events.
Python interpreter.
This change adds two new C-level APIs: PyEval_SetProfile() and
PyEval_SetTrace(). These can be used to install profile and trace
functions implemented in C, which can operate at much higher speeds
than Python-based functions. The overhead for calling a C-based
profile function is a very small fraction of a percent of the overhead
involved in calling a Python-based function.
The machinery required to call a Python-based profile or trace
function been moved to sysmodule.c, where sys.setprofile() and
sys.setprofile() simply become users of the new interface.
As a side effect, SF bug #436058 is fixed; there is no longer a
_PyTrace_Init() function to declare.
can cause it to get called by multiple threads simultaneously.
Ditto for PyInterpreterState_Delete.
Of the former, the docs say "The interpreter lock need not be held, but may
be held if it is necessary to serialize calls to this function". This
kinda implies it both is and isn't thread-safe.
Of the latter, the docs merely say "The interpreter lock need not be
held.", and the clause about serializing is absent.
I expect it was *believed* these are both thread-safe, and the bit about
serializing via the global lock was meant as a permission rather than a
caution.
I also expect we've never seen a problem here because the Python core
(prior to the _PyPclose fix) only calls these functions once per run.
The Py_NewInterpreter subsystem exposed by the C API (but not used by
Python itself) also calls them, but that subsystem appears to be very
rarely used.
Whatever, they're both thread-safe now.
We occasionally received reports from people getting "invalid tstate"
crashes (this is a fatal error in PyThreadState_Delete()). Finally
several people were able to reproduce it reliably and Tim Peters
discovered that there is a race condition when multiple threads are
calling this function without holding the global interpreter lock (the
function may be called without holding that).
Solved the race condition by adding a lock around the mutating uses of
interp->tstate_head. Tim and Jonathan Giddy have run tests that make
it likely that this fixes the crashes -- although Tim hasn't heard
from the person who reported the original problem.