waffle

What to do When Your MacBook Pro’s Speakers Go “Pop” Loudly in the Middle of Playing Music and That’s The Only Sound It Will Continue to Make

  1. Zap your PRAM.
  2. Stop laughing, realize I’m serious and actually zap your PRAM.
  3. Wonder how much you paid for this computer again, and if using Time Machine really did transport you back to 1989 when “zap your PRAM” was not only a valid troubleshooting technique but actually often the answer.

Funniest Thing I’ve Heard All Week

It’s a very different thing from what I had originally set it out to be. So I tell the story: it’s like sending your son off to college and he comes back graduating with honor, summa cum laude and a sex change operation. And you have to go: “well, you know, that’s my boy!- I mean, my girl!”… you know, you kinda have to take your pride in their accomplishments… I am the father of Visual Basic, regardless of its gender.

Alan Cooper, the creator of Tripod, the precursor to Visual Basic, in The Microsoft 101: Visual Studio documentary

Grand Central Rethink

I recently mentioned a technique that I’ve taken to when writing code with Grand Central Dispatch, which is to avoid the @synchronized statement used for locking. Even though I just said that blind translation into a new pattern is bad, it occurs to me that almost all uses of @synchronized can be replaced by alternatives in GCD.

What’s wrong with @synchronized today? There’s nothing wrong with the statement as such; it provides useful functionality using brief syntax. Like lock in C#, @synchronized(x) { ... } is essentially pseudo-syntax for this:

@try {
    // magically enter a lock (x) depending on implementation (currently objc_sync_enter)
    ...
} @finally {
    // magically exit the lock
}

If you desperately want the exact semantics this statement provides (recursive lock given arbitrary object), it is a good thing.

However, most of the time that’s not explicitly what you’re after. You’re after either running code once (say, for initializing a singleton just once) or making sure that things of a certain nature (say, logging or database access) only happens sequentially. Mike Ash has you covered on singletons, so I’ll deal with the other scenario.

What’s so bad with @synchronized in that scenario? It’s a perfectly valid way to implement it. But you’re not just dealing with that.

  • @synchronized will block.
  • Unless you want to block on the main thread for a period of time, or write code to carefully control that period of time, you have to create a new thread.
  • If you have to create a new thread, you have to set up an autorelease pool. (Sometimes.)
  • If this is the first thread you are creating, you have to suddenly deal with data concurrency.
  • Threads take time to create and memory to manage. The more you have, the worse the load on the system gets.
  • @synchronized works on a lock object; you have to manage the lifetime, visibility and transportation of that lock object.
  • Instead of doing this inline, if you have to do this in the middle of a method with state, you have to arrange for a single context object (probably a dictionary) to pass to the thread. This adds more opportunity for failure.

Let’s see what you could do otherwise. The alternative I propose is to use a dispatch queue. (You could also use an operation queue, which might be the better alternative; I’m going to stick to GCD.) You will have to do exactly three things:

  1. Initialize the queue (as a serial, non-concurrent queue).
  2. Push any jobs to the queue.
  3. Appropriately dispose of the queue. (Unless you run under GC, in which case there’s no step three. I apologize profusely for that phrase.)

This still seems like some work until both the magic of GCD and blocks enter the equation. Thanks to their features, you can:

  • Give the queue a sensible name.
  • Build up your state right before the job (given as a block) and call it without parameters; thanks to blocks being closures, you have access to the variables from “outside” and can use them.
  • Opt to run the job asynchronously or synchronously; in other words, choose whether to wait for the completion or not.
  • See the exact code in context.
  • Use other aspects of GCD if you’d like; like forwarding the jobs to a different queue with target queues.
  • Measure the load of the queue itself and separate jobs/blocks in particular with high granularity in Instruments (although the block labeling leaves something to be desired).
  • Mess around with exactly one object that you need to arrange transportation and lifetime for: the queue itself. The block is an object, but you declare it inline, as you do with regards to setting up its state. You can’t even pass parameters to blocks with the kind of signature that you can push onto the queue; they’re for all intents of purposes just a bunch of lines running in the original scope but on a different thread.

And beyond that, the code you write is much more in step with the task you’re trying to accomplish. The important detail that requires you to use a special technique is that this needs to run on its own and serialized; that’s pretty much all you explicitly declare with GCD, that you submit something to a queue. This means that if GCD comes up with a better way to do that, you get it for free. (And in fact its way of doing that right now is already plenty faster.) It also means that you can still mess up, say, data concurrency in that space because it does after all run on another thread, but you’re not doomed to manage the minutia around that abstraction and its consequences.

I’ve already come to the conclusion that parallelism demands more from us. A good tool is therefore not only a tool that solves the problem but one that is easy to use and provides a good model. A lock will always work, but progress will come no sooner than we untether ourselves from the mechanism.

Older posts »