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

How to use your own frameworks in Swift playgrounds

With the new beta of Xcode Apple added support for importing custom frameworks to Swift Playgrounds. Let’s explore the way to use them. We’ll start with a new Cocoa Touch Framework project: Let’s call it MyFramework. I have selected... Continue →