A few weeks ago I started writing an asynchronous toolkit for GObject, inspired by NSOperation, CCR, Threading Building Blocks, and mostly, Python Twisted. Its implemented in C and has bindings for Python and Vala. I'll be adding Mono bindings in a minor release or two like I did with rss-glib.
You can get the tarball here.
I've written some documentation that covers how and why you would use the API. While doing so, I discovered how awesome txt2tags is. I highly recommend it for anyone who hasn't used it. (Thanks Dan for pointing it out).
Of course, there is the gtk-doc based API reference as well.
Lets take a quick look at the python bindings out of code succinctness. Of course, you could always just use Twisted in Python, but I've written bindings nonetheless.
import gtask import urllib import gtk import webkit def worker(url): return url, urllib.urlopen(url).read() win = gtk.Window() win.connect('destroy', gtk.main_quit) web = webkit.WebView() win.add(web) win.show_all() task = gtask.Task(worker, 'http://google.com') task.add_callback(lambda (url, data): web.load_html_string(data, url)) gtask.schedule(task) gtk.main()
The default scheduler (which can be overridden, mind you) performs the work on a regular GThreadPool. I hope to add a work stealing scheduler as soon as I complete the revamp. I'd like to pull thread management out of the scheduler so it can concentrate on whats important.
Why does the scheduler need to be tunable? Think about operations that use a certain resource. It might be beneficial to tag the task with an ID so that your scheduler can pin it to a given CPU, thus maximizing potential for a cache hit.
You might think that updating the GUI from the callback isn't safe, as you do not own the GDK thread lock. However, I assure you it is. By default, callbacks and errbacks are performed from the main loop so that you do not need to worry about it. You can disable this with the "main-dispatch" property on the scheduler instance.
You may also return a new task from a callback or errback. This will pause the post-processing chain until that task has finished. At which point the result of the new task will become the new result for the task which yielded it.
I've also added task dependencies so that you may have a task which will not execute prematurely until dependent tasks have been completed. I'll be building some neat helpers around this later to do things such as; Go do these three tasks, let me know when they are done. This is a good idea for web applications as if you need to do multiple database calls, they should be done in parallel.
Most of what I've written here is available in the docs, go check them out.
-- Christian Hergert 2008-11-13
Back to Index