-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathCwlMutex.swift
executable file
·142 lines (122 loc) · 5.2 KB
/
CwlMutex.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
//
// CwlMutex.swift
// CwlUtils
//
// Created by Matt Gallagher on 2015/02/03.
// Copyright © 2015 Matt Gallagher ( http://cocoawithlove.com ). All rights reserved.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
// IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
import Foundation
/// A basic mutex protocol that requires nothing more than "performing work inside the mutex".
public protocol ScopedMutex {
/// Perform work inside the mutex
func sync<R>(execute work: () throws -> R) rethrows -> R
func trySync<R>(execute work: () throws -> R) rethrows -> R?
}
/// A more specific kind of mutex that assume an underlying primitive and unbalanced lock/trylock/unlock operators
public protocol RawMutex: ScopedMutex {
associatedtype MutexPrimitive
/// The raw primitive is exposed as an "unsafe" public property for faster access in some cases
var unsafeMutex: MutexPrimitive { get set }
func unbalancedLock()
func unbalancedTryLock() -> Bool
func unbalancedUnlock()
}
extension RawMutex {
/** RECOMMENDATION: until Swift can inline between modules or at least optimize @noescape closures to the stack, if this file is linked into another compilation unit (i.e. linked as part of the CwlUtils.framework but used from another module) it might be a good idea to copy and paste the relevant `fastsync` implementation code into your file (or module and delete `private` if whole module optimization is enabled) and use it instead, allowing the function to be inlined.
~~~
private extension UnfairLock {
func fastsync<R>(execute work: @noescape () throws -> R) rethrows -> R {
os_unfair_lock_lock(&unsafeLock)
defer { os_unfair_lock_unlock(&unsafeLock) }
return try work()
}
}
private extension PThreadMutex {
func fastsync<R>(execute work: @noescape () throws -> R) rethrows -> R {
pthread_mutex_lock(&unsafeMutex)
defer { pthread_mutex_unlock(&unsafeMutex) }
return try work()
}
}
~~~
*/
public func sync<R>(execute work: () throws -> R) rethrows -> R {
unbalancedLock()
defer { unbalancedUnlock() }
return try work()
}
public func trySync<R>(execute work: () throws -> R) rethrows -> R? {
guard unbalancedTryLock() else { return nil }
defer { unbalancedUnlock() }
return try work()
}
}
/// A basic wrapper around the "NORMAL" and "RECURSIVE" `pthread_mutex_t` (a safe, general purpose FIFO mutex). This type is a "class" type to take advantage of the "deinit" method and prevent accidental copying of the `pthread_mutex_t`.
public final class PThreadMutex: RawMutex {
public typealias MutexPrimitive = pthread_mutex_t
// Non-recursive "PTHREAD_MUTEX_NORMAL" and recursive "PTHREAD_MUTEX_RECURSIVE" mutex types.
public enum PThreadMutexType {
case normal
case recursive
}
public var unsafeMutex = pthread_mutex_t()
/// Default constructs as ".Normal" or ".Recursive" on request.
public init(type: PThreadMutexType = .normal) {
var attr = pthread_mutexattr_t()
guard pthread_mutexattr_init(&attr) == 0 else {
preconditionFailure()
}
switch type {
case .normal:
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL)
case .recursive:
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)
}
guard pthread_mutex_init(&unsafeMutex, &attr) == 0 else {
preconditionFailure()
}
}
deinit {
pthread_mutex_destroy(&unsafeMutex)
}
public func unbalancedLock() {
pthread_mutex_lock(&unsafeMutex)
}
public func unbalancedTryLock() -> Bool {
return pthread_mutex_trylock(&unsafeMutex) == 0
}
public func unbalancedUnlock() {
pthread_mutex_unlock(&unsafeMutex)
}
}
/// A basic wrapper around `os_unfair_lock` (a non-FIFO, high performance lock that offers safety against priority inversion). This type is a "class" type to prevent accidental copying of the `os_unfair_lock`.
/// NOTE: due to the behavior of the lock (non-FIFO) a single thread might drop and reacquire the lock without giving waiting threads a chance to resume (leading to potential starvation of waiters). For this reason, it is only recommended in situations where contention is expected to be rare or the interaction between contenders is otherwise known.
@available(OSX 10.12, iOS 10, *)
public final class UnfairLock: RawMutex {
public typealias MutexPrimitive = os_unfair_lock
public init() {
}
/// Exposed as an "unsafe" public property so non-scoped patterns can be implemented, if required.
public var unsafeMutex = os_unfair_lock()
public func unbalancedLock() {
os_unfair_lock_lock(&unsafeMutex)
}
public func unbalancedTryLock() -> Bool {
return os_unfair_lock_trylock(&unsafeMutex)
}
public func unbalancedUnlock() {
os_unfair_lock_unlock(&unsafeMutex)
}
}