Tuesday, April 16, 2013

Using libdispatch for Critical Sections and Synchronization

libdispatch is the new foundation of concurrent programming on iOS and OS X. Most people look to libdispatch to asynchronously run blocks of code. However, even in code based on pthreads and NSThreads, you can use dispatch queues for synchronization where you might otherwise use pthread mutex or NSLock objects. Let's look at two contrived examples and talk about the differences. The locking is highlighted in red.

Using an NSRecursiveLock.

- (void) syncLockExample
{    
    for (unsigned i = 0; i < 1000; i++)
    {
        int r = rand();
        
        [_recursiveLock lock];
        
        // add a value if in the lower half of the range.
        if (r <= RAND_MAX/2)
            [_dataArray addObject:@(r)];
        
        // if the value is odd, remove it.
        if ([[_dataArray lastObject] intValue] % 2)
            [_dataArray removeLastObject];
        
        [_recursiveLock unlock];
    }
}

Using a dispatch queue:

- (void) syncQueueExample
{    
    for (unsigned i = 0; i < 1000; i++)
    {
        int r = rand();
        
        dispatch_sync(_dispatchQueue, ^{
            // add a value if in the lower half of the range.
            if (r <= RAND_MAX/2)
                [_dataArray addObject:@(r)];
            
            // if the value is odd, remove it.
            if ([[_dataArray lastObject] intValue] % 2)
                [_dataArray removeLastObject];
        });
    }
}

At first blush, there isn't much difference — hardly worth blogging about. However, these are trivial examples. From a maintenance perspective, the dispatch queue approach seems much better. Nobody can insert a return statement or remove a line of code that results in a lock left in the locked state, likely resulting in a deadlock. Aesthetically, the dispatch queue approach is better because the critical section is a block, a well-defined construct supported by the language (much like @synchronized). The lock version uses no feature of the language to define its scope.

There are some disadvantages to the dispatch queue approach. NSRecursiveLock, which is the NSLock I would normally choose for these scenarios, is forgiving of code in the critical section that itself calls more critical sections that share the same lock. Be warned that if you do the same with dispatch_sync, you will deadlock. However, if you're calling big swatches of code in such a synchronized state, you may want to rethink your locking altogether.

Thursday, April 26, 2012

No WWDC for me this year!

You snooze you loose. Plus I have gigs galore in June. Plus the session videos will probably suffice.

Monday, April 9, 2012

Kodagram

Kodak, a company with vast manufacturing capacity and patents to key photographic technology, and the ability to make pictures look great, is worthless. Instagram, a company with no way to make money and that only knows how to make your photos look like they were take with a broken film camera, is worth a billion.

Oh the irony.

Wednesday, April 4, 2012

Blocks instead of RAII

Many C++ programmers are familiar with the resource-aquisition-is-initialization pattern, where a class's constructor and destructor manage the acquiring and releasing of a resource, typically a lock such a mutex. Before blocks were added to Objective-C, I missed this pattern because I felt it made it so much easier to get things right and keep code maintainable. Blocks provide an equally clean way to factor the acquiring and releasing of a resource into reusable code that achieves the same goal, but rather than create a class, you create a function. 

For example, an OS X IOSurfaceRef must be locked before accessing the data with the CPU. The calls to lock and unlock the surface are simple enough, but why repeat them? Here's a function that does that clearly:

OSStatus CallBlockWithIOSurfaceLockedForReading(IOSurfaceRef surface, OSStatus (^block)(void))
{
    uint32_t seed = 0;
    OSStatus error = IOSurfaceLock(surface, kIOSurfaceLockReadOnly, &seed);
    if (!error)
    {
        error = block();
        
        OSStatus unlockError = IOSurfaceUnlock(surface, kIOSurfaceLockReadOnly, &seed);
        if (!error)
            error = unlockError;
    }
    return error;
}


And a call to that looks like:

CallBlockWithIOSurfaceLockedForReading(surface, ^OSStatus{
    void *bytes = IOSurfaceGetBaseAddress(surface);
    // do something with bytes here.
    return noErr;
});


This pattern ensures that you get consistent behavior wherever in your code you need to read data from an IOSurface, getting the appropriate error values, and always keeping the calls balanced, which is critical.