Search code examples
concurrencyswiftatomic

How to use OSAtomicCompareAndSwapPtrBarrier from Swift?


I have already asked this question in the official Apple developer forums, but unfortunately did not get an answer. I am currently trying to build some simple concurrent data structures in Swift and couldn't find a way to call OSAtomicCompareAndSwapPtrBarrier from Swift code. The signature is

OSAtomicCompareAndSwapPtrBarrier(
  __oldValue: UnsafePointer<()>,
  __newValue: UnsafePointer<()>,
  __theValue: UnsafePointer<UnsafePointer<()>>)

which in C would be

OSAtomicCompareAndSwapPtrBarrier(
  void *__oldValue,
  void *__newValue,
  void * volatile *__theValue)

I couldn't find a way to create an UnsafePointer<UnsafePointer<()>>. Just a short code example:

class Stack<A> {
  var __head: Node<A>

  init() {
    __head = Node()
  }

  func push(elem: A) {
    var newNode = Node<A>()
    newNode.elem = elem
    var currentHead: Node<A>
    do {
      currentHead = __head
      newNode.next = currentHead
    } while(!OSAtomicCompareAndSwapPtrBarrier(&currentHead, &newNode, &__head))
  }
}

class Node<A> {
  var elem: A?
  var next: Node<A>?
}

Using &__headobviously does not work, as it only creates an UnsafePointer<()>. So how can I create the UnsafePointer<UnsafePointer<()>> here?

edit:

In C I can use it as follows (I don't normally use C, so this code may be terribly wrong):

#include <stdio.h>
#include <stdlib.h>
#include <libkern/OSAtomic.h>

typedef struct {
  int bar;
} foo;

int main(int argc, const char * argv[]) {
  // insert code here...
  foo * volatile f = malloc(sizeof(foo));
  foo *n = malloc(sizeof(foo));

  f->bar = 1;
  foo *old = f;
  n->bar = 3;

  OSAtomicCompareAndSwapPtrBarrier(old, n, &f);

  free(old);
  printf("%d\n", f->bar);
  free(f);

  return 0;
}

Solution

  • This is an example how to use OSAtomicCompareAndSwapPtrBarrier or OSAtomicCompareAndSwapPtr in Swift:

    public final class AtomicReference<T : AnyObject> {
        private var _value : T
        public var value : T {
            get {
                OSMemoryBarrier()
                return _value
            }
            set {
                OSMemoryBarrier()
                _value = newValue
            }
        }
    
        private let pointer : UnsafeMutablePointer<UnsafeMutablePointer<Void>> = UnsafeMutablePointer.alloc(1)
        public init(_ value : T) {
            self._value = value
            pointer.memory = UnsafeMutablePointer<Void>(Unmanaged.passUnretained(value).toOpaque())
        }
        deinit {
            pointer.destroy()
        }
    
        public func compareAndSwap(#oldValue : T, newValue: T) -> Bool {
            let ov = Unmanaged.passUnretained(oldValue)
            let nv = Unmanaged.passUnretained(newValue)
    
            if OSAtomicCompareAndSwapPtrBarrier(UnsafeMutablePointer<Void>(ov.toOpaque()), UnsafeMutablePointer<Void>(nv.toOpaque()), pointer) {
                _value = newValue
                return true
            } else {
                return false
            }
        }
    }
    

    This code is a class which works like AtomicReference in Java. It enables you to avoid thread locks in some cases using atomic operations instead. Atomic operations usually provide higher performance in case of high concurrency.

    The compareAndSwap function will change the value only if the previous value is the same as oldValue. For instance:

    class LockArray {
        var value = NSArray()
        let lock = NSLock()
        func addItem(item : Int) {
            lock.lock()
            value = value.arrayByAddingObject(item)
            lock.unlock()
        }
    }
    
    class AtomicArray {
        let reference = AtomicRefence<NSArray>([])
        func addItem(item : Int) {
            while true {
                let oldValue = reference.value
                let newValue = oldValue.arrayByAddingObject(item)
                if reference.compareAndSwap(oldValue: oldValue, newValue: newValue) {
                    break
                }
            }
        }
    }
    

    Both of these classes are thread-safe and do the same thing. However, the second one will work better with high concurrency.