├── .gitattributes ├── .gitignore ├── LICENSE ├── Package.swift ├── README.md ├── Sources └── Threading │ ├── Collections │ ├── Atomic.swift │ ├── ThreadedArray.swift │ ├── ThreadedDictionary.swift │ └── ThreadedQueue.swift │ └── Components │ ├── LabelDispatch.swift │ ├── LinkedQueue.swift │ ├── ThreadedCollection.swift │ ├── ThreadedObject.swift │ └── ThreadingType.swift ├── Tests ├── LinuxMain.swift └── ThreadingTests │ ├── ThreadingTests.swift │ └── XCTestManifests.swift └── Threading.xcodeproj ├── ThreadingTests_Info.plist ├── Threading_Info.plist ├── project.pbxproj ├── project.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist └── xcshareddata └── xcschemes ├── Threading-Package.xcscheme └── xcschememanagement.plist /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | # Package.resolved 41 | .build/ 42 | 43 | # CocoaPods 44 | # 45 | # We recommend against adding the Pods directory to your .gitignore. However 46 | # you should judge for yourself, the pros and cons are mentioned at: 47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 48 | # 49 | # Pods/ 50 | 51 | # Carthage 52 | # 53 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 54 | # Carthage/Checkouts 55 | 56 | Carthage/Build 57 | 58 | # fastlane 59 | # 60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 61 | # screenshots whenever they are needed. 62 | # For more information about the recommended setup visit: 63 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 64 | 65 | fastlane/report.xml 66 | fastlane/Preview.html 67 | fastlane/screenshots/**/*.png 68 | fastlane/test_output 69 | 70 | .idea 71 | *.DS_Store 72 | 73 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Jeremy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:4.0 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "Threading", 8 | products: [ 9 | .library(name: "Threading", targets: ["Threading"]), 10 | ], 11 | dependencies: [ 12 | 13 | ], 14 | targets: [ 15 | .target(name: "Threading", dependencies: []), 16 | .testTarget(name: "ThreadingTests", dependencies: ["Threading"]), 17 | ] 18 | ) 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Threading 2 | 3 | Threading provides a collection of thread-safe objects implemented purly in Swift. The objects are designed to be as easy to use as the native swift objects, but with an underlying implementation that is designed for concurrent reading and writing. 4 | 5 | This library was inspired by [this](http://basememara.com/creating-thread-safe-arrays-in-swift/) blog post by Basem Emara. 6 | 7 | ## Table of Contents 8 | - [Installation](#installation) 9 | - [Usage](#usage) 10 | - [Common](#common) 11 | - [Atomic](#atomic) 12 | - [ThreadedArray](#threadedarray) 13 | - [ThreadedDictionary](#threadeddictionary) 14 | - [ThreadedQueue](#threadedqueue) 15 | 16 | ## Installation 17 | 18 | To install using Swift Package Manager, simply include the following line in your `Package.swift` file under dependencies. Don't forget to add the dependency `"Threading"` to any targets that require the library. 19 | 20 | ``` swift 21 | .package(url: "https://github.com/Miraion/Threading.git", from: "1.0.0"), 22 | ``` 23 | 24 | Then simply add the following import statement to your source files. 25 | 26 | ``` swift 27 | import Threading 28 | ``` 29 | 30 | ## Usage 31 | 32 | ### Common 33 | 34 | Most threaded objects take an optional `ThreadingType` enum parameter in their initializers. This parameter tells the object how it should behave when being interacted with. There are two options: `ThreadingType.serial` where only one thread can use the object at a time, and `ThreadingType.concurrent` (default) where multiple threads can read from the object at a time, but when being mutated, only one thread can have access. 35 | 36 | All threaded objects extend the generic `ThreadedObject` base class which wraps around an object and provides thread-safe interfaces for interacting with said object. These three methods, `async`, `sync` and `mutableSync`, act as the interfaces through which the underlying (threaded) object may be viewed. 37 | 38 | The `async` method acts as a mutable interface for the theaded object. The theaded object is passed as an `inout` parameter to a given closure which is executed concurrently along side the calling thread. Any actions that mutate the threaded object should be done asynchronously to ensure that no other thread is trying to access the threaded object at the same time. 39 | 40 | ``` swift 41 | async(_ callback: @escaping (inout T) -> Void) 42 | ``` 43 |
44 | 45 | The `sync` method is a readonly interface for the threaded object. This method is executed within the calling thread which allows values to be returned from this method. Only non-mutating methods may be called from within this method as there is no guarentee that the calling thread is the only viewer of the threaded object. 46 | 47 | ``` swift 48 | sync(_ callback: @escaping (T) -> ReturnType) -> ReturnType 49 | ``` 50 | 51 |
52 | 53 | A third interface, `mutatingSync`, is a mutable interface that is executed within the calling thread. This method should be used when an action needs to both write and return a value from the underlying object: for example, the 'pop' action of a stack. 54 | 55 | ``` swift 56 | mutatingSync(_ callback: @escaping (inout T) -> ReturnType) -> ReturnType 57 | ``` 58 | 59 | ### Atomic 60 | 61 | The `Atomic` class is a thread-safe container that holds a single object. The object can be interacted with using the `sync` and `async` methods described above or special `load` and `store` methods that are built specifically for this class. 62 | 63 | #### Methods 64 | 65 | ----- 66 | 67 | ``` swift 68 | func load() -> T 69 | ``` 70 | 71 | A synchronous read method that returns a copy of the threaded object. 72 | 73 | ----- 74 | 75 | ``` swift 76 | func store(_ newElement: T) 77 | ``` 78 | 79 | An asynchronous write method that stores a brand new value in the atomic object. 80 | 81 | ----- 82 | 83 | ``` swift 84 | func loading(_ action: @escaping (inout T) -> Void) 85 | ``` 86 | 87 | Performs an action on the underlying value asynchronously. 88 | 89 | ----- 90 | 91 | #### Example 92 | 93 | ``` swift 94 | let atomicFlag = Atomic(true) 95 | 96 | DispatchQueue.main.async { 97 | while atomicFlag.load() { 98 | print("From Async Thread") 99 | sleep(1) 100 | } 101 | } 102 | 103 | sleep(5) 104 | atomicFlag.store(false) 105 | ``` 106 | 107 | ### ThreadedArray 108 | 109 | `ThreadedArray`, as the name implies, is a thread-safe, random access array. A majority of Swift's standard array methods are incorperated into this object to make it as easy to use as possible. 110 | 111 | `ThreadedArray` conforms to the `Collection` protocol; meaning that can be used in `for in` loops. 112 | 113 | #### Types 114 | 115 | ``` swift 116 | typealias InternalCollectionType = [T] 117 | ```` 118 | 119 | ``` swift 120 | typealias Element = InternalCollectionType.Element 121 | ``` 122 | 123 | ``` swift 124 | typealias Index = InternalCollectionType.Index 125 | ``` 126 | 127 | #### Methods 128 | 129 | ----- 130 | 131 | ``` swift 132 | subscript(position: Int) -> T { get, set } 133 | ``` 134 | 135 | Random read/write access to any element in the array. Gets are performed synchronously while sets are performed asynchronously. 136 | 137 | ----- 138 | 139 | ``` swift 140 | func append(_ newElement: T) 141 | ``` 142 | 143 | Asynchronously appends a new element to the end of the array. 144 | 145 | ----- 146 | 147 | ``` swift 148 | func remove(at position: Int, callback: ((T) -> Void)? = nil) 149 | ``` 150 | 151 | Removes an element at a given position and passes that element to an optional closure which is executed asynchronously on the main thread after the element is removed. 152 | 153 | ----- 154 | 155 | ### ThreadedDictionary 156 | 157 | `ThreadedDictionary` is a thread-safe wrapper class for Swift's standard dictionary. A majority of the methods present in the standard dictionary are present in `ThreadedDictionary`. 158 | 159 | `ThreadedDictionary` conforms to the `Collection` protocol; meaning that it can be used in `for in` loops. 160 | 161 | #### Types 162 | 163 | ``` swift 164 | typealias InternalCollectionType = [K : V] 165 | ``` 166 | 167 | ``` swift 168 | typealias Index = InternalCollectionType.Index 169 | ``` 170 | 171 | ``` swift 172 | typealias Element = InternalCollectionType.Element 173 | ``` 174 | 175 | ``` swift 176 | typealias Keys = InternalCollectionType.Keys 177 | ``` 178 | 179 | ``` swift 180 | typealias Values = InternalCollectionType.Values 181 | ``` 182 | 183 | #### Methods 184 | 185 | ----- 186 | 187 | ``` swift 188 | subscript(key: K) -> V? { get, set } 189 | ``` 190 | 191 | Key style subscript. Gets are performed synchronously while sets are perfomed asynchronously. 192 | 193 | ----- 194 | 195 | ``` swift 196 | var keys: Keys { get } 197 | ``` 198 | 199 | ``` swift 200 | var values: Values { get } 201 | ``` 202 | 203 | Returns the keys/values of the dictionary as a collection. 204 | 205 | ----- 206 | 207 | ### ThreadedQueue 208 | 209 | `ThreadedQueue` is a thread-safe wrapper for the `LinkedQueue` class which comes with this library. It is a linked list style implementation of a queue which provides insertions and removals that are *O(1)*. 210 | 211 | #### Types 212 | 213 | ``` swift 214 | typealias InternalCollectionType = LinkedQueue 215 | ``` 216 | 217 | ``` swift 218 | typealias Element = InternalCollectionType.Element 219 | ``` 220 | 221 | #### Methods 222 | 223 | ----- 224 | 225 | ``` swift 226 | func enqueue(_ newElement: T) 227 | ``` 228 | 229 | Asynchronously adds an element to the end of the queue. 230 | 231 | ----- 232 | 233 | ``` swift 234 | func dequeue() -> Element 235 | ``` 236 | 237 | Synchronously removes and returns the first element of the queue. This method should **not** be called on an empty queue. 238 | 239 | Uses the `mutatingSync` interface. 240 | 241 | ----- 242 | 243 | ``` swift 244 | func safeDequeue() -> Element? 245 | ``` 246 | 247 | A safe version of `dequeue` that can be called on an empty queue. This method should be used instead of the regular `dequeue` when multiple threads are simultaneously removing elements from the queue. 248 | 249 | ----- 250 | 251 | ``` swift 252 | var isEmpty: Bool { get } 253 | ``` 254 | 255 | Returns `true` if the queue is empty, `false` otherwise. 256 | 257 | ----- 258 | 259 | ``` swift 260 | var count: Int { get } 261 | ``` 262 | 263 | Returns the number of elements in the queue. 264 | 265 | ----- 266 | -------------------------------------------------------------------------------- /Sources/Threading/Collections/Atomic.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Atomic.swift 3 | // Threading 4 | // 5 | // Created by Jeremy Schwartz on 2018-06-16. 6 | // 7 | 8 | import Foundation 9 | 10 | public class Atomic : ThreadedObject { 11 | 12 | /// Initializes this atomic element from a normal element. 13 | /// 14 | /// - parameters: 15 | /// - value: Value to store. 16 | /// 17 | /// - type: The access type for this attomic element. 18 | public override init(_ value: Element, type: ThreadingType) { 19 | super.init(value, type: type) 20 | } 21 | 22 | /// Initializes this atomic element from a normal element using a concurrent 23 | /// threading type. 24 | /// 25 | /// - parameters: 26 | /// - value: Value to store. 27 | public convenience init(_ value: Element) { 28 | self.init(value, type: .concurrent) 29 | } 30 | 31 | /// Performs a synchronous access to the internal value and returns its 32 | /// state. 33 | /// 34 | /// - returns: The value stored in the atomic object. 35 | /// 36 | /// _Synchronous Method_ 37 | public func load() -> Element { 38 | return sync { value in 39 | return value 40 | } 41 | } 42 | 43 | /// Performs an asynchronous action on the internal value. 44 | /// 45 | /// - parameters: 46 | /// - action: A closure containing code that which may access the 47 | /// internal value of this atomic object. 48 | /// 49 | /// _Asynchronous Method_ 50 | public func loading(_ action: @escaping (inout Element) -> Void) { 51 | async { value in 52 | action(&value) 53 | } 54 | } 55 | 56 | /// Stores a given value in this atomic object. 57 | /// 58 | /// - parameters: 59 | /// - newValue: The value to store. 60 | /// 61 | /// _Asynchronous Method_ 62 | public func store(_ newValue: Element) { 63 | async { value in 64 | value = newValue 65 | } 66 | } 67 | } 68 | 69 | 70 | extension Atomic : CustomStringConvertible 71 | where Element : CustomStringConvertible 72 | { 73 | public var description: String { 74 | return load().description 75 | } 76 | } 77 | 78 | extension Atomic : CustomDebugStringConvertible 79 | where Element : CustomDebugStringConvertible 80 | { 81 | public var debugDescription: String { 82 | return load().debugDescription 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Sources/Threading/Collections/ThreadedArray.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ThreadedArray.swift 3 | // Threading 4 | // 5 | // Created by Jeremy Schwartz on 2018-06-17. 6 | // 7 | 8 | import Foundation 9 | 10 | public class ThreadedArray : ThreadedObject> { 11 | 12 | public typealias InternalCollectionType = Array 13 | public typealias Index = InternalCollectionType.Index 14 | public typealias Element = InternalCollectionType.Element 15 | 16 | /// Constructs from an existing array and a threading type. 17 | /// 18 | /// - parameters: 19 | /// - array: An array who's elements will be copied to the new array. 20 | /// 21 | /// - type: The threading type which defines how this array should act. 22 | public override init(_ array: [Element], type: ThreadingType) { 23 | super.init(array, type: type) 24 | } 25 | 26 | /// Initializes this array with a blank base array and concurrent type. 27 | public convenience init() { 28 | self.init([Element](), type: .concurrent) 29 | } 30 | 31 | /// Constructs from an existing array. 32 | /// 33 | /// - parameters: 34 | /// - array: An array who's elements will be copied to the new array. 35 | /// 36 | /// Threading type is set to concurrent. 37 | public convenience init(_ array: [Element]) { 38 | self.init(array, type: .concurrent) 39 | } 40 | 41 | } 42 | 43 | // MARK: - Mutating Methods 44 | 45 | public extension ThreadedArray { 46 | 47 | /// Appends a new element to the end of the array. 48 | /// 49 | /// - parameters: 50 | /// - newElement: The element to add to the array. 51 | public func append(_ newElement: Element) { 52 | async { collection in 53 | collection.append(newElement) 54 | } 55 | } 56 | 57 | /// Appends the contents of a sequence to the end of the array. 58 | /// 59 | /// - parameters: 60 | /// - sequence: The sequence who's elements should be added. 61 | /// 62 | /// `Sequence.Elmement` must be the same as `Self.Element`. 63 | /// 64 | /// - complexity: _O(n)_, where n is the length of the resulting array. 65 | public func append(contentsOf sequence: S) 66 | where S.Element == Element 67 | { 68 | async { collection in 69 | collection.append(contentsOf: sequence) 70 | } 71 | } 72 | 73 | /// Removes the element at a given index. 74 | /// 75 | /// - parameters: 76 | /// - index: The position at which to remove an element. 77 | /// 78 | /// - callback: An optional closure which is passed the element that was 79 | /// just removed. This closure, if not `nil`, will be called 80 | /// asynchronously on the main thread once the removal is 81 | /// complete. 82 | /// 83 | /// - complexity: _O(n)_, where n is the size of the collection. 84 | public func remove(at index: Index, callback: ((Element) -> Void)? = nil) { 85 | async { collection in 86 | let value = collection.remove(at: index) 87 | if let function = callback { 88 | DispatchQueue.main.async { 89 | function(value) 90 | } 91 | } 92 | } 93 | } 94 | 95 | /// Removes the first element in the array. 96 | /// 97 | /// - parameters: 98 | /// - callback: An optional closure which is passed the element that was 99 | /// just removed. This closure, if not `nil`, will be called 100 | /// asynchronously on the main thread once the removal is 101 | /// complete. 102 | /// 103 | /// The collection must not be empty. 104 | /// 105 | /// - complexity: _O(n)_, where n in the size of the collection. 106 | public func removeFirst(callback: ((Element) -> Void)? = nil) { 107 | async { collection in 108 | let value = collection.removeFirst() 109 | if let function = callback { 110 | DispatchQueue.main.async { 111 | function(value) 112 | } 113 | } 114 | } 115 | } 116 | 117 | /// Removes the last element in the array. 118 | /// 119 | /// - parameters: 120 | /// - callback: An optional closure which is passed the element that was 121 | /// just removed. This closure, if not `nil`, will be called 122 | /// asynchronously on the main thread once the removal is 123 | /// complete. 124 | /// 125 | /// The collection must not be empty. 126 | /// 127 | /// - complexity: _O(1)_ 128 | public func removeLast(callback: ((Element) -> Void)? = nil) { 129 | async { collection in 130 | let value = collection.removeLast() 131 | if let function = callback { 132 | DispatchQueue.main.async { 133 | function(value) 134 | } 135 | } 136 | } 137 | } 138 | 139 | } 140 | 141 | // MARK: - Threaded Collection Conformance 142 | 143 | extension ThreadedArray : ThreadedCollection { 144 | 145 | /// Returns an unmanaged version of the underlying object. 146 | public var unthreaded: Array { 147 | return sync { collection in 148 | return collection 149 | } 150 | } 151 | 152 | } 153 | 154 | // MARK: - Swift Collection Conformance 155 | 156 | extension ThreadedArray : MutableCollection, RandomAccessCollection { 157 | 158 | /// The position of the first element in a non-empty array. 159 | public var startIndex: Int { 160 | return sync { collection in 161 | return collection.startIndex 162 | } 163 | } 164 | 165 | /// The collection's "past the end" position – that is the position one 166 | /// past the last valid subscript argument. 167 | public var endIndex: Int { 168 | return sync { collection in 169 | return collection.endIndex 170 | } 171 | } 172 | 173 | /// Provides random access to any element located in the collection. 174 | /// 175 | /// - parameters: 176 | /// - position: The zero-indexed location of the element to access. 177 | /// `position` must be witin the range 178 | /// `startIndex.. Element { 181 | get { 182 | return sync { collection in 183 | return collection[position] 184 | } 185 | } 186 | set { 187 | async { collection in 188 | collection[position] = newValue 189 | } 190 | } 191 | } 192 | 193 | /// Returns the position immediately after a given index. 194 | /// 195 | /// - parameters: 196 | /// - i: A valid position in the collection, 197 | /// i must be less than `endIndex`, 198 | public func index(after i: Index) -> Index { 199 | return sync { collection in 200 | return collection.index(after: i) 201 | } 202 | } 203 | 204 | } 205 | -------------------------------------------------------------------------------- /Sources/Threading/Collections/ThreadedDictionary.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ThreadedDictionary.swift 3 | // Threading 4 | // 5 | // Created by Jeremy Schwartz on 2018-06-17. 6 | // 7 | 8 | import Foundation 9 | 10 | public class ThreadedDictionary : 11 | ThreadedObject> 12 | { 13 | 14 | public typealias InternalCollectionType = Dictionary 15 | public typealias Index = InternalCollectionType.Index 16 | public typealias Element = InternalCollectionType.Element 17 | public typealias Keys = InternalCollectionType.Keys 18 | public typealias Values = InternalCollectionType.Values 19 | 20 | /// Constructs from an existing dictionary and a threading type. 21 | /// 22 | /// - parameters: 23 | /// - dictionary: A dictionary who's elements will be copied to the 24 | /// threaded dictionary. 25 | /// 26 | /// - type: The threading type which defines how this object should act. 27 | public override init(_ dictionary: [Key : Value], type: ThreadingType) { 28 | super.init(dictionary, type: type) 29 | } 30 | 31 | /// Initializes with a blank dictionary and concurrent type. 32 | public convenience init() { 33 | self.init([Key : Value](), type: .concurrent) 34 | } 35 | 36 | /// Constructs from an existing dictionary. 37 | /// 38 | /// - parameters: 39 | /// - dictionary: A dictionary who's elements will be copied to the 40 | /// threaded dictionary. 41 | /// 42 | /// Threading type is set to concurrent. 43 | public convenience init(_ dictionary: [Key : Value]) { 44 | self.init(dictionary, type: .concurrent) 45 | } 46 | 47 | /// Key style access for the dictionary. 48 | /// 49 | /// - parameters: 50 | /// - key: Access key for the desired value. 51 | /// 52 | /// If no key-value pair exists in the dictionary with the given key, then 53 | /// the subscript returns nil. 54 | /// 55 | /// Setting a value to nil will remove that key-value pair from the 56 | /// dictionary. 57 | public subscript(key: Key) -> Value? { 58 | get { 59 | return sync { collection in 60 | return collection[key] 61 | } 62 | } 63 | set { 64 | async { collection in 65 | collection[key] = newValue 66 | } 67 | } 68 | } 69 | 70 | } 71 | 72 | // MARK: - Computed Properties 73 | 74 | public extension ThreadedDictionary { 75 | 76 | /// A collection containing just the keys of the dictionary. 77 | public var keys: Keys { 78 | return sync { collection in 79 | return collection.keys 80 | } 81 | } 82 | 83 | /// A collection containing just the values of the dictionary. 84 | public var values: Values { 85 | return sync { collection in 86 | return collection.values 87 | } 88 | } 89 | 90 | } 91 | 92 | // MARK: - Non-mutating Methods 93 | 94 | public extension ThreadedDictionary { 95 | 96 | public func mapValues 97 | (_ transform: @escaping (Value) -> T) -> [Key : T] 98 | { 99 | var result = [Key : T]() 100 | sync { collection in 101 | result = collection.mapValues(transform) 102 | } 103 | return result 104 | } 105 | 106 | } 107 | 108 | 109 | // MARK: - Threaded Collection Conformance 110 | 111 | extension ThreadedDictionary : ThreadedCollection { 112 | 113 | /// Returns an unmanaged version of the underlying object. 114 | public var unthreaded: Dictionary { 115 | return sync { collection in 116 | return collection 117 | } 118 | } 119 | 120 | } 121 | 122 | // MARK: - Swift Collection Conformance 123 | 124 | extension ThreadedDictionary : Collection { 125 | 126 | /// Positional based subscript for the dictionary. 127 | /// 128 | /// - parameters: 129 | /// - position: The zero-indexed location of the element to access. 130 | /// `position` must be witin the range 131 | /// `startIndex.. Element { 134 | return sync { collection in 135 | return collection[position] 136 | } 137 | } 138 | 139 | /// Returns the position immediately after a given index. 140 | /// 141 | /// - parameters: 142 | /// - i: A valid position in the collection, 143 | /// i must be less than `endIndex`, 144 | public func index(after i: Index) -> Index { 145 | return sync { collection in 146 | return collection.index(after: i) 147 | } 148 | } 149 | 150 | /// The position of the first element in a non-empty dictionary. 151 | public var startIndex: Index { 152 | return sync { collection in 153 | return collection.startIndex 154 | } 155 | } 156 | 157 | /// The collection's "past the end" position – that is the position one 158 | /// past the last valid subscript argument. 159 | public var endIndex: Index { 160 | return sync { collection in 161 | return collection.endIndex 162 | } 163 | } 164 | 165 | } 166 | -------------------------------------------------------------------------------- /Sources/Threading/Collections/ThreadedQueue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ThreadedQueue.swift 3 | // Threading 4 | // 5 | // Created by Jeremy Schwartz on 2018-06-17. 6 | // 7 | 8 | import Foundation 9 | 10 | public class ThreadedQueue : ThreadedObject> { 11 | 12 | public typealias InternalCollectionType = LinkedQueue 13 | public typealias Element = InternalCollectionType.Element 14 | 15 | /// Initailizes with a given threading type from an existing queue. 16 | /// 17 | /// - parameters: 18 | /// - queue: An existing queue to copy elements from. 19 | /// 20 | /// - type: The threading type which defines ho this queue should act. 21 | public override init(_ queue: LinkedQueue, type: ThreadingType) { 22 | super.init(queue, type: type) 23 | } 24 | 25 | /// Initializes as a blank queue and concurrent type. 26 | public convenience init() { 27 | self.init(LinkedQueue(), type: .concurrent) 28 | } 29 | 30 | /// Initializes from an existing queue with concurrent type. 31 | /// 32 | /// - parameters: 33 | /// - queue: An existing queue to copy elements from. 34 | public convenience init(_ queue: LinkedQueue) { 35 | self.init(queue, type: .concurrent) 36 | } 37 | 38 | } 39 | 40 | // MARK: - Computed Properties 41 | 42 | public extension ThreadedQueue { 43 | 44 | /// Number of elements in the queue. 45 | public var count: Int { 46 | return sync { queue in 47 | return queue.count 48 | } 49 | } 50 | 51 | /// Returns `true` if there are no elements in the queue, `false` otherwise. 52 | public var isEmpty: Bool { 53 | return sync { queue in 54 | return queue.isEmpty 55 | } 56 | } 57 | 58 | /// First element of the queue if any. 59 | public var front: Element? { 60 | return sync { queue in 61 | return queue.front 62 | } 63 | } 64 | 65 | /// Last element ofthe queue if any. 66 | public var back: Element? { 67 | return sync { queue in 68 | return queue.back 69 | } 70 | } 71 | 72 | } 73 | 74 | // MARK: - Mutating Methods 75 | 76 | public extension ThreadedQueue { 77 | 78 | /// Adds an item to the end of the queue. 79 | /// 80 | /// - parameters: 81 | /// - newElement: The element to add to the queue. 82 | /// 83 | /// - complexity: _O(1)_ 84 | func enqueue(_ newElement: Element) { 85 | async { queue in 86 | queue.enqueue(newElement) 87 | } 88 | } 89 | 90 | /// Removes and returns the first element of the queue. 91 | /// 92 | /// - complexity: _O(1)_ 93 | /// 94 | /// - warning: Do not call this method on an empty queue; method will throw 95 | /// a fatal error. If multiple threads are removing items at the same 96 | /// time, use of `safeDequeue` is recommended. 97 | @discardableResult 98 | func dequeue() -> Element { 99 | return mutatingSync { queue in 100 | return queue.dequeue() 101 | } 102 | } 103 | 104 | /// Removes and returns the first element of the queue if the queue is not 105 | /// empty; otherwise, `nil` is returned. 106 | /// 107 | /// This is a safer version of `dequeue` and should be used instead of it 108 | /// when multiple threads are removing items from the queue. 109 | /// 110 | /// - complexity: _O(1)_ 111 | @discardableResult 112 | func safeDequeue() -> Element? { 113 | return mutatingSync { queue in 114 | if !queue.isEmpty { 115 | return queue.dequeue() 116 | } else { 117 | return nil 118 | } 119 | } 120 | } 121 | 122 | } 123 | 124 | // MARK: - Non-mutating methods 125 | 126 | public extension ThreadedQueue { 127 | 128 | /// Performs an action for each element in the queue. 129 | /// 130 | /// - parameters: 131 | /// - callback: The function to call on every element. 132 | /// 133 | /// - complexity: _O(n * x)_ where n is the size of the queue and x is the 134 | /// complexity of `callback` 135 | func forEach(_ callback: @escaping (Element) -> Void) { 136 | return sync { queue in 137 | queue.forEach(callback) 138 | } 139 | } 140 | 141 | /// Returns an array whose elements are the result of a transformation done 142 | /// to each element of the queue. 143 | /// 144 | /// - parameters: 145 | /// - transform: A function called on each element in the queue to 146 | /// change the element into the desired type. 147 | func map(_ transform: @escaping (Element) -> U) -> [U] { 148 | return sync { queue in 149 | return queue.map(transform) 150 | } 151 | } 152 | 153 | } 154 | 155 | // MARK: - Threaded Collection Conformance 156 | 157 | extension ThreadedQueue : ThreadedCollection { 158 | 159 | /// Returns the underlying object. 160 | public var unthreaded: InternalCollectionType { 161 | return sync { queue in 162 | return queue 163 | } 164 | } 165 | 166 | } 167 | -------------------------------------------------------------------------------- /Sources/Threading/Components/LabelDispatch.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LabelDispatch.swift 3 | // Threading 4 | // 5 | // Created by Jeremy Schwartz on 2018-06-16. 6 | // 7 | 8 | internal class LabelDispatch { 9 | 10 | private static var nextId = 0 11 | 12 | internal static func get() -> String { 13 | let id = nextId 14 | nextId += 1 15 | return "com.threading.dispatchqueue_\(id)" 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /Sources/Threading/Components/LinkedQueue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LinkedQueue.swift 3 | // Threading 4 | // 5 | // Created by Jeremy Schwartz on 2018-06-23. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Linked list implementation of a first-in first-out queue. Insertion and 11 | /// extraction operators are O(1). This collection does not provide random 12 | /// access to its elements. 13 | public struct LinkedQueue { 14 | 15 | public typealias Element = T 16 | 17 | /// Linked list node class. 18 | internal class LinkedNode { 19 | internal var element: Element 20 | internal var next: LinkedNode? 21 | 22 | init(_ element: Element, _ next: LinkedNode? = nil) { 23 | self.element = element 24 | self.next = next 25 | } 26 | } 27 | 28 | fileprivate var root: LinkedNode? = nil 29 | fileprivate var tail: LinkedNode? = nil 30 | 31 | /// Number of elements in the queue. 32 | public fileprivate(set) var count: Int = 0 33 | 34 | public init() { } 35 | 36 | } 37 | 38 | // MARK: Computed Properties 39 | 40 | public extension LinkedQueue { 41 | 42 | /// First element of the queue if any. 43 | public var front: Element? { 44 | return root?.element 45 | } 46 | 47 | /// Last element of the queue if any. 48 | public var back: Element? { 49 | return tail?.element 50 | } 51 | 52 | /// Returns `true` if there are no elements in the queue, `false` otherwise. 53 | public var isEmpty: Bool { 54 | return count == 0 55 | } 56 | 57 | } 58 | 59 | // MARK: Mutating Methods 60 | 61 | public extension LinkedQueue { 62 | 63 | /// Adds an item to the end of the queue. 64 | /// 65 | /// - parameters: 66 | /// - newElement: The element to add to the queue. 67 | /// 68 | /// - complexity: _O(1)_ 69 | public mutating func enqueue(_ newElement: Element) { 70 | if let t = tail { 71 | t.next = LinkedNode(newElement) 72 | tail = t.next 73 | } else { 74 | root = LinkedNode(newElement) 75 | tail = root 76 | } 77 | count += 1 78 | } 79 | 80 | /// Removes and returns the first element of the queue. 81 | /// 82 | /// - complexity: _O(1)_ 83 | /// 84 | /// - warning: Do not call this method on an empty queue; method will throw 85 | /// a fatal error. 86 | @discardableResult 87 | public mutating func dequeue() -> Element { 88 | if root == nil { 89 | fatalError("dequeue called on empty queue") 90 | } 91 | let element = root!.element 92 | root = root?.next 93 | count -= 1 94 | if count == 0 { 95 | tail = nil 96 | } 97 | return element 98 | } 99 | 100 | } 101 | 102 | // MARK: Non-mutating Methods 103 | 104 | public extension LinkedQueue { 105 | 106 | /// Performs an action for each element in the queue. 107 | /// 108 | /// - parameters: 109 | /// - callback: The function to call on every element. 110 | /// 111 | /// - complexity: _O(n * x)_ where n is the size of the queue and x is the 112 | /// complexity of `callback` 113 | public func forEach 114 | (_ callback: @escaping (Element) throws -> Void) rethrows 115 | { 116 | var node = root 117 | while node != nil { 118 | try callback(node!.element) 119 | node = node!.next 120 | } 121 | } 122 | 123 | /// Returns an array whose elements are the result of a transformation done 124 | /// to each element of the queue. 125 | /// 126 | /// - parameters: 127 | /// - transform: A function called on each element in the queue to 128 | /// change the element into the desired type. 129 | func map(_ transform: @escaping (Element) throws -> U) rethrows -> [U] { 130 | var result = [U]() 131 | try forEach { element in 132 | result.append(try transform(element)) 133 | } 134 | return result 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /Sources/Threading/Components/ThreadedCollection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ThreadedCollection.swift 3 | // Threading 4 | // 5 | // Created by Jeremy Schwartz on 2018-06-17. 6 | // 7 | 8 | import Foundation 9 | 10 | public protocol ThreadedCollection { 11 | 12 | associatedtype InternalCollectionType 13 | 14 | /// Returns an unmanaged version of the underlying object. 15 | var unthreaded: InternalCollectionType { get } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /Sources/Threading/Components/ThreadedObject.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ThreadedObject.swift 3 | // Threading 4 | // 5 | // Created by Jeremy Schwartz on 2018-06-17. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Base class for all threaded objects. Provides asyncronous and syncronous 11 | /// views for writting and reading respectively. 12 | public class ThreadedObject { 13 | 14 | /// Underlying value to be accessed by the different views. 15 | internal var threadedValue: Element 16 | 17 | /// Dispatch queue to control interactions with this object. 18 | private var queue: DispatchQueue 19 | 20 | /// The type of dispatch queue used in this object; either concurrent or 21 | /// serial. 22 | public private(set) var threadingType: ThreadingType 23 | 24 | internal init(_ value: Element, type: ThreadingType) { 25 | self.threadedValue = value 26 | self.threadingType = type 27 | if type == .concurrent { 28 | self.queue = DispatchQueue( 29 | label: LabelDispatch.get(), 30 | attributes: .concurrent 31 | ) 32 | } else { 33 | self.queue = DispatchQueue(label: LabelDispatch.get()) 34 | } 35 | } 36 | 37 | /// Asyncronous interaction with this object; used for writting to 38 | /// the underlying value. 39 | public func async(_ callback: @escaping (inout Element) -> Void) { 40 | switch (threadingType) { 41 | case .concurrent: 42 | queue.async(flags: .barrier) { callback(&self.threadedValue) } 43 | 44 | case .serial: 45 | queue.async { callback(&self.threadedValue) } 46 | } 47 | } 48 | 49 | /// Syncronous internaction with this object; used for reading from the 50 | /// underlying value. 51 | public func sync 52 | (_ callback: @escaping (Element) -> ReturnType) -> ReturnType 53 | { 54 | return queue.sync { return callback(self.threadedValue) } 55 | } 56 | 57 | /// Mutable syncronous interaction with this object; used when needing to 58 | /// both read and write to the underlying value. 59 | public func mutatingSync 60 | (_ callback: @escaping (inout Element) -> ReturnType) -> ReturnType 61 | { 62 | return queue.sync(flags: .barrier) { 63 | return callback(&self.threadedValue) 64 | } 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /Sources/Threading/Components/ThreadingType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ThreadingType.swift 3 | // Threading 4 | // 5 | // Created by Jeremy Schwartz on 2018-06-16. 6 | // 7 | 8 | /// Enumeration of threading types for threaded objects. Depending on the case 9 | /// supplied to the threaded object, the object will behave differently. 10 | public enum ThreadingType { 11 | 12 | /// Allows concurrent reads on threaded objects. 13 | /// When writting, concurrency is disabled. 14 | case concurrent 15 | 16 | /// All operations, reads and writes, are executed sequentially. 17 | case serial 18 | 19 | } 20 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import ThreadingTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += ThreadingTests.allTests() 7 | XCTMain(tests) -------------------------------------------------------------------------------- /Tests/ThreadingTests/ThreadingTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Threading 3 | 4 | final class ThreadingTests: XCTestCase { 5 | 6 | func testAtomic() { 7 | let atomic = Atomic(0) 8 | DispatchQueue.concurrentPerform(iterations: 1000) { value in 9 | atomic.loading { value in 10 | value += 1 11 | } 12 | } 13 | print(atomic.load()) 14 | } 15 | 16 | func testArray() { 17 | let array = ThreadedArray() 18 | let count = 1000 19 | DispatchQueue.concurrentPerform(iterations: count) { _ in 20 | if let x = array.last { 21 | array.append(x + array.count) 22 | } else { 23 | array.append(0) 24 | } 25 | } 26 | XCTAssert(array.count == count) 27 | } 28 | 29 | func testDictionary() { 30 | let dic = ThreadedDictionary() 31 | let count = 1000 32 | DispatchQueue.concurrentPerform(iterations: count) { i in 33 | dic[i / 4] = dic[i] ?? 0 34 | } 35 | XCTAssert(dic.count == count / 4) 36 | } 37 | 38 | func testQueue() { 39 | let queue = ThreadedQueue() 40 | let shouldContinue = Atomic(true) 41 | var array = [Int]() 42 | DispatchQueue.global().async { 43 | DispatchQueue.concurrentPerform(iterations: 1000) { i in 44 | queue.enqueue(i) 45 | } 46 | shouldContinue.store(false) 47 | } 48 | while shouldContinue.load() || !queue.isEmpty { 49 | if !queue.isEmpty { 50 | array.append(queue.dequeue()) 51 | } 52 | } 53 | 54 | XCTAssert(array.count == 1000) 55 | } 56 | 57 | func testLinkedQueue() { 58 | var queue = LinkedQueue() 59 | queue.enqueue(1) 60 | XCTAssert(queue.count == 1) 61 | XCTAssert(queue.dequeue() == 1) 62 | XCTAssert(queue.count == 0) 63 | queue.enqueue(2) 64 | XCTAssert(queue.count == 1) 65 | XCTAssert(queue.dequeue() == 2) 66 | XCTAssert(queue.count == 0) 67 | } 68 | 69 | 70 | static var allTests = [ 71 | ("testAtomic", testAtomic), 72 | ("testArray", testArray), 73 | ("testDictionary", testDictionary), 74 | ("queueTest", testQueue), 75 | ] 76 | } 77 | -------------------------------------------------------------------------------- /Tests/ThreadingTests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | #if !os(macOS) 4 | public func allTests() -> [XCTestCaseEntry] { 5 | return [ 6 | testCase(ThreadingTests.allTests), 7 | ] 8 | } 9 | #endif -------------------------------------------------------------------------------- /Threading.xcodeproj/ThreadingTests_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | BNDL 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Threading.xcodeproj/Threading_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Threading.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXAggregateTarget section */ 10 | "Threading::ThreadingPackageTests::ProductTarget" /* ThreadingPackageTests */ = { 11 | isa = PBXAggregateTarget; 12 | buildConfigurationList = OBJ_31 /* Build configuration list for PBXAggregateTarget "ThreadingPackageTests" */; 13 | buildPhases = ( 14 | ); 15 | dependencies = ( 16 | OBJ_34 /* PBXTargetDependency */, 17 | ); 18 | name = ThreadingPackageTests; 19 | productName = ThreadingPackageTests; 20 | }; 21 | /* End PBXAggregateTarget section */ 22 | 23 | /* Begin PBXBuildFile section */ 24 | 3FB8F01020D731FA006504D4 /* ThreadedObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FB8F00F20D731FA006504D4 /* ThreadedObject.swift */; }; 25 | 3FB8F01220D74FD4006504D4 /* ThreadedDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FB8F01120D74FD4006504D4 /* ThreadedDictionary.swift */; }; 26 | 3FB8F01420D76E7E006504D4 /* ThreadedQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FB8F01320D76E7E006504D4 /* ThreadedQueue.swift */; }; 27 | 3FB8F01A20DEEAFA006504D4 /* LinkedQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FB8F01920DEEAFA006504D4 /* LinkedQueue.swift */; }; 28 | 3FD30E4F20D6309C002D8595 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FD30E4E20D6309C002D8595 /* Atomic.swift */; }; 29 | 3FD30E5120D632F7002D8595 /* LabelDispatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FD30E5020D632F7002D8595 /* LabelDispatch.swift */; }; 30 | 3FD30E5320D63555002D8595 /* ThreadingType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FD30E5220D63555002D8595 /* ThreadingType.swift */; }; 31 | 3FD30E5720D6422B002D8595 /* ThreadedArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FD30E5620D6422B002D8595 /* ThreadedArray.swift */; }; 32 | 3FD30E5920D64275002D8595 /* ThreadedCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FD30E5820D64275002D8595 /* ThreadedCollection.swift */; }; 33 | OBJ_29 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_6 /* Package.swift */; }; 34 | OBJ_40 /* ThreadingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_12 /* ThreadingTests.swift */; }; 35 | OBJ_41 /* XCTestManifests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_13 /* XCTestManifests.swift */; }; 36 | OBJ_43 /* Threading.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "Threading::Threading::Product" /* Threading.framework */; }; 37 | /* End PBXBuildFile section */ 38 | 39 | /* Begin PBXContainerItemProxy section */ 40 | 3FD30E4C20D63042002D8595 /* PBXContainerItemProxy */ = { 41 | isa = PBXContainerItemProxy; 42 | containerPortal = OBJ_1 /* Project object */; 43 | proxyType = 1; 44 | remoteGlobalIDString = "Threading::Threading"; 45 | remoteInfo = Threading; 46 | }; 47 | 3FD30E4D20D63043002D8595 /* PBXContainerItemProxy */ = { 48 | isa = PBXContainerItemProxy; 49 | containerPortal = OBJ_1 /* Project object */; 50 | proxyType = 1; 51 | remoteGlobalIDString = "Threading::ThreadingTests"; 52 | remoteInfo = ThreadingTests; 53 | }; 54 | /* End PBXContainerItemProxy section */ 55 | 56 | /* Begin PBXFileReference section */ 57 | 3FB8F00F20D731FA006504D4 /* ThreadedObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadedObject.swift; sourceTree = ""; }; 58 | 3FB8F01120D74FD4006504D4 /* ThreadedDictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadedDictionary.swift; sourceTree = ""; }; 59 | 3FB8F01320D76E7E006504D4 /* ThreadedQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadedQueue.swift; sourceTree = ""; }; 60 | 3FB8F01920DEEAFA006504D4 /* LinkedQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkedQueue.swift; sourceTree = ""; }; 61 | 3FD30E4E20D6309C002D8595 /* Atomic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Atomic.swift; sourceTree = ""; }; 62 | 3FD30E5020D632F7002D8595 /* LabelDispatch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelDispatch.swift; sourceTree = ""; }; 63 | 3FD30E5220D63555002D8595 /* ThreadingType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadingType.swift; sourceTree = ""; }; 64 | 3FD30E5620D6422B002D8595 /* ThreadedArray.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadedArray.swift; sourceTree = ""; }; 65 | 3FD30E5820D64275002D8595 /* ThreadedCollection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadedCollection.swift; sourceTree = ""; }; 66 | OBJ_12 /* ThreadingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadingTests.swift; sourceTree = ""; }; 67 | OBJ_13 /* XCTestManifests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCTestManifests.swift; sourceTree = ""; }; 68 | OBJ_6 /* Package.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; 69 | "Threading::Threading::Product" /* Threading.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Threading.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 70 | "Threading::ThreadingTests::Product" /* ThreadingTests.xctest */ = {isa = PBXFileReference; lastKnownFileType = file; path = ThreadingTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 71 | /* End PBXFileReference section */ 72 | 73 | /* Begin PBXFrameworksBuildPhase section */ 74 | OBJ_23 /* Frameworks */ = { 75 | isa = PBXFrameworksBuildPhase; 76 | buildActionMask = 0; 77 | files = ( 78 | ); 79 | runOnlyForDeploymentPostprocessing = 0; 80 | }; 81 | OBJ_42 /* Frameworks */ = { 82 | isa = PBXFrameworksBuildPhase; 83 | buildActionMask = 0; 84 | files = ( 85 | OBJ_43 /* Threading.framework in Frameworks */, 86 | ); 87 | runOnlyForDeploymentPostprocessing = 0; 88 | }; 89 | /* End PBXFrameworksBuildPhase section */ 90 | 91 | /* Begin PBXGroup section */ 92 | 3FD30E5420D641CB002D8595 /* Collections */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | 3FD30E4E20D6309C002D8595 /* Atomic.swift */, 96 | 3FD30E5620D6422B002D8595 /* ThreadedArray.swift */, 97 | 3FB8F01120D74FD4006504D4 /* ThreadedDictionary.swift */, 98 | 3FB8F01320D76E7E006504D4 /* ThreadedQueue.swift */, 99 | ); 100 | path = Collections; 101 | sourceTree = ""; 102 | }; 103 | 3FD30E5520D641E5002D8595 /* Components */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | 3FD30E5020D632F7002D8595 /* LabelDispatch.swift */, 107 | 3FD30E5220D63555002D8595 /* ThreadingType.swift */, 108 | 3FD30E5820D64275002D8595 /* ThreadedCollection.swift */, 109 | 3FB8F00F20D731FA006504D4 /* ThreadedObject.swift */, 110 | 3FB8F01920DEEAFA006504D4 /* LinkedQueue.swift */, 111 | ); 112 | path = Components; 113 | sourceTree = ""; 114 | }; 115 | OBJ_10 /* Tests */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | OBJ_11 /* ThreadingTests */, 119 | ); 120 | name = Tests; 121 | sourceTree = SOURCE_ROOT; 122 | }; 123 | OBJ_11 /* ThreadingTests */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | OBJ_12 /* ThreadingTests.swift */, 127 | OBJ_13 /* XCTestManifests.swift */, 128 | ); 129 | name = ThreadingTests; 130 | path = Tests/ThreadingTests; 131 | sourceTree = SOURCE_ROOT; 132 | }; 133 | OBJ_14 /* Products */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | "Threading::Threading::Product" /* Threading.framework */, 137 | "Threading::ThreadingTests::Product" /* ThreadingTests.xctest */, 138 | ); 139 | name = Products; 140 | sourceTree = BUILT_PRODUCTS_DIR; 141 | }; 142 | OBJ_5 = { 143 | isa = PBXGroup; 144 | children = ( 145 | OBJ_6 /* Package.swift */, 146 | OBJ_7 /* Sources */, 147 | OBJ_10 /* Tests */, 148 | OBJ_14 /* Products */, 149 | ); 150 | sourceTree = ""; 151 | }; 152 | OBJ_7 /* Sources */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | OBJ_8 /* Threading */, 156 | ); 157 | name = Sources; 158 | sourceTree = SOURCE_ROOT; 159 | }; 160 | OBJ_8 /* Threading */ = { 161 | isa = PBXGroup; 162 | children = ( 163 | 3FD30E5520D641E5002D8595 /* Components */, 164 | 3FD30E5420D641CB002D8595 /* Collections */, 165 | ); 166 | name = Threading; 167 | path = Sources/Threading; 168 | sourceTree = SOURCE_ROOT; 169 | }; 170 | /* End PBXGroup section */ 171 | 172 | /* Begin PBXNativeTarget section */ 173 | "Threading::SwiftPMPackageDescription" /* ThreadingPackageDescription */ = { 174 | isa = PBXNativeTarget; 175 | buildConfigurationList = OBJ_25 /* Build configuration list for PBXNativeTarget "ThreadingPackageDescription" */; 176 | buildPhases = ( 177 | OBJ_28 /* Sources */, 178 | ); 179 | buildRules = ( 180 | ); 181 | dependencies = ( 182 | ); 183 | name = ThreadingPackageDescription; 184 | productName = ThreadingPackageDescription; 185 | productType = "com.apple.product-type.framework"; 186 | }; 187 | "Threading::Threading" /* Threading */ = { 188 | isa = PBXNativeTarget; 189 | buildConfigurationList = OBJ_18 /* Build configuration list for PBXNativeTarget "Threading" */; 190 | buildPhases = ( 191 | OBJ_21 /* Sources */, 192 | OBJ_23 /* Frameworks */, 193 | ); 194 | buildRules = ( 195 | ); 196 | dependencies = ( 197 | ); 198 | name = Threading; 199 | productName = Threading; 200 | productReference = "Threading::Threading::Product" /* Threading.framework */; 201 | productType = "com.apple.product-type.framework"; 202 | }; 203 | "Threading::ThreadingTests" /* ThreadingTests */ = { 204 | isa = PBXNativeTarget; 205 | buildConfigurationList = OBJ_36 /* Build configuration list for PBXNativeTarget "ThreadingTests" */; 206 | buildPhases = ( 207 | OBJ_39 /* Sources */, 208 | OBJ_42 /* Frameworks */, 209 | ); 210 | buildRules = ( 211 | ); 212 | dependencies = ( 213 | OBJ_44 /* PBXTargetDependency */, 214 | ); 215 | name = ThreadingTests; 216 | productName = ThreadingTests; 217 | productReference = "Threading::ThreadingTests::Product" /* ThreadingTests.xctest */; 218 | productType = "com.apple.product-type.bundle.unit-test"; 219 | }; 220 | /* End PBXNativeTarget section */ 221 | 222 | /* Begin PBXProject section */ 223 | OBJ_1 /* Project object */ = { 224 | isa = PBXProject; 225 | attributes = { 226 | LastUpgradeCheck = 9999; 227 | }; 228 | buildConfigurationList = OBJ_2 /* Build configuration list for PBXProject "Threading" */; 229 | compatibilityVersion = "Xcode 3.2"; 230 | developmentRegion = English; 231 | hasScannedForEncodings = 0; 232 | knownRegions = ( 233 | en, 234 | ); 235 | mainGroup = OBJ_5; 236 | productRefGroup = OBJ_14 /* Products */; 237 | projectDirPath = ""; 238 | projectRoot = ""; 239 | targets = ( 240 | "Threading::Threading" /* Threading */, 241 | "Threading::SwiftPMPackageDescription" /* ThreadingPackageDescription */, 242 | "Threading::ThreadingPackageTests::ProductTarget" /* ThreadingPackageTests */, 243 | "Threading::ThreadingTests" /* ThreadingTests */, 244 | ); 245 | }; 246 | /* End PBXProject section */ 247 | 248 | /* Begin PBXSourcesBuildPhase section */ 249 | OBJ_21 /* Sources */ = { 250 | isa = PBXSourcesBuildPhase; 251 | buildActionMask = 0; 252 | files = ( 253 | 3FB8F01020D731FA006504D4 /* ThreadedObject.swift in Sources */, 254 | 3FD30E5920D64275002D8595 /* ThreadedCollection.swift in Sources */, 255 | 3FB8F01A20DEEAFA006504D4 /* LinkedQueue.swift in Sources */, 256 | 3FD30E5120D632F7002D8595 /* LabelDispatch.swift in Sources */, 257 | 3FD30E5320D63555002D8595 /* ThreadingType.swift in Sources */, 258 | 3FB8F01420D76E7E006504D4 /* ThreadedQueue.swift in Sources */, 259 | 3FD30E5720D6422B002D8595 /* ThreadedArray.swift in Sources */, 260 | 3FB8F01220D74FD4006504D4 /* ThreadedDictionary.swift in Sources */, 261 | 3FD30E4F20D6309C002D8595 /* Atomic.swift in Sources */, 262 | ); 263 | runOnlyForDeploymentPostprocessing = 0; 264 | }; 265 | OBJ_28 /* Sources */ = { 266 | isa = PBXSourcesBuildPhase; 267 | buildActionMask = 0; 268 | files = ( 269 | OBJ_29 /* Package.swift in Sources */, 270 | ); 271 | runOnlyForDeploymentPostprocessing = 0; 272 | }; 273 | OBJ_39 /* Sources */ = { 274 | isa = PBXSourcesBuildPhase; 275 | buildActionMask = 0; 276 | files = ( 277 | OBJ_40 /* ThreadingTests.swift in Sources */, 278 | OBJ_41 /* XCTestManifests.swift in Sources */, 279 | ); 280 | runOnlyForDeploymentPostprocessing = 0; 281 | }; 282 | /* End PBXSourcesBuildPhase section */ 283 | 284 | /* Begin PBXTargetDependency section */ 285 | OBJ_34 /* PBXTargetDependency */ = { 286 | isa = PBXTargetDependency; 287 | target = "Threading::ThreadingTests" /* ThreadingTests */; 288 | targetProxy = 3FD30E4D20D63043002D8595 /* PBXContainerItemProxy */; 289 | }; 290 | OBJ_44 /* PBXTargetDependency */ = { 291 | isa = PBXTargetDependency; 292 | target = "Threading::Threading" /* Threading */; 293 | targetProxy = 3FD30E4C20D63042002D8595 /* PBXContainerItemProxy */; 294 | }; 295 | /* End PBXTargetDependency section */ 296 | 297 | /* Begin XCBuildConfiguration section */ 298 | OBJ_19 /* Debug */ = { 299 | isa = XCBuildConfiguration; 300 | buildSettings = { 301 | ENABLE_TESTABILITY = YES; 302 | FRAMEWORK_SEARCH_PATHS = ( 303 | "$(inherited)", 304 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 305 | ); 306 | HEADER_SEARCH_PATHS = "$(inherited)"; 307 | INFOPLIST_FILE = Threading.xcodeproj/Threading_Info.plist; 308 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; 309 | OTHER_CFLAGS = "$(inherited)"; 310 | OTHER_LDFLAGS = "$(inherited)"; 311 | OTHER_SWIFT_FLAGS = "$(inherited)"; 312 | PRODUCT_BUNDLE_IDENTIFIER = Threading; 313 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; 314 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 315 | SKIP_INSTALL = YES; 316 | SWIFT_VERSION = 4.0; 317 | TARGET_NAME = Threading; 318 | }; 319 | name = Debug; 320 | }; 321 | OBJ_20 /* Release */ = { 322 | isa = XCBuildConfiguration; 323 | buildSettings = { 324 | ENABLE_TESTABILITY = YES; 325 | FRAMEWORK_SEARCH_PATHS = ( 326 | "$(inherited)", 327 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 328 | ); 329 | HEADER_SEARCH_PATHS = "$(inherited)"; 330 | INFOPLIST_FILE = Threading.xcodeproj/Threading_Info.plist; 331 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; 332 | OTHER_CFLAGS = "$(inherited)"; 333 | OTHER_LDFLAGS = "$(inherited)"; 334 | OTHER_SWIFT_FLAGS = "$(inherited)"; 335 | PRODUCT_BUNDLE_IDENTIFIER = Threading; 336 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; 337 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 338 | SKIP_INSTALL = YES; 339 | SWIFT_VERSION = 4.0; 340 | TARGET_NAME = Threading; 341 | }; 342 | name = Release; 343 | }; 344 | OBJ_26 /* Debug */ = { 345 | isa = XCBuildConfiguration; 346 | buildSettings = { 347 | LD = /usr/bin/true; 348 | OTHER_SWIFT_FLAGS = "-swift-version 4 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4 -target x86_64-apple-macosx10.10 -sdk /Library/Developer/CommandLineTools/SDKs/MacOSX10.13.sdk"; 349 | SWIFT_VERSION = 4.0; 350 | }; 351 | name = Debug; 352 | }; 353 | OBJ_27 /* Release */ = { 354 | isa = XCBuildConfiguration; 355 | buildSettings = { 356 | LD = /usr/bin/true; 357 | OTHER_SWIFT_FLAGS = "-swift-version 4 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4 -target x86_64-apple-macosx10.10 -sdk /Library/Developer/CommandLineTools/SDKs/MacOSX10.13.sdk"; 358 | SWIFT_VERSION = 4.0; 359 | }; 360 | name = Release; 361 | }; 362 | OBJ_3 /* Debug */ = { 363 | isa = XCBuildConfiguration; 364 | buildSettings = { 365 | CLANG_ENABLE_OBJC_ARC = YES; 366 | COMBINE_HIDPI_IMAGES = YES; 367 | COPY_PHASE_STRIP = NO; 368 | DEBUG_INFORMATION_FORMAT = dwarf; 369 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 370 | ENABLE_NS_ASSERTIONS = YES; 371 | GCC_OPTIMIZATION_LEVEL = 0; 372 | MACOSX_DEPLOYMENT_TARGET = 10.10; 373 | ONLY_ACTIVE_ARCH = YES; 374 | OTHER_SWIFT_FLAGS = "-DXcode"; 375 | PRODUCT_NAME = "$(TARGET_NAME)"; 376 | SDKROOT = macosx; 377 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; 378 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = SWIFT_PACKAGE; 379 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 380 | USE_HEADERMAP = NO; 381 | }; 382 | name = Debug; 383 | }; 384 | OBJ_32 /* Debug */ = { 385 | isa = XCBuildConfiguration; 386 | buildSettings = { 387 | }; 388 | name = Debug; 389 | }; 390 | OBJ_33 /* Release */ = { 391 | isa = XCBuildConfiguration; 392 | buildSettings = { 393 | }; 394 | name = Release; 395 | }; 396 | OBJ_37 /* Debug */ = { 397 | isa = XCBuildConfiguration; 398 | buildSettings = { 399 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; 400 | FRAMEWORK_SEARCH_PATHS = ( 401 | "$(inherited)", 402 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 403 | ); 404 | HEADER_SEARCH_PATHS = "$(inherited)"; 405 | INFOPLIST_FILE = Threading.xcodeproj/ThreadingTests_Info.plist; 406 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @loader_path/Frameworks"; 407 | OTHER_CFLAGS = "$(inherited)"; 408 | OTHER_LDFLAGS = "$(inherited)"; 409 | OTHER_SWIFT_FLAGS = "$(inherited)"; 410 | SWIFT_VERSION = 4.0; 411 | TARGET_NAME = ThreadingTests; 412 | }; 413 | name = Debug; 414 | }; 415 | OBJ_38 /* Release */ = { 416 | isa = XCBuildConfiguration; 417 | buildSettings = { 418 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; 419 | FRAMEWORK_SEARCH_PATHS = ( 420 | "$(inherited)", 421 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 422 | ); 423 | HEADER_SEARCH_PATHS = "$(inherited)"; 424 | INFOPLIST_FILE = Threading.xcodeproj/ThreadingTests_Info.plist; 425 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @loader_path/Frameworks"; 426 | OTHER_CFLAGS = "$(inherited)"; 427 | OTHER_LDFLAGS = "$(inherited)"; 428 | OTHER_SWIFT_FLAGS = "$(inherited)"; 429 | SWIFT_VERSION = 4.0; 430 | TARGET_NAME = ThreadingTests; 431 | }; 432 | name = Release; 433 | }; 434 | OBJ_4 /* Release */ = { 435 | isa = XCBuildConfiguration; 436 | buildSettings = { 437 | CLANG_ENABLE_OBJC_ARC = YES; 438 | COMBINE_HIDPI_IMAGES = YES; 439 | COPY_PHASE_STRIP = YES; 440 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 441 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 442 | GCC_OPTIMIZATION_LEVEL = s; 443 | MACOSX_DEPLOYMENT_TARGET = 10.10; 444 | OTHER_SWIFT_FLAGS = "-DXcode"; 445 | PRODUCT_NAME = "$(TARGET_NAME)"; 446 | SDKROOT = macosx; 447 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; 448 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = SWIFT_PACKAGE; 449 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 450 | USE_HEADERMAP = NO; 451 | }; 452 | name = Release; 453 | }; 454 | /* End XCBuildConfiguration section */ 455 | 456 | /* Begin XCConfigurationList section */ 457 | OBJ_18 /* Build configuration list for PBXNativeTarget "Threading" */ = { 458 | isa = XCConfigurationList; 459 | buildConfigurations = ( 460 | OBJ_19 /* Debug */, 461 | OBJ_20 /* Release */, 462 | ); 463 | defaultConfigurationIsVisible = 0; 464 | defaultConfigurationName = Release; 465 | }; 466 | OBJ_2 /* Build configuration list for PBXProject "Threading" */ = { 467 | isa = XCConfigurationList; 468 | buildConfigurations = ( 469 | OBJ_3 /* Debug */, 470 | OBJ_4 /* Release */, 471 | ); 472 | defaultConfigurationIsVisible = 0; 473 | defaultConfigurationName = Release; 474 | }; 475 | OBJ_25 /* Build configuration list for PBXNativeTarget "ThreadingPackageDescription" */ = { 476 | isa = XCConfigurationList; 477 | buildConfigurations = ( 478 | OBJ_26 /* Debug */, 479 | OBJ_27 /* Release */, 480 | ); 481 | defaultConfigurationIsVisible = 0; 482 | defaultConfigurationName = Release; 483 | }; 484 | OBJ_31 /* Build configuration list for PBXAggregateTarget "ThreadingPackageTests" */ = { 485 | isa = XCConfigurationList; 486 | buildConfigurations = ( 487 | OBJ_32 /* Debug */, 488 | OBJ_33 /* Release */, 489 | ); 490 | defaultConfigurationIsVisible = 0; 491 | defaultConfigurationName = Release; 492 | }; 493 | OBJ_36 /* Build configuration list for PBXNativeTarget "ThreadingTests" */ = { 494 | isa = XCConfigurationList; 495 | buildConfigurations = ( 496 | OBJ_37 /* Debug */, 497 | OBJ_38 /* Release */, 498 | ); 499 | defaultConfigurationIsVisible = 0; 500 | defaultConfigurationName = Release; 501 | }; 502 | /* End XCConfigurationList section */ 503 | }; 504 | rootObject = OBJ_1 /* Project object */; 505 | } 506 | -------------------------------------------------------------------------------- /Threading.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Threading.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Threading.xcodeproj/xcshareddata/xcschemes/Threading-Package.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 55 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 74 | 76 | 77 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /Threading.xcodeproj/xcshareddata/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SchemeUserState 5 | 6 | Threading-Package.xcscheme 7 | 8 | 9 | SuppressBuildableAutocreation 10 | 11 | 12 | 13 | --------------------------------------------------------------------------------