Skip to content

Instantly share code, notes, and snippets.

@swillits
Created August 13, 2019 22:19
Show Gist options
  • Save swillits/9b12548150a046ced1452bb4f3870235 to your computer and use it in GitHub Desktop.
Save swillits/9b12548150a046ced1452bb4f3870235 to your computer and use it in GitHub Desktop.
//
// Lock.swift
//
// Created by Seth Willits on 11/23/15.
// Copyright © 2015 Araelium Group. All rights reserved.
//
import Foundation
protocol Lock {
func around(_ block: () throws -> Void) rethrows
func getting<T>(_ block: () throws -> T?) rethrows -> T?
func getting<T>(_ block: () throws -> T) rethrows -> T
}
/// A dispatch_queue_t-based Lock. Great for most uses. In general, use a QueueLock when
/// needing a lock, but keep SpinLock in mind as a fairly easy optimization if it starts
/// to show up in the profiler because there's actually low contention over the lock.
final class QueueLock: Lock {
private var queue = DispatchQueue(label: "", attributes: [])
func around(_ block: () throws -> Void) rethrows {
try queue.sync(execute: block)
}
func getting<T>(_ block: () throws -> T?) rethrows -> T? {
var result: T? = nil
try queue.sync(execute: {
result = try block()
})
return result
}
func getting<T>(_ block: () throws -> T) rethrows -> T {
var result: T! = nil
try queue.sync(execute: {
result = try block()
})
return result!
}
}
final class ReadWriteLock {
private var queue = DispatchQueue(label: "", attributes: [.concurrent])
func writing(_ block: () throws -> Void) rethrows {
try queue.sync(flags: DispatchWorkItemFlags.barrier, execute: block)
}
func reading<T>(_ block: () throws -> T?) rethrows -> T? {
var result: T? = nil
try queue.sync(execute: {
result = try block()
})
return result
}
func reading<T>(_ block: () throws -> T) rethrows -> T {
var result: T! = nil
try queue.sync(execute: {
result = try block()
})
return result!
}
}
/// An NSRecursiveLock-based lock which allows recursive locking. Generally should be avoided,
/// as needing a recursive lock typically points to a problem in the code that's using it, but
/// does have legitimate uses. Reasonably fast.
final class RecursiveLock: Lock {
private var lock = NSRecursiveLock()
func around(_ block: () throws -> Void) rethrows {
lock.lock()
try block()
lock.unlock()
}
func getting<T>(_ block: () throws -> T?) rethrows -> T? {
var result: T? = nil
lock.lock()
result = try block()
lock.unlock()
return result
}
func getting<T>(_ block: () throws -> T) rethrows -> T {
var result: T! = nil
lock.lock()
result = try block()
lock.unlock()
return result!
}
}
/// A non-recursive unfair lock. A lock holder can immediately reacquire the lock before another thread has a chance to acquire it, so a hard loop which grabs the lock over and over can starve others wanting to acquire the lock.
/// The tradeoff is that is a very fast lock.
final class UnfairLock: Lock {
private var lock = os_unfair_lock()
func around(_ block: () throws -> Void) rethrows {
os_unfair_lock_lock(&lock)
try block()
os_unfair_lock_unlock(&lock)
}
func getting<T>(_ block: () throws -> T?) rethrows -> T? {
var result: T? = nil
os_unfair_lock_lock(&lock)
result = try block()
os_unfair_lock_unlock(&lock)
return result
}
func getting<T>(_ block: () throws -> T) rethrows -> T {
var result: T! = nil
os_unfair_lock_lock(&lock)
result = try block()
os_unfair_lock_unlock(&lock)
return result
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment