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

Write yourself KVO in Swift

KVO is a major part of Objective-C dynamic nature. Having virtually every property or ivar observable by its key path opens doors to simple bindings or advanced technologies like ReactiveCocoa. But what about Swift? It’s static nature... Continue →