Skip to content

Instantly share code, notes, and snippets.

@ObuchiYuki
Created March 23, 2020 06:08
Show Gist options
  • Save ObuchiYuki/455b1bb63e60b2a5e96e3ec1345529c0 to your computer and use it in GitHub Desktop.
Save ObuchiYuki/455b1bb63e60b2a5e96e3ec1345529c0 to your computer and use it in GitHub Desktop.
A simple implementation of lock in swift
//
// Lock.swift
// CoreUtil
//
// Created by yuki on 2020/03/22.
// Copyright © 2020 yuki. All rights reserved.
//
import Foundation
public protocol Locking {
@inlinable func lock()
@inlinable func unlock()
}
extension Locking {
/// Executes a closure returning a value while acquiring the lock.
///
/// - Parameter closure: The closure to run.
///
/// - Returns: The value the closure generated.
@inlinable public func around<T>(_ closure: ()->T) -> T {
lock() ; defer { unlock() }
return closure()
}
/// Execute a closure while acquiring the lock.
///
/// - Parameter closure: The closure to run.
@inlinable public func around(_ closure: ()->Void) {
lock() ; defer { unlock() }
closure()
}
}
/// A thin wrapper of `pthread_mutex`
public final class Lock: Locking {
@usableFromInline internal var _lock = pthread_mutex_t()
@inlinable public init() {
#if DEBUG
let err = pthread_mutex_init(&_lock, nil)
switch err {
case 0: break
case EAGAIN: fatalError("Could not create mutex: EAGAIN (The system temporarily lacks the resources to create another mutex.)")
case EINVAL: fatalError("Could not create mutex: invalid attributes")
case ENOMEM: fatalError("Could not create mutex: no memory")
default: fatalError("Could not create mutex, unspecified error \(err)")
}
#else
pthread_mutex_init(&_lock, nil)
#endif
}
@inlinable deinit {
#if DEBUG
assert(
pthread_mutex_trylock(&_lock)==0 && pthread_mutex_unlock(&_lock)==0,
"deinitialization of a locked mutex results in undefined behavior!"
)
#endif
pthread_mutex_destroy(&_lock)
}
/// Lock threads until calling `unlock()`
@inlinable public func lock() {
#if DEBUG
let ret = pthread_mutex_lock(&_lock)
switch ret {
case 0: break
/// With no recursive counter. This will never happen. (I think)
case EDEADLK: fatalError("Could not lock mutex: a deadlock would have occurred")
case EINVAL: fatalError("Could not lock mutex: the mutex is invalid")
default: fatalError("Could not lock mutex: unspecified error \(ret)")
}
#else
pthread_mutex_lock(&_lock)
#endif
}
/// Unlock threads.
@inlinable public func unlock() {
#if DEBUG
let ret = pthread_mutex_unlock(&_lock)
switch ret {
case 0: break
case EPERM: fatalError("Could not unlock mutex: thread does not hold this mutex")
case EINVAL: fatalError("Could not unlock mutex: the mutex is invalid")
default: fatalError("Could not unlock mutex: unspecified error \(ret)")
}
#else
pthread_mutex_unlock(&_lock)
#endif
}
}
/// Use recursive counter to avoid dead lock.
public final class RecursiveLock: Locking {
@usableFromInline internal var _lock = pthread_mutex_t()
@inlinable public init() {
var attr = pthread_mutexattr_t()
pthread_mutexattr_init(&attr)
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)
#if DEBUG
let err = pthread_mutex_init(&_lock, &attr)
switch err {
case 0: break
case EAGAIN: fatalError("Could not create mutex: EAGAIN (The system temporarily lacks the resources to create another mutex.)")
case EINVAL: fatalError("Could not create mutex: invalid attributes")
case ENOMEM: fatalError("Could not create mutex: no memory")
default: fatalError("Could not create mutex, unspecified error \(err)")
}
#else
pthread_mutex_init(&_lock, &attr)
#endif
pthread_mutexattr_destroy(&attr)
}
@inlinable deinit {
#if DEBUG
assert(
pthread_mutex_trylock(&_lock)==0 && pthread_mutex_unlock(&_lock)==0,
"deinitialization of a locked mutex results in undefined behavior!"
)
#endif
pthread_mutex_destroy(&_lock)
}
/// Lock threads until calling `unlock()`
@inlinable public func lock() {
#if DEBUG
let ret = pthread_mutex_lock(&_lock)
switch ret {
case 0: break
/// With no recursive counter. This will never happen. (I think)
case EDEADLK: fatalError("Could not lock mutex: a deadlock would have occurred")
case EINVAL: fatalError("Could not lock mutex: the mutex is invalid")
default: fatalError("Could not lock mutex: unspecified error \(ret)")
}
#else
pthread_mutex_lock(&_lock)
#endif
}
/// Unlock threads.
@inlinable public func unlock() {
#if DEBUG
let ret = pthread_mutex_unlock(&_lock)
switch ret {
case 0: break
case EPERM: fatalError("Could not unlock mutex: thread does not hold this mutex")
case EINVAL: fatalError("Could not unlock mutex: the mutex is invalid")
default: fatalError("Could not unlock mutex: unspecified error \(ret)")
}
#else
pthread_mutex_unlock(&_lock)
#endif
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment