Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@maelp
Last active August 29, 2015 14:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save maelp/989a4a136da220805761 to your computer and use it in GitHub Desktop.
Save maelp/989a4a136da220805761 to your computer and use it in GitHub Desktop.
How to lock around a value
// Inspired from <https://www.mikeash.com/pyblog/friday-qa-2015-02-06-locks-thread-safety-and-swift.html>
import UIKit
/*
NOTE:
I’m not sure spin locks are the most efficient way to do this if we have methods which can potentially hold the lock for a long time, or too many threads trying to acquire a lock
A spinlock is a way to implement a lock that tries to minimize overhead in the lock itself. The name comes from how a spinlock implementation "spins" on the lock when it has to wait, repeatedly polling it to see if it's been released. A normal lock will coordinate with the OS to sleep waiting threads, and wake them up when the lock is released. This saves CPU time, but the additional coordination doesn't come for free. By cutting down on that, a spinlock saves time in the case where the lock isn't held, at the expense of less efficient behavior when multiple threads try to take the lock at the same time.
<https://www.mikeash.com/pyblog/friday-qa-2014-06-06-secrets-of-dispatch_once.html>
*/
class Spinlock {
private var _lock: OSSpinLock = OS_SPINLOCK_INIT
func around(code: Void -> Void) {
OSSpinLockLock(&self._lock)
code()
OSSpinLockUnlock(&self._lock)
}
func around<T>(code: Void -> T) -> T {
OSSpinLockLock(&self._lock)
let result = code()
OSSpinLockUnlock(&self._lock)
return result
}
}
class SharedValue<T> {
var value: T
private let lock = Spinlock()
init(value: T) {
self.value = value
}
func get() -> T {
return lock.around({ return self.value })
}
func set(value: T) {
lock.around({ self.value = value })
}
func mutate(f: (inout T) -> Void) {
lock.around({ () -> Void in f(&self.value) })
}
func map<U>(f: T -> U) -> U {
return lock.around({ return f(self.value) })
}
}
let qA = dispatch_queue_create("net.snips.SDK.lock", nil)
let qB = dispatch_queue_create("net.snips.SDK.lock", nil)
var values: SharedValue<[String]> = SharedValue(value: [])
values.mutate({ (inout x: [String]) -> Void in x.append("one") })
dispatch_async(qA, {
values.mutate({ (inout x: [String]) -> Void in x.append("first") })
})
dispatch_async(qA, {
values.mutate({ (inout x: [String]) -> Void in x.append("second") })
})
dispatch_async(qB, {
values.mutate({ (inout x: [String]) -> Void in x.append("third") })
})
// Synchronize with queues (we could have created a dispatch group)
dispatch_sync(qA, {})
dispatch_sync(qB, {})
println(values.get())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment