Thread safe variable access, functional way. Part I.

Correctness of the threaded code has always been a concern. The common pattern is guarding some shared state with a serial dispatch queue:

func setCounter(value: Int) {
  dispatch_async(syncQueue) {
    self.counter = value
  }
}

func getCounter() -> Int {
  var result = 0
  dispatch_sync(syncQueue) {
    result = self.counter
  }
  return result
}

This code has a big drawback - nothing prevents developer from accessing counter from outside the syncQueue.

We can easily fix that by wrapping the variable in a container:

class AccessibleOnQueue<T> {
    init(_ value: T) {
        self.value = value
    }

    private var value: T
}

But how do we access that value? We don’t. Instead we provide a way to apply a function to the contained value:

extension AccessibleOnQueue {
    func accessor<U>(f: (inout T) -> U)
                -> AccessibleOnQueue<() -> U> {
        return AccessibleOnQueue<() -> U>({
            return f(&self.value)
        })
    }
}

Note the type of accessor, it returns a function ‘lifted’ into the container type. This function implements some algorithm over a protected variable.

But there still is no way of calling this construction. Let’s introduce one:

func accessAsync<T>(accessor: AccessibleOnQueue<() -> T>,
                    # queue: dispatch_queue_t) {
    dispatch_async(queue) {
        accessor.value()
    }
}

func accessSync<T>(accessor: AccessibleOnQueue<() -> T>,
                   # queue: dispatch_queue_t) -> T {
    var result: T?
    dispatch_sync(queue) {
        result = accessor.value()
    }
    return result!
}

Returning to code in the beginning, this is how it looks now:

func setCounter(value: Int) {
    let setter = self.counter.accessor() {
        (inout counter: Int) -> () in
            counter = value
        }
    accessAsync(setter, queue: syncQueue)
}

func getCounter() -> Int {
    let getter = self.counter.accessor() {
        (inout counter: Int) -> Int in
            return counter
        }
    return accessSync(getter, queue: syncQueue)
}

Not too much longer, but much safer.

That’s the end of part I, in the next post I will demonstrate how to use this construction to compose operations on protected data and why is it called Applicative Functor.

 
3
Kudos
 
3
Kudos

Now read this

Facebook iOS Infrastructure talk

I’d like to share this brilliant talk from Facebook regarding their experience in building Facebook app. One of the most important problems for them was to tame model layer performance. As a part of their migration from HTML to native... Continue →