The GIL and PyEval_RestoreThread

MRAB python at mrabarnett.plus.com
Tue Sep 26 22:48:15 EDT 2023


On 2023-09-26 14:20, Peter Ebden via Python-list wrote:
> Hi all,
> 
> I've been working on embedding Python and have an interesting case around
> locking with PyEval_RestoreThread which wasn't quite doing what I expect,
> hoping someone can explain what I should expect here.
> 
> I have a little example (I'm running this in parallel from two different
> threads; I have some more C code for that but I don't think it's super
> interesting):
> 
> void run_python(PyThreadState* thread) {
>    LOG("Restoring thread %p...", thread);
>    PyEval_RestoreThread(thread);
>    LOG("Restored thread %p", thread);
>    PyRun_SimpleString("import time; print('sleeping'); time.sleep(3.0)");
>    LOG("Saving thread...");
>    PyThreadState* saved_thread = PyEval_SaveThread();
>    LOG("Saved thread %p", saved_thread);
> }
> 
> This produces output like
> 11:46:48.110058893: Restoring thread 0xabc480...
> 11:46:48.110121656: Restored thread 0xabc480
> 11:46:48.110166060: Restoring thread 0xabc480...
> sleeping
> 11:46:48.110464194: Restored thread 0xabc480
> sleeping
> 11:46:51.111307541: Saving thread...
> 11:46:51.111361075: Saved thread 0xabc480
> 11:46:51.113116633: Saving thread...
> 11:46:51.113177605: Saved thread 0xabc480
> 
> The thing that surprises me is that both threads seem to be able to pass
> PyEval_RestoreThread before either reaches the corresponding
> PyEval_SaveThread call, which I wasn't expecting to happen; I assumed that
> since RestoreThread acquires the GIL, that thread state would remain locked
> until it's released.
> 
> I understand that the system occasionally switches threads, which I guess
> might well happen with that time.sleep() call, but I wasn't expecting the
> same thread to become usable somewhere else. Maybe I am just confusing
> things by approaching the same Python thread from multiple OS threads
> concurrently and should be managing my own locking around that?
> 
Storing the result of PyEval_SaveThread in a local variable looks wrong 
to me.

In the source for the regex module, I release the GIL with 
PyEval_SaveThread and save its result. Then, when I want to claim the 
GIL, I pass that saved value to PyEval_RestoreThread.

You seem to be releasing the GIL and discarding the result, so which 
thread are you resuming when you call PyEval_RestoreThread?

It looks like you're resuming the same thread twice. As it's already 
resumed the second time, no wonder it's not blocking!



More information about the Python-list mailing list