Skip to content

Instantly share code, notes, and snippets.

@RomanTruba
Last active December 28, 2017 13:06
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save RomanTruba/65e403377a9b8a39f0b1727c9c8299a9 to your computer and use it in GitHub Desktop.
Save RomanTruba/65e403377a9b8a39f0b1727c9c8299a9 to your computer and use it in GitHub Desktop.
Swift 3, Xcode 8 updated. Also test os_unfair_lock. Much more real life example: working with an array
import XCTest
final class LockingTests: XCTestCase {
// Unsafe in iOS, may cause priority inversion
// But fastest
func testSpinLock() {
var spinLock = OS_SPINLOCK_INIT
executeLockTest { (block) in
OSSpinLockLock(&spinLock)
block()
OSSpinLockUnlock(&spinLock)
}
}
// iOS 10 only, fixed variant of spinlock. Not actually spins, but waits in the kernel
// Maybe be reacquired before woken up waiter gets an opportunity to attempt to acquire the lock
func testUnfairLock() {
var unfairLock = os_unfair_lock_s()
executeLockTest { (block) in
os_unfair_lock_lock(&unfairLock)
block()
os_unfair_lock_unlock(&unfairLock)
}
}
// Fastest after spinlock and unfair_lock
// May cause priority inversion on iOS 9, probably safe in iOS 10
func testDispatchSemaphore() {
let sem = DispatchSemaphore(value: 1)
executeLockTest { (block) in
_ = sem.wait(timeout: DispatchTime.distantFuture)
block()
sem.signal()
}
}
func testNSLock() {
let lock = NSLock()
executeLockTest { (block) in
lock.lock()
block()
lock.unlock()
}
}
func testPthreadMutex() {
var mutex = pthread_mutex_t()
pthread_mutex_init(&mutex, nil)
executeLockTest{ (block) in
pthread_mutex_lock(&mutex)
block()
pthread_mutex_unlock(&mutex)
}
pthread_mutex_destroy(&mutex);
}
// Obj-c analogue is @synchronized
// Almost the same as pthread_mutex, but slower.
// Best for inline usage in non performant places (because you don't need to initialize it before)
func testSynchronized() {
let obj = NSObject()
executeLockTest{ (block) in
objc_sync_enter(obj)
block()
objc_sync_exit(obj)
}
}
func testQueue() {
let lockQueue = DispatchQueue.init(label: "com.test.LockQueue")
executeLockTest{ (block) in
lockQueue.sync() {
block()
}
}
}
// Enable this test to ensure work will fail
func disable_testNoLock() {
executeLockTest { (block) in
block()
}
}
private func executeLockTest(performBlock:@escaping (_ block:() -> Void) -> Void) {
let dispatchBlockCount = 16
let iterationCountPerBlock = 10_000
// This is an example of a performance test case.
let queues = [
DispatchQueue.global(qos: DispatchQoS.QoSClass.userInteractive),
DispatchQueue.global(qos: DispatchQoS.QoSClass.default),
DispatchQueue.global(qos: DispatchQoS.QoSClass.utility),
]
let array = NSMutableArray.init()
self.measure {
let group = DispatchGroup.init()
for block in 0..<dispatchBlockCount {
group.enter()
let queue = queues[block % queues.count]
queue.async(execute: {
for _ in 0..<iterationCountPerBlock {
performBlock({
array.addObjects(from: [1,2]);
array.removeObject(at: 1)
})
}
group.leave()
})
}
_ = group.wait(timeout: DispatchTime.distantFuture)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment