Skip to content

Instantly share code, notes, and snippets.

@jstn
Last active September 6, 2016 15:28
Show Gist options
  • Save jstn/396e56d5c36f9cda172d to your computer and use it in GitHub Desktop.
Save jstn/396e56d5c36f9cda172d to your computer and use it in GitHub Desktop.
/*
No-Starve and Read/Write mutexes, based on GCD in Swift 3.0
Inspired by the Little Book of Semaphores: http://greenteapress.com/wp/semaphores/
*/
import Foundation
final class NoStarveMutex {
private final let _semaphoreA = DispatchSemaphore(value: 1)
private final let _semaphoreB = DispatchSemaphore(value: 0)
private final var _countA = Int64(0)
private final var _countB = Int64(0)
final func lockAndWait() {
lock()
waitForLock()
}
final func lock() {
OSAtomicIncrement64(&_countA)
_semaphoreA.wait(timeout: DispatchTime.distantFuture)
_countB += 1
if OSAtomicDecrement64(&_countA) == 0 {
_semaphoreB.signal()
} else {
_semaphoreA.signal()
}
}
final func waitForLock() {
_semaphoreB.wait(timeout: DispatchTime.distantFuture)
_countB -= 1
}
final func unlock() {
if _countB == 0 {
_semaphoreA.signal()
} else {
_semaphoreB.signal()
}
}
}
final class ReadWriteMutex {
private final let _readMutex = NoStarveMutex()
private final let _writeMutex = NoStarveMutex()
private final var _count = Int64(0)
final func lock() {
_readMutex.lock()
}
final func lockForReadingAndWait() {
lock()
waitForReadLock()
}
final func waitForReadLock() {
_readMutex.waitForLock()
if OSAtomicIncrement64(&_count) == 1 {
_writeMutex.lockAndWait()
}
_readMutex.unlock()
}
final func unlockForReading() {
if OSAtomicDecrement64(&_count) == 1 {
_writeMutex.unlock()
}
}
final func lockForWritingAndWait() {
lock()
waitForWriteLock()
}
final func waitForWriteLock() {
_readMutex.waitForLock()
_writeMutex.lockAndWait()
}
final func unlockForWriting() {
_writeMutex.unlock()
_readMutex.unlock()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment