The signals and threads flying circus

2009-01-31

Disclaimer : if you don’t program in python and don’t know what a thread is, you probably want to skip this post as this is highly technically geekish loghorea which is not funny or even understandable in a remotely perverse way. Knowing some Flying Circus episodes might also help.

Say that you have two thread in your python application. One is « TV_speaker » and the other is « knight ». Those two threads live in parallel their own live and all is good. But sometimes you want the « TV_speaker » to stop. In order to do that, you will ask the knight to throw a chicken at him. And the first thing that comes to mind is using a « signal ».

You cant to call knight.send(« chicken ») and that, on the other side, tv_speaker.connect(« chicken ») handles things correctly.

Problem : using google, you find that, since 2.6, the python documentation is barely readable and unusable.

the python documentation

Hopefully, you find your way back to the good old and well organized 2.5 documentation only to discover that those signals are UNIX signals and have nothing in common with signals you can use in a GTK application.

2.5 documentation

Worse : those signals don’t work with threads !

You seat down, cry and wait that a giant foot quickly stops your pain.

Monty Foot

## Nobody expects the GObject introspection !

There’s hope : GObject signals are just what you want. It’s really simple : your signal sender (the knight) should be a subclass of gobject.GObject and that’s nearly all. Well, you also have to define your signal with some obscure GObject terminology but copy/paste works well.

<strong>gsignals</strong> = { 'chicken': (gobject.SIGNAL_RUN_FIRST,\

(str,)) }

gobject.GObject.<strong>init</strong>(self)

a = None

class tv_speaker :

self.emiter = emiter

print "receiving the chicken on the head : my brain hurt"

print "And now for something completely %s" %param2

k = knight()

k.emit("chicken","different")

As you can see, the « do » function is called for each signal, one after the other. This works even if the knight is in another thread !

Download the code for this example

## Intermediate class

And what if you don’t have a direct reference to the sender ? Your knight is not able to throw the chicken itself. Instead, Chapman will take the chicken from the knight and throw it at the tv_speaker.

All you have to do is to implement the « connect » method in chapman.

def <strong>init</strong>(self,emiter) :

self.emiter.connect(signal,func)

k = knight()

tv = tv_speaker(c)

k.emit("chicken","random")

receiving the chicken on the head : my brain hurt

receiving the chicken on the head : my brain hurt

Download the code for this example

## And now with threads

But what if you want to launch an action as soon as the signal is received without waiting for its result ? That’s when you want to use threads.

Modify the do() function of the tv_speaker :

def reallydo(param) :

time.sleep(2)

t = threading.Thread(target=reallydo,args=[param2])

Now, the output is the following, illustrating that signals are handled in parallel :

receiving the chicken on the head : my brain hurt

And now for something completely different

gobject.idle_add(reallydo,param2)

gobject.threads_init()

def do(self,param1,param2) :

print "receiving the chicken on the head : my brain hurt"

print "And now for something completely %s" %param

if self.lock.acquire(False) :

t.start()

print "We don't need you today"

def do(self,param1,param2) :

self.main_lock.acquire()

print "receiving the chicken on the head : my brain hurt"

print "And now for something completely %s" %param

if self.lock_lock.acquire(False) :

t.start()

print "We don't need you today"