Created
March 23, 2020 06:08
-
-
Save ObuchiYuki/455b1bb63e60b2a5e96e3ec1345529c0 to your computer and use it in GitHub Desktop.
A simple implementation of lock in swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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