Extension libraries, spawned threads, and the global interpreter lock 
Author Message
 Extension libraries, spawned threads, and the global interpreter lock

Hi folks,

If I haven't already scared you off with the subject field, please humour me.
I'm still in the beginner to intermediate stages with Python, and I apologise
if the answer to this is in an obvious place.

Basically I'm wrapping an xlib based library that was written by a third party.
I have access to the source. This library spawns two threads to deal with
certain xlib events. The call chain to do this is roughly:

thewrapper() ->

(in C)  wrapper_ext_module_init() ->

(in C)      xlib_stuff_init()

and it's in that third function that two threads are spawned.

Now, from my understanding of the docs, the C extension module function which
I've created operates under a global interpreter lock. But the two threads
spawned would not (as the lock would be cleared when the spawning function
returned).

The problem I'm getting is that C-c when running from the interactive
interpreter is killing python, like so:

reflex$ python
Python 2.1.1+ (#1, Dec 21 2001, 01:01:30)
[GCC 2.95.4 20011006 (Debian prerelease)] on linux2
Type "copyright", "credits" or "license" for more information.

Quote:
>>> import pyosd

>>> # this code runs the extension module which spawns the threads
>>> p = pyosd.osd(pyosd.default_font, "white")

>>> # now we hit C-c
KeyboardInterrupt

KeyboardInterrupt

KeyboardInterrupt

>>> # and again

KeyboardInterrupt

KeyboardInterrupt

KeyboardInterrupt

Quote:
>>> Segmentation fault

I get similar results when not using the readline module.

If, however, I create a file which sleeps in a try: except block, and I catch
the keyboard interrupt exception, these problems don't surface.

So my question is: am I correct in thinking it's a problem with the GIL not
being held in the other threads?

If so, would explicitly grabbing and freeing the lock in the xlib threads solve
the problem?

If so, is there a better way? I'd prefer not to have to delve into their
source.

I hope I've been coherent. If anyone could offer any suggestions, I'd really
appreciate it.

--
Damien Elmes

Usenet replies are fine, please parse my email address if you
want to send directly.



Mon, 21 Jun 2004 10:16:49 GMT  
 Extension libraries, spawned threads, and the global interpreter lock

Quote:

> So my question is: am I correct in thinking it's a problem with the GIL not
> being held in the other threads?

Probably not. Do the other threads ever call back into Python code, or
invoke Python interpreter API? If so, they need to hold the lock while
doing so. If they are completely separate, they are not concerned with
the GIL.

Quote:
> If so, would explicitly grabbing and freeing the lock in the xlib
> threads solve the problem?

At what time would you free it? As long as one of the threads holds
the lock, your main thread will not advance.

More likely, you have a problem with signal handlers. Python installs
a SIGINT handler, to generate the KeyboardInterrupt. This can break
badly if the signal handler is not delivered to a Python thread, or
more specifically, to the main Python thread.

You have a number of choices:

- don't install the SIGINT handler, but live with the default SIGINT
  processing (i.e. process termination)
- block SIGINT in the other threads. How to do this much depends on
  your operating system.
- find out why delivering the signal to another process causes a
  problem (it *may* be that you have to get the GIL inside
  Modules/signalmodule.c:signal_handler; in theory, the current
  code is supposed to deal with this case, but perhaps it breaks
  for some reason, e.g. if getpid() returns the same value in all
  threads).

Regards,
Martin



Mon, 21 Jun 2004 11:16:03 GMT  
 Extension libraries, spawned threads, and the global interpreter lock

Thanks for your feedback, Martin.

More below.


Quote:

> > So my question is: am I correct in thinking it's a problem with the GIL not
> > being held in the other threads?

> Probably not. Do the other threads ever call back into Python code, or
> invoke Python interpreter API? If so, they need to hold the lock while
> doing so. If they are completely separate, they are not concerned with
> the GIL.

They're entirely seperate, and thus, I thought, safe from the GIL. This problem
appears to crop up when multiple threads receive SIGINT at once.

Quote:
> > If so, would explicitly grabbing and freeing the lock in the xlib
> > threads solve the problem?

> At what time would you free it? As long as one of the threads holds
> the lock, your main thread will not advance.

Thought of that while lying in bed last night :-) Silly of me to mention it in
the first place.

Quote:
> More likely, you have a problem with signal handlers. Python installs
> a SIGINT handler, to generate the KeyboardInterrupt. This can break
> badly if the signal handler is not delivered to a Python thread, or
> more specifically, to the main Python thread.

> You have a number of choices:

> - don't install the SIGINT handler, but live with the default SIGINT
>   processing (i.e. process termination)

The problem is that the interactive interpreter installs a sigint handler,
which is sort of rude for a library to remove when it's instantianted. I think
the 'default' behavior for interactive use is installed once per statement
anyway, so if I were to try modify the behavior, it wouldn't change this
problem.

Quote:
> - block SIGINT in the other threads. How to do this much depends on
>   your operating system.

Maybe this is worth looking into. So far I've been able to determine:

* when this class calls the backend stuff, two threads are forked, to a total
of three "processes" in a ps listing.

* sending an INT signal to any one of them yields the correct behavior.

* sending an INT signal to the main program thread, and any of the other two
(but only one of the other two) yields the correct behavior

* extmodulethread1 + extmodulethread2 yields the crash.

This problem is compounded by the behaviour of the interactive interpreter - a
C-c seemingly generates an interrupt signal to each of the threads instead of
just the main one.

This problem also doesn't seem to crop up when catching a keyboard error in my
own code, which leads me to believe the erroneous interaction is with exception
stuff - but as I understand it, that's all been thread-safe for a long time.

This behaviour is really perplexing me :-)

--
Damien Elmes



Mon, 21 Jun 2004 14:30:02 GMT  
 Extension libraries, spawned threads, and the global interpreter lock

Quote:

> The problem is that the interactive interpreter installs a sigint
> handler, which is sort of rude for a library to remove when it's
> instantianted. I think the 'default' behavior for interactive use is
> installed once per statement anyway, so if I were to try modify the
> behavior, it wouldn't change this problem.

If the interactiuve interpreter is involved, you get to fight readline too.

Good luck.

You could try using a build without readline -- painful to use, but it
might just help.

Cheers,
M.



Mon, 21 Jun 2004 19:04:20 GMT  
 Extension libraries, spawned threads, and the global interpreter lock

Quote:


> > The problem is that the interactive interpreter installs a sigint
> > handler, which is sort of rude for a library to remove when it's
> > instantianted. I think the 'default' behavior for interactive use is
> > installed once per statement anyway, so if I were to try modify the
> > behavior, it wouldn't change this problem.

> If the interactiuve interpreter is involved, you get to fight readline too.

> Good luck.

Thanks :-)

Quote:
> You could try using a build without readline -- painful to use, but it
> might just help.

That stopped the segfaults from occuring - instead of three KeyboardInterrupt
messages appearing at once, only one does.

Does it seem likely the problem lies in the behavior of the readline module?
I'm pretty it's just some silly oversight on my behalf, but I can't see why
this is happening, as AFAIK, the signals should only be being directed to the
main thread

I've done

  PyEval_InitThreads();

in the extension module before any threads are forked, but these threads are
created by the pthread library. I have no desire to access python functions
from them, I just figured they may need an interpreter lock when they get a
signal.

I'll keep on hunting. Thanks for your patience.

--
Damien Elmes



Mon, 21 Jun 2004 21:57:32 GMT  
 Extension libraries, spawned threads, and the global interpreter lock

Hi Folks,

Just thought I'd write and offer up the solution to this problem.

In short, the readline module was causing weird behavior when a C-c was
received in the interactive interpreter. Instead of the SIGINT handler being
called once, it appeared to be being called once for each thread. I do not know
if that is a bug or not - a non-readline enabled interpreter seems to function
correctly.

Anyway, the fix was to wrap the call to the threaded library, blocking the INT
signal, so that to library's threads inherited the blocked behavior. This also
meant I didn't have to change the threaded library, which didn't really seem
appropriate here.

The magical code is basically of the form:

  sigset_t newset;

...

  sigemptyset(&newset);
  sigaddset(&newset, SIGINT);

  sigprocmask(SIG_BLOCK, &newset, NULL);
  osd = xosd_init(fontdesc, colour, timeout, osd_pos, offset, shadow);
  sigprocmask(SIG_UNBLOCK, &newset, NULL);

Thanks to those who jumped in and led me in the right direction.

Quote:



> > > The problem is that the interactive interpreter installs a sigint
> > > handler, which is sort of rude for a library to remove when it's
> > > instantianted. I think the 'default' behavior for interactive use is
> > > installed once per statement anyway, so if I were to try modify the
> > > behavior, it wouldn't change this problem.

> > If the interactiuve interpreter is involved, you get to fight readline too.

> > Good luck.

> Thanks :-)

> > You could try using a build without readline -- painful to use, but it
> > might just help.

> That stopped the segfaults from occuring - instead of three KeyboardInterrupt
> messages appearing at once, only one does.

> Does it seem likely the problem lies in the behavior of the readline module?
> I'm pretty it's just some silly oversight on my behalf, but I can't see why
> this is happening, as AFAIK, the signals should only be being directed to the
> main thread

> I've done

>   PyEval_InitThreads();

> in the extension module before any threads are forked, but these threads are
> created by the pthread library. I have no desire to access python functions
> from them, I just figured they may need an interpreter lock when they get a
> signal.

> I'll keep on hunting. Thanks for your patience.

--
Damien Elmes



Sat, 26 Jun 2004 02:27:25 GMT  
 
 [ 7 post ] 

 Relevant Pages 

1. Global interpreter locks, thread states et al...

2. thread state and the global interpreter lock

3. Thread State and the Global Interpreter Lock

4. global threading.Lock not locking correctly?

5. global interpreter lock not working as it should

6. Global Interpreter Lock and Callbacks

7. Global Interpreter Lock

8. Multiple interpreters and the global lock

9. Global Interpreter Lock

10. embedded Python: one thread hangs while trying to get global Python lock

11. thread, threading, mutex modules and non-threading interpreters

12. Thread safe tcl: interpreter per thread?

 

 
Powered by phpBB® Forum Software