<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd">
<article>
    <articleinfo>
        <title>GTasklets: Python generator based pseudo-threads for
        PyGTK</title>

        <author>
            <firstname>Gustavo</firstname>

            <othername>J. A. M.</othername>

            <surname>Carneiro</surname>

            <email>gjc at inescporto dot pt</email>
          
            <email>gjcarneiro at gmail dot com</email>
        </author>
    </articleinfo>

    <section>
      <title>Project moved</title>
      
      <warning>
        The gtasklets module has now been merged into <ulink
        url="http://www.async.com.br/projects/kiwi/">kiwi</ulink>.
        Check the new documentation <ulink
        url="http://www.async.com.br/projects/kiwi/api/kiwi.tasklet.html">here</ulink>.
        </warning>

    </section>

    <section>
        <title>Motivation</title>

        <para>Threads are evil.</para>

        <para>Oh, need I say more?... Well, lots of nasty things happen with
        threads, such as deadlocks and race conditions. OK? Are you happy
        now?</para>

        <para>The typical PyGTK solution to avoid threads is to use callbacks.
        It is a fine solution, the only problem being that sometimes you end
        up with too many callbacks, one after the other, and that hurts the
        readability of a program, just as a bunch of <code>goto</code>'s in C
        make what we usually call a <emphasis>sphagetti program</emphasis>.
        Not to mention the difficulty for some newbies to think in terms of
        callbacks; one has to do some mental gymnastics to split a long
        operation in a series of steps, put each step in a different function,
        and figure out which data needs to be passed from one function to the
        next one. Not to mention handling errors and disconnecting
        callbacks...</para>

        <para>Since Python 2.2, a new language feature has been available
        called <firstterm>generator</firstterm><xref linkend="pep255" />.
        Generators are objects that are defined as functions, and when called
        produce iterators that return values defined by the body of the
        function, specifically <code>yield </code>statements.</para>

        <para>The neat thing about generators are not the iterators themselves
        but the fact that a function's state is completely frozen and restored
        between one call to the iterator's <code>next()</code> and the
        following one. This is just the right thing we need to turn our
        functions into pseudo-threads, without actually incurring the wrath of
        the gods of software.</para>
    </section>

    <section>
        <title>Solution</title>

        <para>The module gtasklet has support for what is called<emphasis>
        tasklets</emphasis>, which is like a thread, except that:</para>

        <itemizedlist>
            <listitem>
                <para>Only one tasklet can run execute at any given
                time;</para>
            </listitem>

            <listitem>
                <para>Tasklets should give away control from time to time,
                specifying which events must happen for it to regain
                control;</para>
            </listitem>
        </itemizedlist>

        <para>With this model, the programmer is given full control of
        execution. Tasklets decide at which points preemption can occur, and
        why. With threads, preemption can occur any point/time, causing major
        headaches.</para>

        <para>Giving control back to the "system" is done inside the tasklet
        function using the <code>yield</code> keyword, with the
        <firstterm>wait conditions </firstterm>as arguments. A wait condition
        is an object that indicates an event a tasklet is waiting for in order
        to receive back control. Any number of wait conditions can be
        specified as an argument to <code>yield</code>. After
        <code>yield</code>ing, the tasklet should call
        <code>get_event()</code> to find out which event actually took place
        that caused it to regain control. Unlike Twisted, there's no
        distinction between normal and error conditions; it's up to the
        programmer to make that distinction. For example, it could be that an
        object signal is a normal event, but a timeout an error one.</para>

        <para>Here's an example of a Tasklet, based on the <ulink
        url="test-tasklet.py">demo program</ulink>.</para>

        <programlisting id="tasklet-example" lang="Python">def counter(task, dialog):
    timeout = gtasklet.WaitForTimeout(1000)
    msgwait = gtasklet.WaitForMessages(accept='quit')
    for i in xrange(10, 0, -1):
        dialog.format_secondary_markup("Time left: &lt;b&gt;%i&lt;/b&gt; seconds" % i)
        yield timeout, msgwait
        ev = self.get_event()
        if isinstance(ev, gtasklet.Message) and ev.name == 'quit':
            return
        elif ev is timeout:
            pass
        else:
            raise AssertionError
</programlisting>

        <para>In the above code, the tasklet counts down from 10 to 1
        and changes a message dialog's text. Between each step, it
        yields control for one second. After each yield, it checks for
        the received event.  The <code>WaitForMessages</code> instance
        passed to yield declares that we are also interested in
        receiving messages with name 'quit'. It is also possible to
        use <code>WaitForMessages</code> to indicate messages that we
        want to defer (queue) or discard (drop). The message 'quit' is
        used in this tasklet with the meaning that it should stop. To
        launch the above tasklet, one would do (including the main
        loop):</para>

        <programlisting>if __name__ == '__main__':
    dialog = gtk.MessageDialog(...)
    gtasklet.Tasklet(counter, dialog)
    gtk.main()</programlisting>

    </section>

    <section>
        <title>Related Work</title>

        <para>Twisted <code>Deferred</code>s <xref
        linkend="twisted-deferreds" /> are like pygtk callbacks, except that
        it always has two parallel callbacks, one for the normal flow of
        control, the other one for reporting errors. So nothing really new (in
        my opinion) relative to pygtk, just a big dependency for pygtk
        programs.</para>

        <para>Arjan Molenaar submitted <xref linkend="molenaar" /> a very
        useful recipe to the Python Cookbook, which uses generators as idle
        functions. Unfortunately, it is limited to idle functions, and doesn't
        other event sources such as timeouts I/O events.</para>

        <para>The PEAK framework <xref linkend="peak" /> does exactly what is
        right. The only problem is that this is a huge dependency and is
        over-engineered (interfaces and protocols, anyone?). Anyway, I only
        found out about it after writing gtasklets :P</para>

        <para>Finally, Stackless Python <xref linkend="stackless" /> already
        has support for what they call <firstterm>greenlets</firstterm>, which
        is very similar to the tasklets described here.</para>
    </section>

    <bibliography xreflabel="References">
        <biblioentry id="pep255">
            <title>Simple Generators</title>

            <abbrev><ulink
            url="http://www.python.org/peps/pep-0255.html">PEP255</ulink></abbrev>
        </biblioentry>

        <biblioentry id="twisted-deferreds">
            <title><ulink
            url="http://twistedmatrix.com/documents/current/howto/defer">Twisted
            Documentation: Using Deferreds</ulink></title>
        </biblioentry>

        <biblioentry id="molenaar">
            <title><ulink
            url="http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/327082">Pseudo
            threads with generators and PyGTK/gnome-python</ulink></title>

            <author>
                <surname>Arjan Molenaar</surname>
            </author>
        </biblioentry>

        <biblioentry id="peak">
            <title>PEAK -- <ulink url="http://peak.telecommunity.com/">The
            Python Enterprise Application Kit</ulink></title>
        </biblioentry>

        <biblioentry id="stackless">
            <title><ulink url="http://www.stackless.com/">Stackless
            Python</ulink></title>
        </biblioentry>
    </bibliography>
</article>