I really dislike having to use objects (or names, more precisely) for these micro data structures. Over time I’ve moved more and more away from creating classes for data structures in Python, a ‘best practice’ habit I brought over from C#. It is silly in a dynamically typed language. There’s nothing more clear about:
for entry in chartdata:
for x, y in chartdata:
In fact it’s probably less clear, because there is the totally unhelpful variable name “entry.”
At some point- maybe even three items?- it becomes more clear and self-documenting to use names (that is, dicts instead of tuples), but for the very common cases of simple collections used by nearby code, tuples and automatic unpacking can’t be beat!
A few weeks ago, I posted about Killing Tasklets, a feature of Stackless Python that allows you to abort a tasklet at any point. And it turns out I had a perfect use case for them just last week and things went swimmingly well.
We have a client program that controls the state of 3D objects, and a server program that does the rendering. The client calculates a serialized version of the server’s state (based on the client’s state) and sends it to the server. It does this through a publish/subscribe. The server receives the state and applies it to the current scene, moving objects and the like (of course we have other mechanisms for rebuilding the entire scene, this is just for ‘updating’ the attributes of the current object graph).
This causes a problem when the server takes longer to apply the new state to its scene than it does for the client to calculate it (maybe the client is super fast because it is caching everything). The server lags further and further behind the client. So when the server receives the ‘update’ command, it kicks off and stores the tasklet to do the updating. If another ‘update’ comes in while the previous update’s tasklet is still alive, it kills that tasklet and starts a new one. This way we get as smooth an updating as possible (dropping updates would cause more choppiness). This does require that updates are ‘absolute’ and not relative to other updates, and can be aborted without corrupting the scene.
Killing tasklets turned this into very straightforward code. In fact none of it other than the few lines that handle subscriptions on the server know anything about it at all. This sort of “don’t think about it too much, it just works like you’d expect” promise of tasklet killing is exactly why I like it and exactly what was fulfilled in my use case.
Today at work we had a presentation from the venerable Kristján Valur Jónsson about killing Tasklets in Stackless Python, a technique he and partner-in-crime Matthías Guðmundsson started using for their work on DUST514. As someone who’s done some asynchronous programming, this idea sounded blasphemous. It took a little while to stew but I see the value in it now.
The core idea is really simple. Code invokes the kill method on a tasklet. All kill does is synchronously raise a TaskletExit exception (which inherits from BaseException so as not to be caught by general error handling, same idea as SystemExit) on the tasklet. This bubbles up to the Stackless interpreter, and is caught and swallowed.
There are details of course, but that’s the gist. There are a few reasons I like this so much.
First, it uses standard Python exception handling. It’s really easy to explain and understand, like no question about finally blocks being executed (they are). The fact that the killed code runs and dies as I expect makes this at least something to look at.
Second, it can be synchronous. There’s no ‘kill and spin until it’s not alive’ type of thing. When you tell the tasklet to die, it faithfully obeys. You are Thulsa Doom. You can kill it asynchronously but the fact that synchronous behavior works in such a straightforward way is a big thing.
Third, it actually cancels the tasklet where it is. You cannot replicate this with some sort of cancellation token or flag, which always has some delay if it supports cancellation at all. It not only simplifies things but makes them work totally as desired. So even if you are theologically opposed to Tasklet.kill and prefer other disciplined techniques for writing async code, you can’t argue with the superior results.
In the end, you still need to write good code and maintain discipline about shared state and side effects, but I see not only the value in killing tasklets but the superiority of the choice. I hope Kristján (and also Christian Tismer, the other Christian primarily responsible for Stackless) do a more thorough talk about killing tasklets at some conference.