FAQ

Svein Seldal wrote:
Hi

A couple of mulithreaded C API python questions:

I) The PyGILState_Ensure() simply ensures python api call ability, it
doesnt actually lock the GIL, right?

PyGILState_STATE gstate;
gstate = PyGILState_Ensure();

II) Am I required to lock the GIL prior to running any python functions
(in a threaded app)?

PyThreadState *pts = PyGILState_GetThisThreadState();
PyEval_AcquireThread(pts);
PyObject_CallObject(...);

III) Shouldn't the GIL be released after 100 bytecodes or so to enable
other waiting threads to get the GIL?

I'm unable to get access to python as long as another python call is
executing. The PyEval_AcquireThread() call blocks until the first call
returns. I was hoping that the python system itself would release the
GIL after some execution, but it itsnt.

I am dependent upon the ability to have to threads executing in python
land at the same time. How can this be done?
PyGILState_Ensure/Release guarantees to establish the GIL - even if it was already held (useful if you deal with complex call ordering/dependencies)


PyObject_CallObject(...) calls Python code. Thus the interpreter will switch and do as usuall during that.


In your/C/system... code you are responsible to release the GIL or not to enable other Python threads (and prevent from deadlocks)
Usually you'd do this
* if you do time consuming C/system stuff
* or if the code can possibly renter Python through the system (e.g. when you call a Windows function which itself can create Windows messages to be routed back into Python message handlers)


-robert

Search Discussions

  • Farshid Lashkari at Nov 9, 2006 at 5:32 pm

    Svein Seldal wrote:
    I'm unable to get access to python as long as another python call is
    executing. The PyEval_AcquireThread() call blocks until the first call
    returns. I was hoping that the python system itself would release the
    GIL after some execution, but it itsnt.

    I am dependent upon the ability to have to threads executing in python
    land at the same time. How can this be done?
    Are you creating your threads through python or through C code? If you
    are only creating it through C code, then make sure you initialize the
    GIL with the following call at the beginning of your application:

    PyEval_InitThreads()

    From my experience, calling PyGILState_Ensure() was enough. I didn't
    need to call any extra threading functions. But make sure that every
    call to PyGILState_Ensure() is matched with a call to PyGILState_Release()

    -Farshid
  • Robert at Nov 9, 2006 at 7:11 pm

    Svein Seldal wrote:
    robert wrote:
    PyGILState_Ensure/Release guarantees to establish the GIL - even if it
    was already held (useful if you deal with complex call
    ordering/dependencies)
    I understand this to mean that I dont need to explicitly lock the GIL
    (with PyEval_AcquireLock() or PyEval_AcquireThread()).

    Well I cant figure out the PyGILState_Ensure() because if I only use
    this function to establish the GIL, when calling python, python will
    shortly after crash with "Fatal Python error: ceval: tstate mix-up".
    This happens consistently when the main app and the extra thread has
    called python and both are busy executing python code.
    Do did't tell enough to see what you want to do on the big picture.

    usually you create a thread through Python.
    If you boot Python at all from C you have to do
    PyEval_InitThreads
    or maybe more simple use the high level layer PyRun_...

    If you boot a new Python thread manually or a separated interpreter (with separated module state) from outside
    the you have to also do:

    (PyInterpreterState* PyInterpreterState_New() )
    PyThreadState* PyThreadState_New( PyInterpreterState *interp)
    void PyEval_AcquireThread( PyThreadState *tstate)


    If you are already in a Python thread but don't for some reason not know the current thread state:
    PyThreadState* PyThreadState_Get( )
    but usually you release with PyThreadState* PyEval_SaveThread( ) / Py_BEGIN_ALLOW_THREADS


    PyObject_CallObject(...) calls Python code. Thus the interpreter
    will switch and do as usuall during that.
    BTW What do you mean by switch?
    the automatic scheduling every sys.getcheckinterval() etc.. you mentioned
    You'd only have to take care for that, if your C-code does things (e.g. calling python basic funcs) for a long time and doesn't release the lock with Py_BEGIN_ALLOW_THREADS ...
    The main problem is that not done this way, it's the other way around.
    My main C app will call a python function which will be a lengthy time
    consuming process.

    The main C app will call python and it will never return from
    PyObject_CallObject(...), while the extra thread should call a py
    function to deliver occational messages into C.
    Maybe simply boot Python in the main thread (Py_Initialize( )) and run off Python possibly as simple as PyRun_String
    and let Python do thread.start_new(...)
    You'd not have to worry much about states.

    ( and later you'd probably even more simple do "python myapp.py" and expose your C app as extension module :-) )


    -robert
  • Robert at Nov 16, 2006 at 3:05 pm
    Svein Seldal wrote:

    You seem to use the functions in very rude manner - wanting to force the GIL around at lowest level.
    Better forget the word GIL and think about acquiring and releasing Threads.
    For each thread wanting to execute Python stuff there has to be a thread state (ts). And then you have to enter/leave for executing Python by using the thread state.


    PyEval_InitThreads enters Python (incl. the GIL) first time.
    To let Python free in main thread do
    ts=PyEval_SaveThread() // or more easy: Py_BEGIN_ALLOW_THREADS

    to reenter Python do simply:

    PyEval_RestoreThread(ts) // or: Py_END_ALLOW_THREADS

    Forget all the low level PyGIL... functions.

    when you really want another c thread to enter Python first time with no thread state existing through Python itsel (better make the thread in Python with thread.start_new ?), then you have to do once

    ts_cthread = PyThreadState_New(interp)

    then enter Python in this thread:

    PyEval_AcquireThread( ts_cthread )

    to leave again:

    PyEval_ReleaseThread( ts_cthread )


    If you just loop the call_py_function_send() mainly in this thread you probably better create the thread at all in Python and make the loop there. You probably stick too much to C-level at any price :-) Probably you just do a PyRun_xxxx in main thread and then everything else in Python, and expose C-parts for the thread-loop to Python as function (in other_py_inits) - where in the c-function you probably have the usual Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS bracket during time consuming/system/reenter-endangered message stuff.


    Robert




    I think I've found the bug, but I need to confirm this behavior.

    My findings is that if you use PyEval_InitThreads(), it is crucial to
    release the GIL with PyEval_ReleaseThread() afterwards.

    The API docs states that you can release the GIL with
    PyEval_ReleaseLock() or PyEval_ReleaseThread() AFAICS.
    http://docs.python.org/api/threads.html under docs of void
    PyEval_InitThreads().

    However, if I do use PyEval_ReleaseLock() it will crash shortly after
    with "Fatal Python error: ceval: tstate mix-up" in a multithreaded C
    environment.

    If I use PyEval_ReleaseThread() to release the GIL, my app seems stable.
    I release the GIL like this:

    PyThreadState *pts = PyGILState_GetThisThreadState();
    PyEval_ReleaseThread(pts);

    Now, is this a feature or expected behavior in python (i.e. unclear API
    documentation), or is this a bug in python itself?


    Regards,
    Svein


    PS:

    For reference I did something like this in pseudo-code:

    Py_Initialize();
    PyEval_InitThreads();

    other_py_inits(); // Load py modules, etc.

    PyEval_ReleaseLock(); // <-- MAKES THE APP UNSTABLE

    create_c_thread();

    PyGILState_STATE gstate;
    gstate = PyGILState_Ensure();

    call_py_function_main(); // Py main() wont return

    PyGILState_Release(gstate);


    And the "main" of the C thread function looks like this:
    while(1)
    {
    PyGILState_STATE gstate;
    gstate = PyGILState_Ensure();

    call_py_function_send();

    PyGILState_Release(gstate);
    }
  • Robert at Nov 17, 2006 at 12:01 pm

    Svein Seldal wrote:

    A real beauty about the PyGILState_Ensure() is the fact that if it's
    called from a thread that is (yet) unknown to python, it will actually
    create the thread state object for you (with PyThreadState_New) and
    acquire the lock. Hence, I dont have to worry about the process of
    swapping thread states with PyEval_xxxThread() functions. I ONLY need to
    use PyGILState_Ensure() prior to any py-ops.
    Interesting - the PyGILState_Ensure was new to py2.3. I didn't notice so far, that it will even auto-create a fresh tread state (from default InterpreterState only). From the docs its not that clear.

    The confusion in usage is that one has to do

    Py_Initialize()
    PyEval_InitThreads()
    ts=PyEval_SaveThread()

    in order to start clean for further pure "meta-high-level" PyGILState_Ensure/Release.

    They obviously meant PyGILState_Ensure to make live easier, but the discussion shows, it adds additional worries probably forcing each user into investigating the Python C-code :-)

    There should probably a extra init function for this meta-high-level usage like ..

    PyGILState_PyInitThreads() // which would not pre-acquire the lock! - as anyone expects to enter then with PyGILState_Ensure

    . and these 3 Functions grouped together in the docs for an easy-to-use meta-high-level thread state API.

    The current thread state API doc, as you read it from top to bottom now, is in fact totally confusing for anyone who didn't develop Python himself :-)


    Robert
  • Hendrik van Rooyen at Nov 10, 2006 at 5:33 am
    "Svein Seldal" <"svein at seldal dot com">wrote:
    8<---------------------------------------------------
    I am dependent upon the ability to have to threads executing in python
    land at the same time. How can this be done?
    call time.sleep(0.001) in each, as well as the main thread, to politely give the
    rest a chance....

    Fiddle with the sleep times in your setup to get the best performance - and
    don't be misled into thinking that faster is better - its not always true -
    there comes a point where the overhead of swapping a thread consumes the total
    resource of the process, and no work gets done.

    - HTH

    - Hendrik

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
grouppython-list @
categoriespython
postedNov 9, '06 at 1:22p
activeNov 17, '06 at 12:01p
posts6
users3
websitepython.org

People

Translate

site design / logo © 2022 Grokbase