PyGilState_Ensure interrupts python critical sections
billy.omahony at gmail.com
billy.omahony at gmail.com
Thu Nov 15 05:50:58 EST 2007
Many thanks for the clarification. Also good idea to focus on lock
ownership rather that thread activity in your diagram.
To be honest I was not actually experiencing deadlock issues. I had
just deduced (incorrectly) that I might do so if I started using locks
in my py code called from c-land. As I was suffering some horrible
race-condtions that had suddenly appeared in code that had been
perfectly stable for the past 6 months I didn't want to go down that
route given my misconceptions; Even if introducing the locks worked
I'd always feel it was a deadlock waiting to happen.
Misunderstanding arose as for some reason I had decided that python
code called from C did not allow any other python threads to run until
control had returned to C and the gil had been released from C-land.
I.e. once the gil was given to the C thread then python had to wait
until it was given back.
I have to say the docs could be a little more explicit on the
mechanisms involved. They tend to be better at stating what individual
functions do rather that giving overall explanations. Maybe I'll get
around to submitting a chapter on it ;)
On Nov 12, 4:54 pm, "Chris Mellon" <arka... at gmail.com> wrote:
> On Nov 12, 2007 6:56 AM, <billy.omah... at gmail.com> wrote:
>
>
>
> > Hi,
>
> > I have a native windows thread in a c python module which calls into
> > python code and adds an item to a data structure (a home-grown
> > circular buffer). At the same time my main python application is
> > removing items from this data structure.
>
> > Unlike native python containers adding and removing items from the
> > circular buffer is not atomic. So I need to put access to it in a
> > critical section. Using threading.Lock will work for native python
> > threads competing for access. But when access is from a windows thread
> > this will not work. That is because my call to PyGilState_ensure will
> > preempt my native python thread ***even when it is inside the critical
> > section***.
>
> > What is going on looks something like this (I think).
>
> > Py Python Windows Py threading.Lock
> > resource
> > Sched Thread Thread Code | |
> > | | | | |
> > |Go (GIL)# | | | |
> > | # | | | |
> > | # | | | |
> > | #...Doit.....|...........>| | |
> > | # | |. acquire...>| |
> > |<-PyGILState_Ensure--| | | |
> > | ... ... ... ...
> > |Stop #
> > |-------`|
> > |
> > |----Ensure rtns-----># PyObject_ | |
> > | : |CallMethod | | |
> > | : |.(Doit)...> |. acquire...>| DEADLOCK |
> > :
> > :
> > :.how does python thread tell
> > PyScheduler not to give away
> > Gil until we are done with
> > critical section??
>
> > So my question is how in python do I tell the scheduler not to prempt
> > the current python thread. Especially how to tell it not to give the
> > GIL to any calls to PyGILState_ensure until further notice (i.e. until
> > I am outside my critical section?? It sounds like a reasonable request
> > - surely this exists already but I can't find it.
>
> It took me some time to understand the problem you were describing
> because you've got some terms backward - there's no Python scheduler,
> and calling PyGILState_ensure doesn't preempt anything. The Python
> interpreter *releases* the GIL every so often to allow other threads
> looking for it to run, but calling the python GIL functions has no
> effect on preemption.
>
> The problem is that the GIL is being released while your object lock
> is held, a second thread (started from C) acquires the GIL and then
> blocks on the object lock. What you seem to be seeing is that it
> blocking on the object lock is preventing it from releasing the GIL,
> which prevents the python thread from running and releasing the lock.
>
> This shouldn't happen - blocking on a lock releases the GIL, so the
> python thread should run, release the GIL, and eventually your C
> thread should be able to acquire both locks at the same time. Are you
> sure that you're correctly acquiring the GIL in your C code?
>
> The data flow you *should* be seeing should look something like this:
>
> GIL object lock (P=Python, C=C, *=released)
> -------------------
> P P Python holds both locks
> * P Python releases the GIL, inside critical section
> C P C thread acquires the GIL and starts executing Python code
> * P C thread tries to acquire object lock and blocks,
> releasing the GIL
> P P Python thread re-acquires the GIL
> P * Python thread exits critical section and releases
> the object lock
> * * Python thread releases the GIL (can be in any
> order with next state)
> * C The C thread acquires the object lock and blocks on the GIL
> C C C thread acquires the GIL and continues execution.
>
> > One thing that may work (though the documentation does not
> > specifically say so) is using setcheckinterval() to set the check
> > interval to a very large value, acessing my shared structure and then
> > setting the check interval back to the previous value. Provided my
> > access to the shared structure takes less byte codes than what I set
> > checkinterval to I should be okay. However that would be very
> > dependant on the exact fine detail of how the check interval works and
> > may not be compatible with other Python releases
>
> > Maybe someone familiar with the python source code would know for
> > sure?
>
> > I am using Python 2.4.3 on windows XP.
>
> > Thanks for any help/suggestions offered!
> > BR,
> > Billy.
>
> > --
> >http://mail.python.org/mailman/listinfo/python-list
More information about the Python-list
mailing list