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.

 
4
Kudos
 
4
Kudos

Now read this

JSON mapping and reader monad [DRAFT]

Functional JSON mapping became a hot theme recently. Clear declarative syntax backed by cryptic concepts of monads and applicative functors has exploded many heads. I’d like to talk about a little refinement of our mapping pattern to... Continue →