├── .gitignore ├── README.md ├── TDPipeline.h ├── TDPipelineStage.h ├── TDRunnable.h ├── TDThreadUtils.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── TDThreadUtils.xcscheme ├── include └── TDThreadUtils │ ├── TDBoundedBuffer.h │ ├── TDChannel.h │ ├── TDExchanger.h │ ├── TDGamePlayer.h │ ├── TDInterpreterSync.h │ ├── TDLinkedQueue.h │ ├── TDPipeline.h │ ├── TDPiplineStage.h │ ├── TDPool.h │ ├── TDSemaphore.h │ ├── TDSynchronousChannel.h │ ├── TDThreadUtils.h │ ├── TDThreshold.h │ ├── TDTrigger.h │ └── TDWorker.h ├── src ├── Info.plist ├── TDBoundedBuffer.m ├── TDExchanger.m ├── TDGamePlayer.m ├── TDInterpreterSync.m ├── TDLinkedQueue.m ├── TDPipeline.m ├── TDPipelineStage.m ├── TDPool.m ├── TDRunner.h ├── TDRunner.m ├── TDSemaphore.m ├── TDSynchronousChannel.m ├── TDThreshold.m └── TDTrigger.m └── tests ├── Info.plist ├── TDBaseTestCase.h ├── TDBaseTestCase.m ├── TDBoundedBufferTests.m ├── TDExchangerTests.m ├── TDGamePlayerTests.m ├── TDLinkedQueueTests.m ├── TDPoolTests.m ├── TDSemaphoreTests.m ├── TDSynchronousChannelTests.m ├── TDTest.h ├── TDTest.m ├── TDThresholdTests.m └── TDTriggerTests.m /.gitignore: -------------------------------------------------------------------------------- 1 | # CocoaPods 2 | # 3 | # We recommend against adding the Pods directory to your .gitignore. However 4 | # you should judge for yourself, the pros and cons are mentioned at: 5 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control? 6 | # 7 | # Pods/ 8 | # Xcode 9 | .DS_Store 10 | */build/* 11 | *.pbxuser 12 | !default.pbxuser 13 | *.mode1v3 14 | !default.mode1v3 15 | *.mode2v3 16 | !default.mode2v3 17 | *.perspectivev3 18 | !default.perspectivev3 19 | xcuserdata 20 | profile 21 | *.moved-aside 22 | DerivedData 23 | .idea/ 24 | *.hmap 25 | *.xccheckout 26 | #CocoaPods 27 | Pods 28 | lib/pegkit 29 | lib/TDAppKit 30 | lib/PEGKit 31 | lib/OkudaKit 32 | lib/TDTemplateEngine 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Some Cocoa Concurrency Utilities 2 | =========== 3 | 4 | * [Semaphore](https://github.com/itod/threadutils#semaphore) 5 | * [Linked Queue](https://github.com/itod/threadutils#linked-queue) 6 | * [Bounded Buffer](https://github.com/itod/threadutils#bounded-buffer) 7 | * [Synchronous Channel](https://github.com/itod/threadutils#synchronous-channel) 8 | * [Exchanger](https://github.com/itod/threadutils#exchanger) 9 | * [Pool](https://github.com/itod/threadutils#pool) 10 | * [Threshold](https://github.com/itod/threadutils#threshold) 11 | * [Trigger](https://github.com/itod/threadutils#trigger) 12 | * [Game](https://github.com/itod/threadutils#game) 13 | 14 | ---- 15 | 16 | ## Semaphore 17 | 18 | Semaphores allow you to vend a desired number of permits accross multiple threads in a thread-safe manner. 19 | 20 | For example, you may have a pool of a limited number of resources you'd like to vend accross multiple threads. A semaphore can help you do this. 21 | 22 | #### Create 23 | 24 | Create a semaphore with 7 available permits: 25 | 26 | ```objc 27 | TDSemaphore *sem = [TDSemaphore semaphoreWithValue:7]; 28 | ``` 29 | 30 | #### Acquire 31 | 32 | There are three different ways to acquire a permit on the current thread. Use one of the following according to your needs: 33 | 34 | 1. Blocks current thread (possibly forever) until one of the semaphore's permits is acquired: 35 | 36 | ```objc 37 | [sem acquire]; 38 | ``` 39 | 1. Tries to acquire one of semaphore's permits without blocking the current thread. Always returns immediately with a success indicator: 40 | 41 | ```objc 42 | BOOL success = [sem attempt]; 43 | ``` 44 | 1. Tries to acquire one of semaphore's permits for up to 10 seconds while blocking the current thread. Always returns within roughly 10 seconds with a success indicator: 45 | 46 | ```objc 47 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:10.0]; 48 | 49 | BOOL success = [sem attemptBeforeDate:date]; 50 | ``` 51 | 52 | Note that all of the above acquisition methods use signal broadcasting techniques (specifically, `NSConditionLock`). **NONE** involve any polling or busy waiting. 53 | 54 | #### Relinquish 55 | 56 | To relinquish a semaphore's permit owned by the current thread: 57 | 58 | ```objc 59 | [sem relinquish]; // always returns immediately 60 | ``` 61 | 62 | --- 63 | 64 | ## Linked Queue 65 | 66 | Linked Queues allow you to pass an unbounded number of buffered objects across one or more threads in FIFO order. Additionally, linked queues ensure that a *giver* thread will return quickly when *putting* an object in the queue (enqueueing), and you can choose whether or not you'd like a a *taker* thread to block while attempting to *take* from the queue (dequeueing). 67 | 68 | #### Create 69 | 70 | Since Linked Queues are unbounded, no size decision must be made at creation time, so creation is simple: 71 | 72 | ```objc 73 | TDLinkedQueue *q = [TDLinkedQueue linkedQueue]; 74 | ``` 75 | 76 | #### Give 77 | 78 | The *giver* thread should call `-put:`, which will insert (enqueue) the given item and return quickly. 79 | 80 | ```objc 81 | id obj = // …find an object to be given 82 | 83 | [q put:obj]; // always returns quickly without lengthy blocking 84 | ``` 85 | 86 | #### Take 87 | 88 | There are three different ways to take (dequeue) an object on the current thread. Objects are always returned in FIFO order. Use one of the following according to your needs: 89 | 90 | 1. `-take` blocks the current thread (possibly forever) until another thread has inserted an object in the queue. The result is never `nil`: 91 | 92 | ```objc 93 | id obj = [q take]; // obj is never nil 94 | ``` 95 | 1. `-poll` tries to take an object from the queue without blocking the current thread. Always returns immediately, but the result will be `nil` if the queue is empty: 96 | 97 | ```objc 98 | id obj = [q poll]; // obj may be nil 99 | ``` 100 | 1. `-takeBeforeDate:` tries to take an object from the queue for a given duration while blocking the current thread. Always returns within roughly the duration given, but the result will be `nil` if no object is available for dequeueing during that time: 101 | 102 | ```objc 103 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:10.0]; 104 | 105 | id obj = [q takeBeforeDate:date]; // obj may be nil 106 | ``` 107 | 108 | Note that the `-put:` and `-take` methods use signal broadcasting techniques (specifically, `NSConditionLock`). They **DO NOT** involve any polling or busy waiting. 109 | 110 | --- 111 | 112 | ## Bounded Buffer 113 | 114 | Bounded buffers allow you to pass a given number of buffered objects across one or more threads in FIFO order. Additionally, bounded buffers ensure that a *giver* thread will block when attempting to *give* to the buffer while full, and a *taker* thread will block while attempting to *take* from the buffer while empty. 115 | 116 | #### Create 117 | 118 | Create a bounded buffer with the desired buffer size: 119 | 120 | ```objc 121 | TDBoundedBuffer *buff = [TDBoundedBuffer boundedBufferWithSize:4]; 122 | ``` 123 | 124 | #### Give 125 | 126 | The *giver* thread should call `-put:`, which will either: 127 | 128 | * insert the given item and return immediately if the buffer currently contains 3 or fewer items. 129 | 130 | OR 131 | 132 | * if the buffer contains 4 items, the current *giver* thread will block until an item is extracted by another thread. 133 | 134 | ```objc 135 | // on "giver" thread 136 | 137 | id obj = // …find an object to be given 138 | 139 | [buff put:obj]; // blocks while buffer is full, otherwise returns immediately after "putting". 140 | ``` 141 | 142 | #### Take 143 | 144 | The *taker* thread should call `-take`, which will either: 145 | 146 | * extract and return an item immediately if the buffer currently contains 1 or more items. 147 | 148 | OR 149 | 150 | * if the buffer is empty, the current *taker* thread will block until an item is inserted by another thread. 151 | 152 | ```objc 153 | // on "taker" thread 154 | 155 | id obj = [buff take]; // blocks while buffer is empty, otherwise returns an item immediately 156 | ``` 157 | 158 | Note that the `-put:` and `-take` methods use signal broadcasting techniques (specifically, `NSConditionLock`). They **DO NOT** involve any polling or busy waiting. 159 | 160 | --- 161 | 162 | ## Synchronous Channel 163 | 164 | A synchronous channel is like a bounded buffer with a capacity of `0`. Synchronous channels allow two threads to rendezvous while one thread passes an object to the other. If the *giver* thread arrives at the rendezvous point first, the giver thread will block until the *taker* thread arrives, and has taken the object being given. Alternatively, if the *taker* thread arrives at the rendezvous first, it blocks until the *giver* thread has arrived and given an object to be taken. 165 | 166 | So if you have a thread which cannot continue execution until it is guaranteed to have successfully *passed an object* to another thread, a synchronous channel can help. 167 | 168 | #### Create 169 | 170 | Create a synchronous channel: 171 | 172 | ```objc 173 | TDSynchronousChannel *chan = [TDSynchronousChannel synchronousChannel]; 174 | ``` 175 | 176 | #### Give 177 | 178 | The *giver* thread should call `-put:`, which will block until another thread has successfully taken the object: 179 | 180 | ```objc 181 | // on "giver" thread 182 | 183 | id obj = // …find an object to be given 184 | 185 | [chan put:obj]; // blocks until `obj` taken by another thread 186 | ``` 187 | 188 | #### Take 189 | 190 | The *taker* thread should call `-take`, which will block until another thread has given an object to be taken: 191 | 192 | ```objc 193 | // on "taker" thread 194 | 195 | id obj = [chan take]; // blocks until another thread "gives" 196 | ``` 197 | 198 | Note that the order in which these two threads "arrive" at the rendezvous (that is, the order they call `-put:` or `-take`) does not matter. Indeed, across threads it can be difficult to define execution "order" at all. Neither thread will continue beyond the rendezvous point until the object has been successfully taken. 199 | 200 | Note that the `-put:` and `-take` methods use signal broadcasting techniques (specifically, `NSConditionLock`). They **DO NOT** involve any polling or busy waiting. 201 | 202 | --- 203 | 204 | ## Exchanger 205 | 206 | An exchanger is a lot like a synchronous channel, except that instead of passing a single object from one thread to another (like relay racers passing a baton), an exchanger allows two threads to rendezvous and swap two objects (like a hostage taker swapping a hostage for ransom). 207 | 208 | One thread arrives at the rendezvous point first, gives its "offer" object, and blocks until a second thread arrives to give its own "offer". The objects are swapped, each thread receives its "reward" object and continues execution. 209 | 210 | So if you have two threads which cannot continue execution until they are guaranteed to have successfully *swapped objects*, an exchanger can help. 211 | 212 | #### Create 213 | 214 | Create an exchanger: 215 | 216 | ```objc 217 | TDExchanger *ex = [TDExchanger exchanger]; 218 | ``` 219 | 220 | #### Swap 221 | 222 | Each thread should call `-exchange:`, which will block until another thread has also arrived and successfully swapped the given objects: 223 | 224 | ```objc 225 | // the code is the same on both threads 226 | 227 | id offer = // …find an object to be swapped 228 | 229 | id reward = [ex exchange:offer]; // blocks until `offer` taken by another thread 230 | ``` 231 | 232 | Note that the `-exchange:` method uses signal broadcasting techniques (specifically, `NSConditionLock`). It **DOES NOT** involve any polling or busy waiting. 233 | 234 | --- 235 | 236 | ## Pool 237 | 238 | A pool maintains a limited collection of resource items that clients can check out and later check back in. Pools rely on a private semaphore for their counting, but provide a higher-level API for vending a limited number of resource objects across multiple threads in a thread-safe manner. 239 | 240 | This pool implementation ensures one additional integrity constraint: only objects checked out a from a given pool may be checked back into that same pool. 241 | 242 | #### Create 243 | 244 | Create a pool by providing it an array of resource items to maintain: 245 | 246 | ```objc 247 | NSUInteger numItems = … // size of pool 248 | id items = [NSMutableArray arrayWithCapacity:numItems]; 249 | 250 | for (NSUInteger i = 0; i < numItems; ++i) 251 | [items addObject:[MyResource new]]; 252 | 253 | TDPool *pool = [TDPool poolWithItems:items]; 254 | ``` 255 | 256 | #### Check Out 257 | 258 | Any thread may attempt to *check out* an object from the pool by calling `-takeItem:`. This method will block the current thread until a resource item is available: 259 | 260 | ```objc 261 | id item = [pool takeItem]; // blocks until item is available 262 | 263 | … // use item 264 | ``` 265 | 266 | #### Check In 267 | 268 | Any item checked out of a pool should later be *checked back in* by passing it to `-returnItem:`. This method will always return immediately. The item need not be checked back in on the same thread. 269 | 270 | ```objc 271 | … // finish using item 272 | 273 | [pool returnItem:item]; // returns immediately 274 | ``` 275 | 276 | If any other threads were blocked while waiting to check out a resource item, one of those threads will be unblocked and returned this item. 277 | 278 | #### Safety 279 | 280 | For the sake of safety, it may be best to wrap the item usage and check in inside of a try/finally block: 281 | 282 | ```objc 283 | id item = [pool takeItem]; 284 | @try { use(item); } 285 | @finally { [pool returnItem:item]; } 286 | ``` 287 | 288 | --- 289 | 290 | ## Threshold 291 | 292 | A threshold is a way to block multiple threads until a threshold is met. 293 | 294 | A threshold object will block any thread that calls its `-await` method until a given number of threads have have done so. At that point, the threshold is met, and all waiting threads will be unblocked and allowed to continue simultaneously. 295 | 296 | A threshold is useful for designs which call for a specific number of independent actors or tasks (represented as threads) with no centralized mediator or controller. Using a threshold, these threads can be created and blocked until the exact moment when the desired number have been initialized and are waiting. Then, they all continue simultaneously. 297 | 298 | #### Create 299 | 300 | Usage is simple. Create a threshold with the desired limit: 301 | 302 | ```objc 303 | TDThreshold *th = [TDThreshold thresholdWithValue:4]; 304 | ``` 305 | 306 | #### Await 307 | 308 | On any thread which should block until the threshold limit is reached, call `-await`. 309 | 310 | ```objc 311 | [th await]; // blocks until threshold limit is reached 312 | ``` 313 | 314 | In this example, when `-await` is called for the fourth time (on the fourth thread), the three waiting threads, and the current fourth thread will all unblock and continue execution "simultaneously" (note the precise meaning of "simultaneously" is dependent on the number of cores on your device's processor and the thread scheduling behavior of your operating system). 315 | 316 | Note that the `-await` method uses signal broadcasting techniques (specifically, `NSConditionLock`). It **DOES NOT** involve any polling or busy waiting. 317 | 318 | --- 319 | 320 | ## Trigger 321 | 322 | A trigger is a way to block multiple threads until a *fire* signal is explicitly sent by a controller thread. 323 | 324 | Triggers are very similar to thresholds, but are approriate for designs that call for a controller or mediator to unblock all waiting threads in response to a specific condition explicitly coded by the programmer. 325 | 326 | #### Create 327 | 328 | ```objc 329 | TDTrigger *trig = [TDTrigger trigger]; 330 | ``` 331 | 332 | #### Await 333 | 334 | On any thread you wish to block, call `-await`: 335 | 336 | ```objc 337 | [trig await]; // blocks until "fire" signal sent by controller 338 | ``` 339 | 340 | This will block the current thread until the *fire* signal is sent by some other controlling thread. 341 | 342 | #### Fire 343 | 344 | When your application is ready for all waiting threads to proceed with execution, *fire* the trigger on an unblocked controlling thread: 345 | 346 | ```objc 347 | [trig fire]; // unblocks all threads waiting on this trigger 348 | ``` 349 | 350 | All threads that were waiting on this trigger will unblock and proceed simultaneously. 351 | 352 | Note that the `-await` and `-fire` methods use signal broadcasting techniques (specifically, `NSConditionLock`). They **DO NOT** involve any polling or busy waiting. 353 | 354 | --- 355 | 356 | ## Game 357 | 358 | A game is a way to allow exactly two threads to take turns running exclusively with respect to one another. 359 | 360 | While thread A takes its "turn", thread B is awaiting its "turn" in a paused state. After thread A completes its "turn", it pauses while thread B takes its "turn". And so forth. This exclusive turn-taking continues indefinitely until the user stops the game. 361 | 362 | Games are useful for implementing anything like a language interpreter with an interactive debugger. When such an interpreter is running, one of two threads (the code execution thread or the user debug command input thread) must be taking its turn running while the other thread is paused awaiting its turn. 363 | 364 | #### Create 365 | 366 | To set up a game, create exactly two players, and set them as each other's opponent. Also, provide each player with a delegate which executes your "turn" logic by adopting the `TDGamePlayerDelegate` protocol and implementing the `-gamePlayer:doTurnWithInput:` method. This could be either a single delegate object, or two separate objects. 367 | 368 | ```objc 369 | id del = … // implements "move" logic for each player 370 | 371 | id p1 = [[TDGamePlayer alloc] initWithDelegate:del]; 372 | id p2 = [[TDGamePlayer alloc] initWithDelegate:del]; 373 | 374 | p1.opponent = p2; 375 | p2.opponent = p1; 376 | ``` 377 | 378 | #### Run 379 | 380 | To begin the game, designate exactly one player as the first turn taker, and optionally provide an input argument. Then run each player on a separate background thread of your choosing. Each of your two player delegates will receive repeated calls to execute their "moves" on their own thread while the other player's thread is paused. 381 | 382 | ```objc 383 | [p1 giveFirstTurnWithInput:nil]; 384 | 385 | performOnNewBackgroundThread(^{ 386 | [p1 run]; 387 | }); 388 | performOnNewBackgroundThread(^{ 389 | [p2 run]; 390 | }); 391 | ``` 392 | 393 | #### Stop 394 | 395 | Stopping a game is easy. Call each player's `-stop` method from any thread. 396 | 397 | ```objc 398 | [p1 stop]; 399 | [p2 stop]; 400 | ``` 401 | 402 | -------------------------------------------------------------------------------- /TDPipeline.h: -------------------------------------------------------------------------------- 1 | // 2 | // TDPipeline.h 3 | // TDThreadUtils 4 | // 5 | // Created by Todd Ditchendorf on 4/14/25. 6 | // Copyright © 2025 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @protocol TDChannel; 13 | @class TDPipeline; 14 | 15 | @protocol TDPipelineDelegate 16 | // this will always be called on main thread 17 | - (void)pipelineProgressDidUpdate:(TDPipeline *)p; 18 | @end 19 | 20 | @protocol TDLauncher 21 | - (void)launchWithPipeline:(TDPipeline *)p outputChannel:(id )channel; 22 | @end 23 | 24 | @protocol TDReceiver 25 | - (void)receiveWithPipeline:(TDPipeline *)p inputChannel:(id )channel; 26 | @end 27 | 28 | @interface TDPipeline : NSObject 29 | 30 | + (TDPipeline *)pipleineWithLauncher:(id )l receiver:(id )r stages:(NSArray *)stages; 31 | - (instancetype)initWithLauncher:(id )l receiver:(id )r stages:(NSArray *)stages; 32 | 33 | @property (nonatomic, retain, readonly) id launcher; 34 | @property (nonatomic, retain, readonly) id receiver; 35 | @property (nonatomic, copy, readonly) NSArray *stages; 36 | 37 | @property (nonatomic, assign) id delegate; 38 | 39 | @property (nonatomic, assign) double launcherProgress; 40 | @property (nonatomic, assign) double receiverProgress; 41 | 42 | - (BOOL)runWithError:(NSError **)outErr; 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /TDPipelineStage.h: -------------------------------------------------------------------------------- 1 | // 2 | // TDPipelineStage.h 3 | // TDThreadUtils 4 | // 5 | // Created by Todd Ditchendorf on 4/14/25. 6 | // Copyright © 2025 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef NS_ENUM(NSUInteger, TDPipelineStageType) { 12 | TDPipelineStageTypeDeterminate = 0, 13 | TDPipelineStageTypeIndeterminate = 1, 14 | }; 15 | 16 | @protocol TDChannel; 17 | @class TDPipelineStage; 18 | @class TDRunner; 19 | 20 | @protocol TDPipelineStageDelegate 21 | - (void)pipelineStageProgressDidUpdate:(TDPipelineStage *)ps; 22 | @end 23 | 24 | @interface TDPipelineStage : NSObject 25 | 26 | + (TDPipelineStage *)pipelineStageWithType:(TDPipelineStageType)type runnableClass:(Class)cls runnerCount:(NSUInteger)c; 27 | - (instancetype)initWithType:(TDPipelineStageType)type runnableClass:(Class)cls runnerCount:(NSUInteger)c; 28 | 29 | @property (nonatomic, assign, readonly) TDPipelineStageType type; 30 | @property (nonatomic, retain, readonly) Class workerClass; 31 | 32 | @property (nonatomic, assign, readonly) NSUInteger runnerCount; 33 | @property (nonatomic, copy, readonly) NSArray *runners; 34 | 35 | @property (nonatomic, retain, readonly) id inputChannel; 36 | @property (nonatomic, retain, readonly) id outputChannel; 37 | 38 | @property (nonatomic, assign) id delegate; 39 | 40 | @end 41 | 42 | -------------------------------------------------------------------------------- /TDRunnable.h: -------------------------------------------------------------------------------- 1 | // 2 | // TDWorker.h 3 | // TDThreadUtils 4 | // 5 | // Created by Todd Ditchendorf on 4/14/25. 6 | // Copyright © 2025 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol TDRunnable; 12 | 13 | @protocol TDRunnableDelegate 14 | - (void)runnable:(id )r updateProgress:(double)d; 15 | - (void)runnable:(id )r updateTitleText:(NSString *)title infoText:(NSString *)info; 16 | @end 17 | 18 | @protocol TDRunnable 19 | 20 | - (id)runWithInput:(id)input error:(NSError **)outErr; 21 | 22 | @property (nonatomic, assign) id delegate; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /TDThreadUtils.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 54; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | D33849E91D67752100087F7D /* TDExchanger.h in Headers */ = {isa = PBXBuildFile; fileRef = D33849E81D67752100087F7D /* TDExchanger.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | D33849EB1D67752D00087F7D /* TDExchanger.m in Sources */ = {isa = PBXBuildFile; fileRef = D33849EA1D67752D00087F7D /* TDExchanger.m */; }; 12 | D33849ED1D67786800087F7D /* TDExchangerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D33849EC1D67786800087F7D /* TDExchangerTests.m */; }; 13 | D34700C41B0ED4DE0099D78F /* TDPool.m in Sources */ = {isa = PBXBuildFile; fileRef = D34700C21B0ED4DE0099D78F /* TDPool.m */; }; 14 | D34700C61B0ED4EA0099D78F /* TDPool.h in Headers */ = {isa = PBXBuildFile; fileRef = D34700C51B0ED4EA0099D78F /* TDPool.h */; settings = {ATTRIBUTES = (Public, ); }; }; 15 | D34700C91B0EDBF60099D78F /* TDPoolTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D34700C71B0ED9CC0099D78F /* TDPoolTests.m */; }; 16 | D36CB44720ED3F060029669D /* TDGamePlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = D36CB44520ED3F060029669D /* TDGamePlayer.m */; }; 17 | D36CB44B20ED48BB0029669D /* TDGamePlayerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D36CB44A20ED48BB0029669D /* TDGamePlayerTests.m */; }; 18 | D36CB45420ED64260029669D /* TDGamePlayer.h in Headers */ = {isa = PBXBuildFile; fileRef = D36CB45320ED64260029669D /* TDGamePlayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 19 | D37B0BCB2DAD78E600795C50 /* TDPipeline.m in Sources */ = {isa = PBXBuildFile; fileRef = D37B0BCA2DAD78E600795C50 /* TDPipeline.m */; }; 20 | D37B0BCF2DAD78F900795C50 /* TDPipelineStage.m in Sources */ = {isa = PBXBuildFile; fileRef = D37B0BCE2DAD78F900795C50 /* TDPipelineStage.m */; }; 21 | D37B0BD82DAD792400795C50 /* TDPipeline.h in Headers */ = {isa = PBXBuildFile; fileRef = D37B0BD52DAD792400795C50 /* TDPipeline.h */; settings = {ATTRIBUTES = (Public, ); }; }; 22 | D37B0BD92DAD792400795C50 /* TDRunnable.h in Headers */ = {isa = PBXBuildFile; fileRef = D37B0BD72DAD792400795C50 /* TDRunnable.h */; settings = {ATTRIBUTES = (Public, ); }; }; 23 | D37B0BDA2DAD792400795C50 /* TDPipelineStage.h in Headers */ = {isa = PBXBuildFile; fileRef = D37B0BD62DAD792400795C50 /* TDPipelineStage.h */; settings = {ATTRIBUTES = (Public, ); }; }; 24 | D37B0BDD2DADC23300795C50 /* TDRunner.m in Sources */ = {isa = PBXBuildFile; fileRef = D37B0BDC2DADC23300795C50 /* TDRunner.m */; }; 25 | D37B0BDE2DADC23300795C50 /* TDRunner.h in Headers */ = {isa = PBXBuildFile; fileRef = D37B0BDB2DADC23300795C50 /* TDRunner.h */; settings = {ATTRIBUTES = (Public, ); }; }; 26 | D386934F1F9C2D2400928048 /* TDLinkedQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = D386934D1F9C2D2400928048 /* TDLinkedQueue.m */; }; 27 | D38693511F9C2DFF00928048 /* TDLinkedQueueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D38693501F9C2D5F00928048 /* TDLinkedQueueTests.m */; }; 28 | D38693531F9C2E3200928048 /* TDLinkedQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = D38693521F9C2E2800928048 /* TDLinkedQueue.h */; settings = {ATTRIBUTES = (Public, ); }; }; 29 | D3AF43AE1A658078005C309E /* TDBoundedBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = D3AF43A61A658078005C309E /* TDBoundedBuffer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 30 | D3AF43AF1A658078005C309E /* TDChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D3AF43A71A658078005C309E /* TDChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; 31 | D3AF43B01A658078005C309E /* TDInterpreterSync.h in Headers */ = {isa = PBXBuildFile; fileRef = D3AF43A81A658078005C309E /* TDInterpreterSync.h */; settings = {ATTRIBUTES = (Public, ); }; }; 32 | D3AF43B11A658078005C309E /* TDSemaphore.h in Headers */ = {isa = PBXBuildFile; fileRef = D3AF43A91A658078005C309E /* TDSemaphore.h */; settings = {ATTRIBUTES = (Public, ); }; }; 33 | D3AF43B31A658078005C309E /* TDSynchronousChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = D3AF43AB1A658078005C309E /* TDSynchronousChannel.h */; settings = {ATTRIBUTES = (Public, ); }; }; 34 | D3AF43B41A658078005C309E /* TDThreadUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = D3AF43AC1A658078005C309E /* TDThreadUtils.h */; settings = {ATTRIBUTES = (Public, ); }; }; 35 | D3AF43B51A658078005C309E /* TDThreshold.h in Headers */ = {isa = PBXBuildFile; fileRef = D3AF43AD1A658078005C309E /* TDThreshold.h */; settings = {ATTRIBUTES = (Public, ); }; }; 36 | D3AF43BB1A65809A005C309E /* TDBoundedBuffer.m in Sources */ = {isa = PBXBuildFile; fileRef = D3AF43B61A65809A005C309E /* TDBoundedBuffer.m */; }; 37 | D3AF43BC1A65809A005C309E /* TDInterpreterSync.m in Sources */ = {isa = PBXBuildFile; fileRef = D3AF43B71A65809A005C309E /* TDInterpreterSync.m */; }; 38 | D3AF43BD1A65809A005C309E /* TDSemaphore.m in Sources */ = {isa = PBXBuildFile; fileRef = D3AF43B81A65809A005C309E /* TDSemaphore.m */; }; 39 | D3AF43BE1A65809A005C309E /* TDSynchronousChannel.m in Sources */ = {isa = PBXBuildFile; fileRef = D3AF43B91A65809A005C309E /* TDSynchronousChannel.m */; }; 40 | D3AF43BF1A65809A005C309E /* TDThreshold.m in Sources */ = {isa = PBXBuildFile; fileRef = D3AF43BA1A65809A005C309E /* TDThreshold.m */; }; 41 | D3AF43C81A65811F005C309E /* TDBaseTestCase.h in Headers */ = {isa = PBXBuildFile; fileRef = D3AF43C01A65811F005C309E /* TDBaseTestCase.h */; }; 42 | D3AF43CD1A65811F005C309E /* TDTest.h in Headers */ = {isa = PBXBuildFile; fileRef = D3AF43C51A65811F005C309E /* TDTest.h */; }; 43 | D3AF43D01A658166005C309E /* TDTest.m in Sources */ = {isa = PBXBuildFile; fileRef = D3AF43C61A65811F005C309E /* TDTest.m */; }; 44 | D3AF43D11A658166005C309E /* TDBaseTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = D3AF43C11A65811F005C309E /* TDBaseTestCase.m */; }; 45 | D3AF43D21A658166005C309E /* TDSemaphoreTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D3AF43C31A65811F005C309E /* TDSemaphoreTests.m */; }; 46 | D3AF43D31A658166005C309E /* TDBoundedBufferTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D3AF43C21A65811F005C309E /* TDBoundedBufferTests.m */; }; 47 | D3AF43D41A658166005C309E /* TDSynchronousChannelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D3AF43C41A65811F005C309E /* TDSynchronousChannelTests.m */; }; 48 | D3AF43D51A658166005C309E /* TDThresholdTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D3AF43C71A65811F005C309E /* TDThresholdTests.m */; }; 49 | D3AF43E51A659AFE005C309E /* TDTrigger.m in Sources */ = {isa = PBXBuildFile; fileRef = D3AF43E41A659AFE005C309E /* TDTrigger.m */; }; 50 | D3AF43E71A659B01005C309E /* TDTrigger.h in Headers */ = {isa = PBXBuildFile; fileRef = D3AF43E61A659B01005C309E /* TDTrigger.h */; settings = {ATTRIBUTES = (Public, ); }; }; 51 | D3AF43E91A659BEA005C309E /* TDTriggerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D3AF43E81A659BEA005C309E /* TDTriggerTests.m */; }; 52 | D3C0C74D1A641527004C8B9A /* TDThreadUtils.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D3C0C7411A641526004C8B9A /* TDThreadUtils.framework */; }; 53 | /* End PBXBuildFile section */ 54 | 55 | /* Begin PBXContainerItemProxy section */ 56 | D3C0C74E1A641527004C8B9A /* PBXContainerItemProxy */ = { 57 | isa = PBXContainerItemProxy; 58 | containerPortal = D3C0C7381A641526004C8B9A /* Project object */; 59 | proxyType = 1; 60 | remoteGlobalIDString = D3C0C7401A641526004C8B9A; 61 | remoteInfo = TDThreadUtils; 62 | }; 63 | /* End PBXContainerItemProxy section */ 64 | 65 | /* Begin PBXFileReference section */ 66 | D33849E81D67752100087F7D /* TDExchanger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TDExchanger.h; path = include/TDThreadUtils/TDExchanger.h; sourceTree = SOURCE_ROOT; }; 67 | D33849EA1D67752D00087F7D /* TDExchanger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TDExchanger.m; path = src/TDExchanger.m; sourceTree = SOURCE_ROOT; }; 68 | D33849EC1D67786800087F7D /* TDExchangerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TDExchangerTests.m; path = tests/TDExchangerTests.m; sourceTree = SOURCE_ROOT; }; 69 | D34700C21B0ED4DE0099D78F /* TDPool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TDPool.m; path = src/TDPool.m; sourceTree = SOURCE_ROOT; }; 70 | D34700C51B0ED4EA0099D78F /* TDPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TDPool.h; path = include/TDThreadUtils/TDPool.h; sourceTree = SOURCE_ROOT; }; 71 | D34700C71B0ED9CC0099D78F /* TDPoolTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TDPoolTests.m; path = tests/TDPoolTests.m; sourceTree = SOURCE_ROOT; }; 72 | D36CB44520ED3F060029669D /* TDGamePlayer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = TDGamePlayer.m; path = src/TDGamePlayer.m; sourceTree = ""; }; 73 | D36CB44A20ED48BB0029669D /* TDGamePlayerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TDGamePlayerTests.m; path = tests/TDGamePlayerTests.m; sourceTree = ""; }; 74 | D36CB45320ED64260029669D /* TDGamePlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TDGamePlayer.h; path = include/TDThreadUtils/TDGamePlayer.h; sourceTree = ""; }; 75 | D37B0BCA2DAD78E600795C50 /* TDPipeline.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = TDPipeline.m; path = src/TDPipeline.m; sourceTree = ""; }; 76 | D37B0BCE2DAD78F900795C50 /* TDPipelineStage.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = TDPipelineStage.m; path = src/TDPipelineStage.m; sourceTree = ""; }; 77 | D37B0BD52DAD792400795C50 /* TDPipeline.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TDPipeline.h; sourceTree = ""; }; 78 | D37B0BD62DAD792400795C50 /* TDPipelineStage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TDPipelineStage.h; sourceTree = ""; }; 79 | D37B0BD72DAD792400795C50 /* TDRunnable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TDRunnable.h; sourceTree = ""; }; 80 | D37B0BDB2DADC23300795C50 /* TDRunner.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TDRunner.h; path = src/TDRunner.h; sourceTree = ""; }; 81 | D37B0BDC2DADC23300795C50 /* TDRunner.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = TDRunner.m; path = src/TDRunner.m; sourceTree = ""; }; 82 | D386934D1F9C2D2400928048 /* TDLinkedQueue.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = TDLinkedQueue.m; path = src/TDLinkedQueue.m; sourceTree = ""; }; 83 | D38693501F9C2D5F00928048 /* TDLinkedQueueTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = TDLinkedQueueTests.m; path = tests/TDLinkedQueueTests.m; sourceTree = ""; }; 84 | D38693521F9C2E2800928048 /* TDLinkedQueue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TDLinkedQueue.h; path = include/TDThreadUtils/TDLinkedQueue.h; sourceTree = ""; }; 85 | D3AF43A61A658078005C309E /* TDBoundedBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TDBoundedBuffer.h; path = include/TDThreadUtils/TDBoundedBuffer.h; sourceTree = SOURCE_ROOT; }; 86 | D3AF43A71A658078005C309E /* TDChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TDChannel.h; path = include/TDThreadUtils/TDChannel.h; sourceTree = SOURCE_ROOT; }; 87 | D3AF43A81A658078005C309E /* TDInterpreterSync.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TDInterpreterSync.h; path = include/TDThreadUtils/TDInterpreterSync.h; sourceTree = SOURCE_ROOT; }; 88 | D3AF43A91A658078005C309E /* TDSemaphore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TDSemaphore.h; path = include/TDThreadUtils/TDSemaphore.h; sourceTree = SOURCE_ROOT; }; 89 | D3AF43AB1A658078005C309E /* TDSynchronousChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TDSynchronousChannel.h; path = include/TDThreadUtils/TDSynchronousChannel.h; sourceTree = SOURCE_ROOT; }; 90 | D3AF43AC1A658078005C309E /* TDThreadUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TDThreadUtils.h; path = include/TDThreadUtils/TDThreadUtils.h; sourceTree = SOURCE_ROOT; }; 91 | D3AF43AD1A658078005C309E /* TDThreshold.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TDThreshold.h; path = include/TDThreadUtils/TDThreshold.h; sourceTree = SOURCE_ROOT; }; 92 | D3AF43B61A65809A005C309E /* TDBoundedBuffer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TDBoundedBuffer.m; path = src/TDBoundedBuffer.m; sourceTree = SOURCE_ROOT; }; 93 | D3AF43B71A65809A005C309E /* TDInterpreterSync.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TDInterpreterSync.m; path = src/TDInterpreterSync.m; sourceTree = SOURCE_ROOT; }; 94 | D3AF43B81A65809A005C309E /* TDSemaphore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TDSemaphore.m; path = src/TDSemaphore.m; sourceTree = SOURCE_ROOT; }; 95 | D3AF43B91A65809A005C309E /* TDSynchronousChannel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TDSynchronousChannel.m; path = src/TDSynchronousChannel.m; sourceTree = SOURCE_ROOT; }; 96 | D3AF43BA1A65809A005C309E /* TDThreshold.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TDThreshold.m; path = src/TDThreshold.m; sourceTree = SOURCE_ROOT; }; 97 | D3AF43C01A65811F005C309E /* TDBaseTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TDBaseTestCase.h; path = tests/TDBaseTestCase.h; sourceTree = SOURCE_ROOT; }; 98 | D3AF43C11A65811F005C309E /* TDBaseTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TDBaseTestCase.m; path = tests/TDBaseTestCase.m; sourceTree = SOURCE_ROOT; }; 99 | D3AF43C21A65811F005C309E /* TDBoundedBufferTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TDBoundedBufferTests.m; path = tests/TDBoundedBufferTests.m; sourceTree = SOURCE_ROOT; }; 100 | D3AF43C31A65811F005C309E /* TDSemaphoreTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TDSemaphoreTests.m; path = tests/TDSemaphoreTests.m; sourceTree = SOURCE_ROOT; }; 101 | D3AF43C41A65811F005C309E /* TDSynchronousChannelTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TDSynchronousChannelTests.m; path = tests/TDSynchronousChannelTests.m; sourceTree = SOURCE_ROOT; }; 102 | D3AF43C51A65811F005C309E /* TDTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TDTest.h; path = tests/TDTest.h; sourceTree = SOURCE_ROOT; }; 103 | D3AF43C61A65811F005C309E /* TDTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TDTest.m; path = tests/TDTest.m; sourceTree = SOURCE_ROOT; }; 104 | D3AF43C71A65811F005C309E /* TDThresholdTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TDThresholdTests.m; path = tests/TDThresholdTests.m; sourceTree = SOURCE_ROOT; }; 105 | D3AF43E41A659AFE005C309E /* TDTrigger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TDTrigger.m; path = src/TDTrigger.m; sourceTree = SOURCE_ROOT; }; 106 | D3AF43E61A659B01005C309E /* TDTrigger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TDTrigger.h; path = include/TDThreadUtils/TDTrigger.h; sourceTree = SOURCE_ROOT; }; 107 | D3AF43E81A659BEA005C309E /* TDTriggerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TDTriggerTests.m; path = tests/TDTriggerTests.m; sourceTree = SOURCE_ROOT; }; 108 | D3C0C7411A641526004C8B9A /* TDThreadUtils.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TDThreadUtils.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 109 | D3C0C7451A641527004C8B9A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = src/Info.plist; sourceTree = ""; }; 110 | D3C0C74C1A641527004C8B9A /* TDThreadUtilsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TDThreadUtilsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 111 | D3C0C7521A641528004C8B9A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = tests/Info.plist; sourceTree = ""; }; 112 | /* End PBXFileReference section */ 113 | 114 | /* Begin PBXFrameworksBuildPhase section */ 115 | D3C0C73D1A641526004C8B9A /* Frameworks */ = { 116 | isa = PBXFrameworksBuildPhase; 117 | buildActionMask = 2147483647; 118 | files = ( 119 | ); 120 | runOnlyForDeploymentPostprocessing = 0; 121 | }; 122 | D3C0C7491A641527004C8B9A /* Frameworks */ = { 123 | isa = PBXFrameworksBuildPhase; 124 | buildActionMask = 2147483647; 125 | files = ( 126 | D3C0C74D1A641527004C8B9A /* TDThreadUtils.framework in Frameworks */, 127 | ); 128 | runOnlyForDeploymentPostprocessing = 0; 129 | }; 130 | /* End PBXFrameworksBuildPhase section */ 131 | 132 | /* Begin PBXGroup section */ 133 | D3C0C7371A641526004C8B9A = { 134 | isa = PBXGroup; 135 | children = ( 136 | D3C0C7431A641526004C8B9A /* TDThreadUtils */, 137 | D3C0C7501A641527004C8B9A /* TDThreadUtilsTests */, 138 | D3C0C7421A641526004C8B9A /* Products */, 139 | ); 140 | sourceTree = ""; 141 | }; 142 | D3C0C7421A641526004C8B9A /* Products */ = { 143 | isa = PBXGroup; 144 | children = ( 145 | D3C0C7411A641526004C8B9A /* TDThreadUtils.framework */, 146 | D3C0C74C1A641527004C8B9A /* TDThreadUtilsTests.xctest */, 147 | ); 148 | name = Products; 149 | sourceTree = ""; 150 | }; 151 | D3C0C7431A641526004C8B9A /* TDThreadUtils */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | D3AF43AC1A658078005C309E /* TDThreadUtils.h */, 155 | D3AF43A91A658078005C309E /* TDSemaphore.h */, 156 | D3AF43B81A65809A005C309E /* TDSemaphore.m */, 157 | D3AF43A71A658078005C309E /* TDChannel.h */, 158 | D3AF43A61A658078005C309E /* TDBoundedBuffer.h */, 159 | D3AF43B61A65809A005C309E /* TDBoundedBuffer.m */, 160 | D38693521F9C2E2800928048 /* TDLinkedQueue.h */, 161 | D386934D1F9C2D2400928048 /* TDLinkedQueue.m */, 162 | D3AF43AB1A658078005C309E /* TDSynchronousChannel.h */, 163 | D3AF43B91A65809A005C309E /* TDSynchronousChannel.m */, 164 | D33849E81D67752100087F7D /* TDExchanger.h */, 165 | D33849EA1D67752D00087F7D /* TDExchanger.m */, 166 | D3AF43E61A659B01005C309E /* TDTrigger.h */, 167 | D3AF43E41A659AFE005C309E /* TDTrigger.m */, 168 | D3AF43AD1A658078005C309E /* TDThreshold.h */, 169 | D3AF43BA1A65809A005C309E /* TDThreshold.m */, 170 | D34700C51B0ED4EA0099D78F /* TDPool.h */, 171 | D34700C21B0ED4DE0099D78F /* TDPool.m */, 172 | D3AF43A81A658078005C309E /* TDInterpreterSync.h */, 173 | D3AF43B71A65809A005C309E /* TDInterpreterSync.m */, 174 | D36CB45320ED64260029669D /* TDGamePlayer.h */, 175 | D36CB44520ED3F060029669D /* TDGamePlayer.m */, 176 | D3C0C7441A641527004C8B9A /* Supporting Files */, 177 | D37B0BD52DAD792400795C50 /* TDPipeline.h */, 178 | D37B0BCA2DAD78E600795C50 /* TDPipeline.m */, 179 | D37B0BD62DAD792400795C50 /* TDPipelineStage.h */, 180 | D37B0BCE2DAD78F900795C50 /* TDPipelineStage.m */, 181 | D37B0BDB2DADC23300795C50 /* TDRunner.h */, 182 | D37B0BDC2DADC23300795C50 /* TDRunner.m */, 183 | D37B0BD72DAD792400795C50 /* TDRunnable.h */, 184 | ); 185 | name = TDThreadUtils; 186 | sourceTree = ""; 187 | }; 188 | D3C0C7441A641527004C8B9A /* Supporting Files */ = { 189 | isa = PBXGroup; 190 | children = ( 191 | D3C0C7451A641527004C8B9A /* Info.plist */, 192 | ); 193 | name = "Supporting Files"; 194 | sourceTree = ""; 195 | }; 196 | D3C0C7501A641527004C8B9A /* TDThreadUtilsTests */ = { 197 | isa = PBXGroup; 198 | children = ( 199 | D3AF43C51A65811F005C309E /* TDTest.h */, 200 | D3AF43C61A65811F005C309E /* TDTest.m */, 201 | D3AF43C01A65811F005C309E /* TDBaseTestCase.h */, 202 | D3AF43C11A65811F005C309E /* TDBaseTestCase.m */, 203 | D3AF43C31A65811F005C309E /* TDSemaphoreTests.m */, 204 | D3AF43C21A65811F005C309E /* TDBoundedBufferTests.m */, 205 | D3AF43C41A65811F005C309E /* TDSynchronousChannelTests.m */, 206 | D33849EC1D67786800087F7D /* TDExchangerTests.m */, 207 | D3AF43E81A659BEA005C309E /* TDTriggerTests.m */, 208 | D3AF43C71A65811F005C309E /* TDThresholdTests.m */, 209 | D34700C71B0ED9CC0099D78F /* TDPoolTests.m */, 210 | D36CB44A20ED48BB0029669D /* TDGamePlayerTests.m */, 211 | D38693501F9C2D5F00928048 /* TDLinkedQueueTests.m */, 212 | D3C0C7511A641528004C8B9A /* Supporting Files */, 213 | ); 214 | name = TDThreadUtilsTests; 215 | sourceTree = ""; 216 | }; 217 | D3C0C7511A641528004C8B9A /* Supporting Files */ = { 218 | isa = PBXGroup; 219 | children = ( 220 | D3C0C7521A641528004C8B9A /* Info.plist */, 221 | ); 222 | name = "Supporting Files"; 223 | sourceTree = ""; 224 | }; 225 | /* End PBXGroup section */ 226 | 227 | /* Begin PBXHeadersBuildPhase section */ 228 | D3C0C73E1A641526004C8B9A /* Headers */ = { 229 | isa = PBXHeadersBuildPhase; 230 | buildActionMask = 2147483647; 231 | files = ( 232 | D3AF43CD1A65811F005C309E /* TDTest.h in Headers */, 233 | D3AF43B31A658078005C309E /* TDSynchronousChannel.h in Headers */, 234 | D37B0BD82DAD792400795C50 /* TDPipeline.h in Headers */, 235 | D37B0BD92DAD792400795C50 /* TDRunnable.h in Headers */, 236 | D37B0BDA2DAD792400795C50 /* TDPipelineStage.h in Headers */, 237 | D3AF43B41A658078005C309E /* TDThreadUtils.h in Headers */, 238 | D3AF43B11A658078005C309E /* TDSemaphore.h in Headers */, 239 | D36CB45420ED64260029669D /* TDGamePlayer.h in Headers */, 240 | D37B0BDE2DADC23300795C50 /* TDRunner.h in Headers */, 241 | D34700C61B0ED4EA0099D78F /* TDPool.h in Headers */, 242 | D38693531F9C2E3200928048 /* TDLinkedQueue.h in Headers */, 243 | D3AF43B01A658078005C309E /* TDInterpreterSync.h in Headers */, 244 | D3AF43B51A658078005C309E /* TDThreshold.h in Headers */, 245 | D3AF43AE1A658078005C309E /* TDBoundedBuffer.h in Headers */, 246 | D3AF43C81A65811F005C309E /* TDBaseTestCase.h in Headers */, 247 | D33849E91D67752100087F7D /* TDExchanger.h in Headers */, 248 | D3AF43AF1A658078005C309E /* TDChannel.h in Headers */, 249 | D3AF43E71A659B01005C309E /* TDTrigger.h in Headers */, 250 | ); 251 | runOnlyForDeploymentPostprocessing = 0; 252 | }; 253 | /* End PBXHeadersBuildPhase section */ 254 | 255 | /* Begin PBXNativeTarget section */ 256 | D3C0C7401A641526004C8B9A /* TDThreadUtils */ = { 257 | isa = PBXNativeTarget; 258 | buildConfigurationList = D3C0C7571A641528004C8B9A /* Build configuration list for PBXNativeTarget "TDThreadUtils" */; 259 | buildPhases = ( 260 | D3C0C73C1A641526004C8B9A /* Sources */, 261 | D3C0C73D1A641526004C8B9A /* Frameworks */, 262 | D3C0C73E1A641526004C8B9A /* Headers */, 263 | D3C0C73F1A641526004C8B9A /* Resources */, 264 | ); 265 | buildRules = ( 266 | ); 267 | dependencies = ( 268 | ); 269 | name = TDThreadUtils; 270 | productName = TDThreadUtils; 271 | productReference = D3C0C7411A641526004C8B9A /* TDThreadUtils.framework */; 272 | productType = "com.apple.product-type.framework"; 273 | }; 274 | D3C0C74B1A641527004C8B9A /* TDThreadUtilsTests */ = { 275 | isa = PBXNativeTarget; 276 | buildConfigurationList = D3C0C75A1A641528004C8B9A /* Build configuration list for PBXNativeTarget "TDThreadUtilsTests" */; 277 | buildPhases = ( 278 | D3C0C7481A641527004C8B9A /* Sources */, 279 | D3C0C7491A641527004C8B9A /* Frameworks */, 280 | D3C0C74A1A641527004C8B9A /* Resources */, 281 | ); 282 | buildRules = ( 283 | ); 284 | dependencies = ( 285 | D3C0C74F1A641527004C8B9A /* PBXTargetDependency */, 286 | ); 287 | name = TDThreadUtilsTests; 288 | productName = TDThreadUtilsTests; 289 | productReference = D3C0C74C1A641527004C8B9A /* TDThreadUtilsTests.xctest */; 290 | productType = "com.apple.product-type.bundle.unit-test"; 291 | }; 292 | /* End PBXNativeTarget section */ 293 | 294 | /* Begin PBXProject section */ 295 | D3C0C7381A641526004C8B9A /* Project object */ = { 296 | isa = PBXProject; 297 | attributes = { 298 | BuildIndependentTargetsInParallel = YES; 299 | LastUpgradeCheck = 1620; 300 | ORGANIZATIONNAME = "Todd Ditchendorf"; 301 | TargetAttributes = { 302 | D3C0C7401A641526004C8B9A = { 303 | CreatedOnToolsVersion = 6.1; 304 | }; 305 | D3C0C74B1A641527004C8B9A = { 306 | CreatedOnToolsVersion = 6.1; 307 | }; 308 | }; 309 | }; 310 | buildConfigurationList = D3C0C73B1A641526004C8B9A /* Build configuration list for PBXProject "TDThreadUtils" */; 311 | compatibilityVersion = "Xcode 3.2"; 312 | developmentRegion = English; 313 | hasScannedForEncodings = 0; 314 | knownRegions = ( 315 | English, 316 | en, 317 | ); 318 | mainGroup = D3C0C7371A641526004C8B9A; 319 | productRefGroup = D3C0C7421A641526004C8B9A /* Products */; 320 | projectDirPath = ""; 321 | projectRoot = ""; 322 | targets = ( 323 | D3C0C7401A641526004C8B9A /* TDThreadUtils */, 324 | D3C0C74B1A641527004C8B9A /* TDThreadUtilsTests */, 325 | ); 326 | }; 327 | /* End PBXProject section */ 328 | 329 | /* Begin PBXResourcesBuildPhase section */ 330 | D3C0C73F1A641526004C8B9A /* Resources */ = { 331 | isa = PBXResourcesBuildPhase; 332 | buildActionMask = 2147483647; 333 | files = ( 334 | ); 335 | runOnlyForDeploymentPostprocessing = 0; 336 | }; 337 | D3C0C74A1A641527004C8B9A /* Resources */ = { 338 | isa = PBXResourcesBuildPhase; 339 | buildActionMask = 2147483647; 340 | files = ( 341 | ); 342 | runOnlyForDeploymentPostprocessing = 0; 343 | }; 344 | /* End PBXResourcesBuildPhase section */ 345 | 346 | /* Begin PBXSourcesBuildPhase section */ 347 | D3C0C73C1A641526004C8B9A /* Sources */ = { 348 | isa = PBXSourcesBuildPhase; 349 | buildActionMask = 2147483647; 350 | files = ( 351 | D36CB44720ED3F060029669D /* TDGamePlayer.m in Sources */, 352 | D37B0BDD2DADC23300795C50 /* TDRunner.m in Sources */, 353 | D3AF43BE1A65809A005C309E /* TDSynchronousChannel.m in Sources */, 354 | D3AF43BB1A65809A005C309E /* TDBoundedBuffer.m in Sources */, 355 | D33849EB1D67752D00087F7D /* TDExchanger.m in Sources */, 356 | D37B0BCF2DAD78F900795C50 /* TDPipelineStage.m in Sources */, 357 | D3AF43BD1A65809A005C309E /* TDSemaphore.m in Sources */, 358 | D34700C41B0ED4DE0099D78F /* TDPool.m in Sources */, 359 | D37B0BCB2DAD78E600795C50 /* TDPipeline.m in Sources */, 360 | D3AF43BC1A65809A005C309E /* TDInterpreterSync.m in Sources */, 361 | D386934F1F9C2D2400928048 /* TDLinkedQueue.m in Sources */, 362 | D3AF43E51A659AFE005C309E /* TDTrigger.m in Sources */, 363 | D3AF43BF1A65809A005C309E /* TDThreshold.m in Sources */, 364 | ); 365 | runOnlyForDeploymentPostprocessing = 0; 366 | }; 367 | D3C0C7481A641527004C8B9A /* Sources */ = { 368 | isa = PBXSourcesBuildPhase; 369 | buildActionMask = 2147483647; 370 | files = ( 371 | D3AF43D21A658166005C309E /* TDSemaphoreTests.m in Sources */, 372 | D3AF43D51A658166005C309E /* TDThresholdTests.m in Sources */, 373 | D3AF43D41A658166005C309E /* TDSynchronousChannelTests.m in Sources */, 374 | D3AF43D11A658166005C309E /* TDBaseTestCase.m in Sources */, 375 | D3AF43E91A659BEA005C309E /* TDTriggerTests.m in Sources */, 376 | D34700C91B0EDBF60099D78F /* TDPoolTests.m in Sources */, 377 | D3AF43D01A658166005C309E /* TDTest.m in Sources */, 378 | D3AF43D31A658166005C309E /* TDBoundedBufferTests.m in Sources */, 379 | D38693511F9C2DFF00928048 /* TDLinkedQueueTests.m in Sources */, 380 | D36CB44B20ED48BB0029669D /* TDGamePlayerTests.m in Sources */, 381 | D33849ED1D67786800087F7D /* TDExchangerTests.m in Sources */, 382 | ); 383 | runOnlyForDeploymentPostprocessing = 0; 384 | }; 385 | /* End PBXSourcesBuildPhase section */ 386 | 387 | /* Begin PBXTargetDependency section */ 388 | D3C0C74F1A641527004C8B9A /* PBXTargetDependency */ = { 389 | isa = PBXTargetDependency; 390 | target = D3C0C7401A641526004C8B9A /* TDThreadUtils */; 391 | targetProxy = D3C0C74E1A641527004C8B9A /* PBXContainerItemProxy */; 392 | }; 393 | /* End PBXTargetDependency section */ 394 | 395 | /* Begin XCBuildConfiguration section */ 396 | D3C0C7551A641528004C8B9A /* Debug */ = { 397 | isa = XCBuildConfiguration; 398 | buildSettings = { 399 | ALWAYS_SEARCH_USER_PATHS = NO; 400 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 401 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 402 | CLANG_CXX_LIBRARY = "libc++"; 403 | CLANG_ENABLE_MODULES = YES; 404 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 405 | CLANG_WARN_BOOL_CONVERSION = YES; 406 | CLANG_WARN_COMMA = YES; 407 | CLANG_WARN_CONSTANT_CONVERSION = YES; 408 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 409 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 410 | CLANG_WARN_EMPTY_BODY = YES; 411 | CLANG_WARN_ENUM_CONVERSION = YES; 412 | CLANG_WARN_INFINITE_RECURSION = YES; 413 | CLANG_WARN_INT_CONVERSION = YES; 414 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 415 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 416 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 417 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 418 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 419 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 420 | CLANG_WARN_STRICT_PROTOTYPES = YES; 421 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 422 | CLANG_WARN_UNREACHABLE_CODE = YES; 423 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 424 | COPY_PHASE_STRIP = NO; 425 | CURRENT_PROJECT_VERSION = 1; 426 | DEAD_CODE_STRIPPING = YES; 427 | ENABLE_STRICT_OBJC_MSGSEND = YES; 428 | ENABLE_TESTABILITY = YES; 429 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 430 | GCC_C_LANGUAGE_STANDARD = gnu99; 431 | GCC_DYNAMIC_NO_PIC = NO; 432 | GCC_NO_COMMON_BLOCKS = YES; 433 | GCC_OPTIMIZATION_LEVEL = 0; 434 | GCC_PREPROCESSOR_DEFINITIONS = ( 435 | "DEBUG=1", 436 | "$(inherited)", 437 | ); 438 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 439 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 440 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 441 | GCC_WARN_UNDECLARED_SELECTOR = YES; 442 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 443 | GCC_WARN_UNUSED_FUNCTION = YES; 444 | GCC_WARN_UNUSED_VARIABLE = YES; 445 | MACOSX_DEPLOYMENT_TARGET = 10.9; 446 | MTL_ENABLE_DEBUG_INFO = YES; 447 | ONLY_ACTIVE_ARCH = YES; 448 | SDKROOT = macosx; 449 | VERSIONING_SYSTEM = "apple-generic"; 450 | VERSION_INFO_PREFIX = ""; 451 | }; 452 | name = Debug; 453 | }; 454 | D3C0C7561A641528004C8B9A /* Release */ = { 455 | isa = XCBuildConfiguration; 456 | buildSettings = { 457 | ALWAYS_SEARCH_USER_PATHS = NO; 458 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 459 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 460 | CLANG_CXX_LIBRARY = "libc++"; 461 | CLANG_ENABLE_MODULES = YES; 462 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 463 | CLANG_WARN_BOOL_CONVERSION = YES; 464 | CLANG_WARN_COMMA = YES; 465 | CLANG_WARN_CONSTANT_CONVERSION = YES; 466 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 467 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 468 | CLANG_WARN_EMPTY_BODY = YES; 469 | CLANG_WARN_ENUM_CONVERSION = YES; 470 | CLANG_WARN_INFINITE_RECURSION = YES; 471 | CLANG_WARN_INT_CONVERSION = YES; 472 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 473 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 474 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 475 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 476 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 477 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 478 | CLANG_WARN_STRICT_PROTOTYPES = YES; 479 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 480 | CLANG_WARN_UNREACHABLE_CODE = YES; 481 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 482 | COPY_PHASE_STRIP = YES; 483 | CURRENT_PROJECT_VERSION = 1; 484 | DEAD_CODE_STRIPPING = YES; 485 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 486 | ENABLE_NS_ASSERTIONS = NO; 487 | ENABLE_STRICT_OBJC_MSGSEND = YES; 488 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 489 | GCC_C_LANGUAGE_STANDARD = gnu99; 490 | GCC_NO_COMMON_BLOCKS = YES; 491 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 492 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 493 | GCC_WARN_UNDECLARED_SELECTOR = YES; 494 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 495 | GCC_WARN_UNUSED_FUNCTION = YES; 496 | GCC_WARN_UNUSED_VARIABLE = YES; 497 | MACOSX_DEPLOYMENT_TARGET = 10.9; 498 | MTL_ENABLE_DEBUG_INFO = NO; 499 | SDKROOT = macosx; 500 | VERSIONING_SYSTEM = "apple-generic"; 501 | VERSION_INFO_PREFIX = ""; 502 | }; 503 | name = Release; 504 | }; 505 | D3C0C7581A641528004C8B9A /* Debug */ = { 506 | isa = XCBuildConfiguration; 507 | buildSettings = { 508 | CLANG_ENABLE_OBJC_WEAK = YES; 509 | COMBINE_HIDPI_IMAGES = YES; 510 | DEAD_CODE_STRIPPING = YES; 511 | DEFINES_MODULE = YES; 512 | DYLIB_COMPATIBILITY_VERSION = 1; 513 | DYLIB_CURRENT_VERSION = 1; 514 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 515 | ENABLE_MODULE_VERIFIER = YES; 516 | FRAMEWORK_VERSION = A; 517 | INFOPLIST_FILE = src/Info.plist; 518 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 519 | LD_RUNPATH_SEARCH_PATHS = ( 520 | "$(inherited)", 521 | "@executable_path/../Frameworks", 522 | "@loader_path/Frameworks", 523 | ); 524 | MACOSX_DEPLOYMENT_TARGET = 11.0; 525 | MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11"; 526 | PRODUCT_BUNDLE_IDENTIFIER = "com.celestialteapot.$(PRODUCT_NAME:rfc1034identifier)"; 527 | PRODUCT_NAME = "$(TARGET_NAME)"; 528 | SKIP_INSTALL = YES; 529 | }; 530 | name = Debug; 531 | }; 532 | D3C0C7591A641528004C8B9A /* Release */ = { 533 | isa = XCBuildConfiguration; 534 | buildSettings = { 535 | CLANG_ENABLE_OBJC_WEAK = YES; 536 | COMBINE_HIDPI_IMAGES = YES; 537 | DEAD_CODE_STRIPPING = YES; 538 | DEFINES_MODULE = YES; 539 | DYLIB_COMPATIBILITY_VERSION = 1; 540 | DYLIB_CURRENT_VERSION = 1; 541 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 542 | ENABLE_MODULE_VERIFIER = YES; 543 | FRAMEWORK_VERSION = A; 544 | INFOPLIST_FILE = src/Info.plist; 545 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 546 | LD_RUNPATH_SEARCH_PATHS = ( 547 | "$(inherited)", 548 | "@executable_path/../Frameworks", 549 | "@loader_path/Frameworks", 550 | ); 551 | MACOSX_DEPLOYMENT_TARGET = 11.0; 552 | MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11"; 553 | PRODUCT_BUNDLE_IDENTIFIER = "com.celestialteapot.$(PRODUCT_NAME:rfc1034identifier)"; 554 | PRODUCT_NAME = "$(TARGET_NAME)"; 555 | SKIP_INSTALL = YES; 556 | }; 557 | name = Release; 558 | }; 559 | D3C0C75B1A641528004C8B9A /* Debug */ = { 560 | isa = XCBuildConfiguration; 561 | buildSettings = { 562 | CLANG_ENABLE_OBJC_WEAK = YES; 563 | COMBINE_HIDPI_IMAGES = YES; 564 | DEAD_CODE_STRIPPING = YES; 565 | FRAMEWORK_SEARCH_PATHS = ( 566 | "$(DEVELOPER_FRAMEWORKS_DIR)", 567 | "$(inherited)", 568 | ); 569 | GCC_PREPROCESSOR_DEFINITIONS = ( 570 | "DEBUG=1", 571 | "$(inherited)", 572 | ); 573 | INFOPLIST_FILE = tests/Info.plist; 574 | LD_RUNPATH_SEARCH_PATHS = ( 575 | "$(inherited)", 576 | "@executable_path/../Frameworks", 577 | "@loader_path/../Frameworks", 578 | ); 579 | MACOSX_DEPLOYMENT_TARGET = 11.0; 580 | PRODUCT_BUNDLE_IDENTIFIER = "com.celestialteapot.$(PRODUCT_NAME:rfc1034identifier)"; 581 | PRODUCT_NAME = "$(TARGET_NAME)"; 582 | }; 583 | name = Debug; 584 | }; 585 | D3C0C75C1A641528004C8B9A /* Release */ = { 586 | isa = XCBuildConfiguration; 587 | buildSettings = { 588 | CLANG_ENABLE_OBJC_WEAK = YES; 589 | COMBINE_HIDPI_IMAGES = YES; 590 | DEAD_CODE_STRIPPING = YES; 591 | FRAMEWORK_SEARCH_PATHS = ( 592 | "$(DEVELOPER_FRAMEWORKS_DIR)", 593 | "$(inherited)", 594 | ); 595 | INFOPLIST_FILE = tests/Info.plist; 596 | LD_RUNPATH_SEARCH_PATHS = ( 597 | "$(inherited)", 598 | "@executable_path/../Frameworks", 599 | "@loader_path/../Frameworks", 600 | ); 601 | MACOSX_DEPLOYMENT_TARGET = 11.0; 602 | PRODUCT_BUNDLE_IDENTIFIER = "com.celestialteapot.$(PRODUCT_NAME:rfc1034identifier)"; 603 | PRODUCT_NAME = "$(TARGET_NAME)"; 604 | }; 605 | name = Release; 606 | }; 607 | /* End XCBuildConfiguration section */ 608 | 609 | /* Begin XCConfigurationList section */ 610 | D3C0C73B1A641526004C8B9A /* Build configuration list for PBXProject "TDThreadUtils" */ = { 611 | isa = XCConfigurationList; 612 | buildConfigurations = ( 613 | D3C0C7551A641528004C8B9A /* Debug */, 614 | D3C0C7561A641528004C8B9A /* Release */, 615 | ); 616 | defaultConfigurationIsVisible = 0; 617 | defaultConfigurationName = Release; 618 | }; 619 | D3C0C7571A641528004C8B9A /* Build configuration list for PBXNativeTarget "TDThreadUtils" */ = { 620 | isa = XCConfigurationList; 621 | buildConfigurations = ( 622 | D3C0C7581A641528004C8B9A /* Debug */, 623 | D3C0C7591A641528004C8B9A /* Release */, 624 | ); 625 | defaultConfigurationIsVisible = 0; 626 | defaultConfigurationName = Release; 627 | }; 628 | D3C0C75A1A641528004C8B9A /* Build configuration list for PBXNativeTarget "TDThreadUtilsTests" */ = { 629 | isa = XCConfigurationList; 630 | buildConfigurations = ( 631 | D3C0C75B1A641528004C8B9A /* Debug */, 632 | D3C0C75C1A641528004C8B9A /* Release */, 633 | ); 634 | defaultConfigurationIsVisible = 0; 635 | defaultConfigurationName = Release; 636 | }; 637 | /* End XCConfigurationList section */ 638 | }; 639 | rootObject = D3C0C7381A641526004C8B9A /* Project object */; 640 | } 641 | -------------------------------------------------------------------------------- /TDThreadUtils.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TDThreadUtils.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /TDThreadUtils.xcodeproj/xcshareddata/xcschemes/TDThreadUtils.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 9 | 10 | 16 | 22 | 23 | 24 | 25 | 26 | 32 | 33 | 35 | 41 | 42 | 43 | 44 | 45 | 55 | 56 | 62 | 63 | 69 | 70 | 71 | 72 | 74 | 75 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /include/TDThreadUtils/TDBoundedBuffer.h: -------------------------------------------------------------------------------- 1 | // 2 | // TDBoundedBuffer.h 3 | // TDThreadUtils 4 | // 5 | // Created by Todd Ditchendorf on 1/12/15. 6 | // Copyright (c) 2015 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TDBoundedBuffer : NSObject 12 | 13 | + (instancetype)boundedBufferWithSize:(NSUInteger)size; 14 | - (instancetype)initWithSize:(NSUInteger)size; 15 | 16 | - (void)put:(id)obj; 17 | - (id)take; 18 | 19 | - (BOOL)put:(id)obj beforeDate:(NSDate *)date; 20 | - (id)takeBeforeDate:(NSDate *)date; 21 | @end 22 | -------------------------------------------------------------------------------- /include/TDThreadUtils/TDChannel.h: -------------------------------------------------------------------------------- 1 | // 2 | // TDChannel.h 3 | // TDThreadUtils 4 | // 5 | // Created by Todd Ditchendorf on 1/12/15. 6 | // Copyright (c) 2015 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol TDChannel 12 | - (void)put:(id)obj; 13 | - (id)take; 14 | @end 15 | -------------------------------------------------------------------------------- /include/TDThreadUtils/TDExchanger.h: -------------------------------------------------------------------------------- 1 | // 2 | // TDExchanger.h 3 | // TDThreadUtils 4 | // 5 | // Created by Todd Ditchendorf on 5/19/14. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface TDExchanger : NSObject 12 | 13 | + (instancetype)exchanger; 14 | 15 | - (id)exchange:(id)obj; 16 | @end 17 | -------------------------------------------------------------------------------- /include/TDThreadUtils/TDGamePlayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // TDGamePlayer.h 3 | // TDThreadUtils 4 | // 5 | // Created by Todd Ditchendorf on 04.07.18. 6 | // Copyright © 2018 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class TDGamePlayer; 12 | 13 | @protocol TDGamePlayerDelegate 14 | - (id)gamePlayer:(TDGamePlayer *)p doTurnWithInput:(id)plist; 15 | @end 16 | 17 | @interface TDGamePlayer : NSObject 18 | 19 | - (instancetype)initWithDelegate:(id )d; 20 | 21 | - (void)run; // call on a bg thread 22 | - (void)stop; // call on main thread 23 | 24 | - (void)giveFirstTurnWithInput:(id)plist; // call on main thread before -run 25 | 26 | @property (assign, readonly) id delegate; 27 | @property (assign) TDGamePlayer *opponent; 28 | @end 29 | -------------------------------------------------------------------------------- /include/TDThreadUtils/TDInterpreterSync.h: -------------------------------------------------------------------------------- 1 | // 2 | // TDInterpreterSync.h 3 | // TDThreadUtils 4 | // 5 | // 6 | // Created by Todd Ditchendorf on 5/19/14. 7 | // 8 | // 9 | 10 | #import 11 | 12 | @interface TDInterpreterSync : NSObject 13 | 14 | + (instancetype)interpreterSync; 15 | 16 | // called from interp execution thread 17 | - (void)pauseWithInfo:(id)info; 18 | - (id)awaitResume; 19 | 20 | // called from user command/control thread 21 | - (void)resumeWithInfo:(id)info; 22 | - (id)awaitPause; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /include/TDThreadUtils/TDLinkedQueue.h: -------------------------------------------------------------------------------- 1 | // 2 | // TDLinkedQueue.h 3 | // TDThreadUtils 4 | // 5 | // Created by Todd Ditchendorf on 10/21/17. 6 | // Copyright © 2017 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TDLinkedQueue : NSObject 12 | 13 | + (instancetype)linkedQueue; 14 | 15 | - (void)put:(id)obj; // returns immediately without blocking. capacity is unbounded 16 | - (id)take; // blocks indefinitely while waiting for object to become available. never returns nil 17 | 18 | - (id)poll; // returns immediately without blocking. may return nil 19 | - (id)takeBeforeDate:(NSDate *)date; // blocks until date while waiting for object to become available. may return nil 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /include/TDThreadUtils/TDPipeline.h: -------------------------------------------------------------------------------- 1 | // 2 | // TDPipeline.h 3 | // TDThreadUtils 4 | // 5 | // Created by Todd Ditchendorf on 4/14/25. 6 | // Copyright © 2025 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface TDPipeline : NSObject 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /include/TDThreadUtils/TDPiplineStage.h: -------------------------------------------------------------------------------- 1 | // 2 | // TDPiplineStage.h 3 | // TDThreadUtils 4 | // 5 | // Created by Todd Ditchendorf on 4/14/25. 6 | // Copyright © 2025 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface TDPiplineStage : NSObject 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /include/TDThreadUtils/TDPool.h: -------------------------------------------------------------------------------- 1 | // 2 | // TDPool.h 3 | // TDThreadUtils 4 | // 5 | // Created by Todd Ditchendorf on 5/21/15. 6 | // Copyright (c) 2015 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TDPool : NSObject 12 | 13 | + (instancetype)poolWithItems:(NSArray *)items; 14 | - (instancetype)initWithItems:(NSArray *)items; 15 | 16 | - (id)takeItem; 17 | - (void)returnItem:(id)obj; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /include/TDThreadUtils/TDSemaphore.h: -------------------------------------------------------------------------------- 1 | // 2 | // TDSemaphore.h 3 | // TDThreadUtils 4 | // 5 | // Created by Todd Ditchendorf on 5/31/13. 6 | // Copyright (c) 2013 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TDSemaphore : NSObject 12 | 13 | + (instancetype)semaphoreWithValue:(NSInteger)value; 14 | - (instancetype)initWithValue:(NSInteger)value; 15 | 16 | - (BOOL)attempt; // returns success immediately 17 | - (BOOL)attemptBeforeDate:(NSDate *)limit; // returns success. can block up to limit 18 | 19 | - (void)acquire; // blocks forever 20 | - (void)relinquish; // returns immediately 21 | @end 22 | -------------------------------------------------------------------------------- /include/TDThreadUtils/TDSynchronousChannel.h: -------------------------------------------------------------------------------- 1 | // 2 | // TDSynchronousChannel.h 3 | // TDThreadUtils 4 | // 5 | // Created by Todd Ditchendorf on 5/19/14. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface TDSynchronousChannel : NSObject 12 | 13 | + (instancetype)synchronousChannel; 14 | 15 | - (void)put:(id)obj; 16 | - (id)take; 17 | @end 18 | -------------------------------------------------------------------------------- /include/TDThreadUtils/TDThreadUtils.h: -------------------------------------------------------------------------------- 1 | // 2 | // TDThreadUtils.h 3 | // TDThreadUtils 4 | // 5 | // Created by Todd Ditchendorf on 1/12/15. 6 | // Copyright (c) 2015 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for TDThreadUtils. 12 | FOUNDATION_EXPORT double TDThreadUtilsVersionNumber; 13 | 14 | //! Project version string for TDThreadUtils. 15 | FOUNDATION_EXPORT const unsigned char TDThreadUtilsVersionString[]; 16 | 17 | #import 18 | 19 | #import 20 | #import 21 | #import 22 | #import 23 | #import 24 | #import 25 | #import 26 | #import 27 | #import 28 | 29 | #import 30 | 31 | #import 32 | #import 33 | #import 34 | #import 35 | -------------------------------------------------------------------------------- /include/TDThreadUtils/TDThreshold.h: -------------------------------------------------------------------------------- 1 | // 2 | // TDThreshold.h 3 | // TDThreadUtils 4 | // 5 | // Created by Todd Ditchendorf on 1/12/15. 6 | // Copyright (c) 2015 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TDThreshold : NSObject 12 | 13 | + (instancetype)thresholdWithValue:(NSInteger)value; 14 | - (instancetype)initWithValue:(NSInteger)value; 15 | 16 | - (void)await; 17 | @end 18 | -------------------------------------------------------------------------------- /include/TDThreadUtils/TDTrigger.h: -------------------------------------------------------------------------------- 1 | // 2 | // TDTrigger.h 3 | // TDThreadUtils 4 | // 5 | // Created by Todd Ditchendorf on 1/12/15. 6 | // Copyright (c) 2015 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TDTrigger : NSObject 12 | 13 | + (instancetype)trigger; 14 | 15 | - (void)await; 16 | - (void)fire; 17 | @end 18 | -------------------------------------------------------------------------------- /include/TDThreadUtils/TDWorker.h: -------------------------------------------------------------------------------- 1 | // 2 | // TDWorker.h 3 | // TDThreadUtils 4 | // 5 | // Created by Todd Ditchendorf on 4/14/25. 6 | // Copyright © 2025 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface TDWorker : NSObject 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /src/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSHumanReadableCopyright 24 | Copyright © 2015 Todd Ditchendorf. All rights reserved. 25 | NSPrincipalClass 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/TDBoundedBuffer.m: -------------------------------------------------------------------------------- 1 | // 2 | // TDBoundedBuffer.m 3 | // TDThreadUtils 4 | // 5 | // Created by Todd Ditchendorf on 1/12/15. 6 | // Copyright (c) 2015 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface TDBufferArray : NSObject 13 | - (instancetype)initWithSize:(NSUInteger)size; 14 | - (void)insert:(id)obj; 15 | - (id)extract; 16 | 17 | @property (assign) NSUInteger size; 18 | @property (assign) NSUInteger putIndex; 19 | @property (assign) NSUInteger takeIndex; 20 | @end 21 | 22 | @implementation TDBufferArray { 23 | id *_array; 24 | } 25 | 26 | - (instancetype)initWithSize:(NSUInteger)size { 27 | NSParameterAssert(NSNotFound != size); 28 | NSParameterAssert(size > 0); 29 | self = [super init]; 30 | if (self) { 31 | self.size = size; 32 | _array = malloc(sizeof(id) * _size); 33 | } 34 | return self; 35 | } 36 | 37 | 38 | - (void)dealloc { 39 | free(_array); 40 | [super dealloc]; 41 | } 42 | 43 | 44 | - (void)insert:(id)obj { 45 | NSParameterAssert(obj); 46 | NSAssert(_array, @""); 47 | 48 | @synchronized(self) { 49 | _array[_putIndex] = obj; 50 | self.putIndex = (_putIndex + 1) % _size; 51 | } 52 | 53 | NSAssert(_putIndex < _size, @""); 54 | } 55 | 56 | 57 | - (id)extract { 58 | NSAssert(_array, @""); 59 | 60 | id obj = nil; 61 | @synchronized(self) { 62 | obj = [[_array[_takeIndex] retain] autorelease]; 63 | _array[_takeIndex] = nil; 64 | self.takeIndex = (_takeIndex + 1) % _size; 65 | } 66 | 67 | NSAssert(_takeIndex < _size, @""); 68 | return obj; 69 | } 70 | 71 | @end 72 | 73 | 74 | @interface TDBoundedBuffer () 75 | @property (retain) TDBufferArray *buffer; 76 | @property (retain) TDSemaphore *putPermits; 77 | @property (retain) TDSemaphore *takePermits; 78 | @end 79 | 80 | @implementation TDBoundedBuffer 81 | 82 | + (instancetype)boundedBufferWithSize:(NSUInteger)size { 83 | return [[(TDBoundedBuffer *)[self alloc] initWithSize:size] autorelease]; 84 | } 85 | 86 | 87 | - (instancetype)initWithSize:(NSUInteger)size { 88 | NSParameterAssert(NSNotFound != size); 89 | NSParameterAssert(size > 0); 90 | self = [super init]; 91 | if (self) { 92 | self.buffer = [[[TDBufferArray alloc] initWithSize:size] autorelease]; 93 | self.putPermits = [TDSemaphore semaphoreWithValue:size]; 94 | self.takePermits = [TDSemaphore semaphoreWithValue:0]; 95 | } 96 | return self; 97 | } 98 | 99 | 100 | - (void)dealloc { 101 | self.buffer = nil; 102 | self.putPermits = nil; 103 | self.takePermits = nil; 104 | [super dealloc]; 105 | } 106 | 107 | 108 | - (void)put:(id)obj { 109 | NSParameterAssert(obj); 110 | NSAssert(_buffer, @""); 111 | NSAssert(_putPermits, @""); 112 | NSAssert(_takePermits, @""); 113 | 114 | [_putPermits acquire]; 115 | [_buffer insert:[obj retain]]; 116 | [_takePermits relinquish]; 117 | } 118 | 119 | 120 | - (id)take { 121 | NSAssert(_buffer, @""); 122 | NSAssert(_putPermits, @""); 123 | NSAssert(_takePermits, @""); 124 | 125 | [_takePermits acquire]; 126 | id obj = [[_buffer extract] autorelease]; 127 | [_putPermits relinquish]; 128 | 129 | NSAssert(obj, @""); 130 | return obj; 131 | } 132 | 133 | 134 | - (BOOL)put:(id)obj beforeDate:(NSDate *)date { 135 | NSParameterAssert(obj); 136 | NSAssert(_buffer, @""); 137 | NSAssert(_putPermits, @""); 138 | NSAssert(_takePermits, @""); 139 | 140 | BOOL success = [_putPermits attemptBeforeDate:date]; 141 | if (success) { 142 | [_buffer insert:obj]; 143 | [_takePermits relinquish]; 144 | } 145 | return success; 146 | } 147 | 148 | 149 | - (id)takeBeforeDate:(NSDate *)date { 150 | NSAssert(_buffer, @""); 151 | NSAssert(_putPermits, @""); 152 | NSAssert(_takePermits, @""); 153 | 154 | id obj = nil; 155 | 156 | if ([_takePermits attemptBeforeDate:date]) { 157 | obj = [_buffer extract]; 158 | [_putPermits relinquish]; 159 | NSAssert(obj, @""); 160 | } 161 | return obj; 162 | } 163 | 164 | @end 165 | -------------------------------------------------------------------------------- /src/TDExchanger.m: -------------------------------------------------------------------------------- 1 | // 2 | // TDExchanger.m 3 | // TDThreadUtils 4 | // 5 | // Created by Todd Ditchendorf on 5/19/14. 6 | // 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface TDExchanger () 13 | @property (retain) TDSynchronousChannel *channelA; 14 | @property (retain) TDSynchronousChannel *channelB; 15 | @property (nonatomic, assign) BOOL flag; 16 | @end 17 | 18 | @implementation TDExchanger 19 | 20 | + (instancetype)exchanger { 21 | return [[[self alloc] init] autorelease]; 22 | } 23 | 24 | 25 | - (instancetype)init { 26 | self = [super init]; 27 | if (self) { 28 | self.channelA = [TDSynchronousChannel synchronousChannel]; 29 | self.channelB = [TDSynchronousChannel synchronousChannel]; 30 | } 31 | return self; 32 | } 33 | 34 | 35 | - (void)dealloc { 36 | self.channelA = nil; 37 | self.channelB = nil; 38 | [super dealloc]; 39 | } 40 | 41 | 42 | - (id)exchange:(id)inObj { 43 | NSParameterAssert(inObj); 44 | NSAssert(_channelA, @""); 45 | NSAssert(_channelB, @""); 46 | 47 | BOOL flag; 48 | @synchronized(_channelA) { 49 | flag = self.flag; 50 | self.flag = !flag; 51 | } 52 | 53 | id outObj = nil; 54 | if (flag) { 55 | [_channelA put:inObj]; 56 | outObj = [_channelB take]; 57 | } else { 58 | outObj = [_channelA take]; 59 | [_channelB put:inObj]; 60 | } 61 | 62 | NSAssert(outObj, @""); 63 | return outObj; 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /src/TDGamePlayer.m: -------------------------------------------------------------------------------- 1 | // 2 | // TDGamePlayer.m 3 | // TDThreadUtils 4 | // 5 | // Created by Todd Ditchendorf on 04.07.18. 6 | // Copyright © 2018 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TDGamePlayer () 12 | @property (assign, readwrite) id delegate; 13 | @property (assign) BOOL myTurn; 14 | @property (assign) BOOL stopped; 15 | @property (retain) id input; 16 | @property (retain) NSCondition *monitor; 17 | @end 18 | 19 | @implementation TDGamePlayer 20 | 21 | - (instancetype)init { 22 | self = [self initWithDelegate:nil]; 23 | return self; 24 | } 25 | 26 | 27 | - (instancetype)initWithDelegate:(id )d { 28 | NSParameterAssert(d ); 29 | self = [super init]; 30 | if (self) { 31 | self.delegate = d; 32 | self.myTurn = NO; 33 | self.stopped = NO; 34 | self.monitor = [[[NSCondition alloc] init] autorelease]; 35 | } 36 | return self; 37 | } 38 | 39 | 40 | - (void)dealloc { 41 | self.delegate = nil; 42 | self.opponent = nil; 43 | self.input = nil; 44 | self.monitor = nil; 45 | [super dealloc]; 46 | } 47 | 48 | 49 | #pragma mark - 50 | #pragma mark Public 51 | 52 | - (void)run { 53 | NSAssert(![NSThread isMainThread], @""); 54 | NSAssert(![self done], @"Can only call -run once"); 55 | 56 | for (;;) { 57 | [self awaitTurn]; 58 | 59 | BOOL done = [self done]; 60 | 61 | @try { 62 | if (done) { 63 | break; 64 | } else { 65 | [self doTurn]; 66 | } 67 | } 68 | @finally { 69 | [self releaseTurn]; 70 | } 71 | } 72 | } 73 | 74 | 75 | - (void)stop { 76 | [self lock]; 77 | 78 | self.stopped = YES; 79 | 80 | [self unlock]; 81 | } 82 | 83 | 84 | - (BOOL)done { 85 | BOOL done = NO; 86 | 87 | [self lock]; 88 | 89 | done = self.stopped; 90 | 91 | [self unlock]; 92 | 93 | return done; 94 | } 95 | 96 | 97 | - (void)giveFirstTurnWithInput:(id)plist { 98 | [self lock]; 99 | 100 | self.input = plist; 101 | 102 | [self unlock]; 103 | 104 | [self giveTurn]; // open call 105 | } 106 | 107 | 108 | #pragma mark - 109 | #pragma mark Private 110 | 111 | - (void)giveTurn { 112 | [self lock]; 113 | 114 | self.myTurn = YES; 115 | [self signal]; 116 | 117 | [self unlock]; 118 | } 119 | 120 | 121 | - (void)releaseTurn { 122 | TDGamePlayer *p = nil; 123 | 124 | [self lock]; 125 | 126 | self.myTurn = NO; 127 | p = self.opponent; 128 | 129 | [self unlock]; 130 | 131 | [p giveTurn]; // open call 132 | } 133 | 134 | 135 | - (void)awaitTurn { 136 | [self lock]; 137 | 138 | while (!self.myTurn) { 139 | [self wait]; 140 | } 141 | 142 | [self unlock]; 143 | } 144 | 145 | 146 | - (void)doTurn { 147 | NSAssert(self.delegate, @""); 148 | 149 | id input = self.input; 150 | 151 | // TODO round-trip serialize input plist here 152 | 153 | id output = [self.delegate gamePlayer:self doTurnWithInput:input]; 154 | self.opponent.input = output; 155 | } 156 | 157 | 158 | #pragma mark - 159 | #pragma mark Private Convenience 160 | 161 | - (void)lock { 162 | NSAssert(_monitor, @""); 163 | [_monitor lock]; 164 | } 165 | 166 | 167 | - (void)unlock { 168 | NSAssert(_monitor, @""); 169 | [_monitor unlock]; 170 | } 171 | 172 | 173 | - (void)wait { 174 | NSAssert(_monitor, @""); 175 | [_monitor wait]; 176 | } 177 | 178 | 179 | - (void)signal { 180 | NSAssert(_monitor, @""); 181 | [_monitor signal]; 182 | } 183 | 184 | @end 185 | -------------------------------------------------------------------------------- /src/TDInterpreterSync.m: -------------------------------------------------------------------------------- 1 | // 2 | // TDInterpreterSync.m 3 | // TDThreadUtils 4 | // 5 | // Created by Todd Ditchendorf on 5/19/14. 6 | // 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface TDInterpreterSync () 13 | @property (retain) TDSynchronousChannel *pauseChannel; 14 | @property (retain) TDSynchronousChannel *resumeChannel; 15 | @end 16 | 17 | @implementation TDInterpreterSync 18 | 19 | + (instancetype)interpreterSync { 20 | return [[[self alloc] init] autorelease]; 21 | } 22 | 23 | 24 | - (instancetype)init { 25 | self = [super init]; 26 | if (self) { 27 | self.pauseChannel = [TDSynchronousChannel synchronousChannel]; 28 | self.resumeChannel = [TDSynchronousChannel synchronousChannel]; 29 | } 30 | return self; 31 | } 32 | 33 | 34 | - (void)dealloc { 35 | self.pauseChannel = nil; 36 | self.resumeChannel = nil; 37 | [super dealloc]; 38 | } 39 | 40 | 41 | - (id)awaitPause { 42 | NSAssert(_pauseChannel, @""); 43 | return [_pauseChannel take]; 44 | } 45 | 46 | 47 | - (void)pauseWithInfo:(id)info { 48 | NSAssert(_pauseChannel, @""); 49 | [_pauseChannel put:info]; 50 | } 51 | 52 | 53 | - (id)awaitResume { 54 | NSAssert(_resumeChannel, @""); 55 | return [_resumeChannel take]; 56 | } 57 | 58 | 59 | - (void)resumeWithInfo:(id)info { 60 | NSAssert(_resumeChannel, @""); 61 | [_resumeChannel put:info]; 62 | } 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /src/TDLinkedQueue.m: -------------------------------------------------------------------------------- 1 | // 2 | // TDLinkedQueue.m 3 | // TDThreadUtils 4 | // 5 | // Created by Todd Ditchendorf on 10/21/17. 6 | // Copyright © 2017 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LQNode : NSObject 12 | @property (nonatomic, retain) id object; 13 | @property (nonatomic, retain) LQNode *next; 14 | @end 15 | 16 | @implementation LQNode 17 | 18 | - (instancetype)initWithObject:(id)obj { 19 | self = [super init]; 20 | if (self) { 21 | self.object = obj; 22 | } 23 | return self; 24 | } 25 | 26 | 27 | - (void)dealloc { 28 | self.object = nil; 29 | self.next = nil; 30 | [super dealloc]; 31 | } 32 | 33 | @end 34 | 35 | @interface TDLinkedQueue () 36 | @property (nonatomic, retain) LQNode *head; 37 | @property (nonatomic, retain) LQNode *last; 38 | 39 | @property (retain) NSCondition *monitor; 40 | @end 41 | 42 | @implementation TDLinkedQueue 43 | 44 | + (instancetype)linkedQueue { 45 | return [[[self alloc] init] autorelease]; 46 | } 47 | 48 | 49 | - (instancetype)init { 50 | self = [super init]; 51 | if (self) { 52 | self.monitor = [[[NSCondition alloc] init] autorelease]; 53 | } 54 | return self; 55 | } 56 | 57 | 58 | - (void)dealloc { 59 | self.head = nil; 60 | self.last = nil; 61 | 62 | self.monitor = nil; 63 | [super dealloc]; 64 | } 65 | 66 | 67 | - (NSString *)description { 68 | NSMutableString *buf = [NSMutableString string]; 69 | 70 | [self lock]; 71 | 72 | LQNode *node = _head; 73 | 74 | while (node) { 75 | [buf appendFormat:@"%@%@", node.object, node.next ? @"->" : @""]; 76 | node = node.next; 77 | } 78 | 79 | [self unlock]; 80 | 81 | return [NSString stringWithFormat:@"<%@ %p %@>", [self class], self, buf]; 82 | } 83 | 84 | 85 | - (void)put:(id)obj { 86 | NSParameterAssert(obj); 87 | 88 | LQNode *node = [[[LQNode alloc] initWithObject:obj] autorelease]; 89 | 90 | [self lock]; 91 | 92 | if ([self available]) { 93 | _last.next = node; 94 | self.last = node; 95 | } else { 96 | self.head = node; 97 | self.last = node; 98 | } 99 | 100 | [self signal]; 101 | [self unlock]; 102 | } 103 | 104 | 105 | - (id)poll { 106 | [[self retain] autorelease]; 107 | [self lock]; 108 | 109 | id result = nil; 110 | 111 | if ([self available]) { 112 | result = [self doPoll]; 113 | } 114 | 115 | [self unlock]; 116 | 117 | return result; 118 | } 119 | 120 | 121 | - (id)take { 122 | [[self retain] autorelease]; 123 | [self lock]; 124 | 125 | while (![self available]) { 126 | [self wait]; 127 | } 128 | 129 | id result = [self doPoll]; 130 | 131 | [self unlock]; 132 | 133 | return result; 134 | } 135 | 136 | 137 | - (id)takeBeforeDate:(NSDate *)limit { 138 | NSParameterAssert([self isValidDate:limit]); 139 | 140 | [[self retain] autorelease]; 141 | [self lock]; 142 | 143 | while ([self isValidDate:limit] && ![self available]) { 144 | [self waitUntilDate:limit]; 145 | } 146 | 147 | id result = nil; 148 | 149 | if ([self available]) { 150 | result = [self doPoll]; 151 | } 152 | 153 | [self unlock]; 154 | 155 | return result; 156 | } 157 | 158 | 159 | #pragma mark - 160 | #pragma mark Private Business 161 | 162 | // PRE: Lock is held, available is true 163 | - (id)doPoll { 164 | NSAssert([self available], @""); 165 | NSAssert(_head.object, @""); 166 | 167 | id result = [[_head.object retain] autorelease]; 168 | self.head = [[[_head retain] autorelease] next]; 169 | if (!_head) { 170 | self.last = nil; 171 | } 172 | return result; 173 | } 174 | 175 | 176 | // PRE: Lock is held 177 | - (BOOL)available { 178 | NSAssert((_head && _last) || (!_head && !_last), @""); 179 | return nil != _head; 180 | } 181 | 182 | 183 | #pragma mark - 184 | #pragma mark Private Convenience 185 | 186 | - (void)lock { 187 | NSAssert(_monitor, @""); 188 | [_monitor lock]; 189 | } 190 | 191 | 192 | - (void)unlock { 193 | NSAssert(_monitor, @""); 194 | [_monitor unlock]; 195 | } 196 | 197 | 198 | - (void)wait { 199 | NSAssert(_monitor, @""); 200 | [_monitor wait]; 201 | } 202 | 203 | 204 | - (void)waitUntilDate:(NSDate *)date { 205 | NSAssert(_monitor, @""); 206 | [_monitor waitUntilDate:date]; 207 | } 208 | 209 | 210 | - (void)signal { 211 | NSAssert(_monitor, @""); 212 | [_monitor signal]; 213 | } 214 | 215 | 216 | - (BOOL)isValidDate:(NSDate *)limit { 217 | NSParameterAssert(limit); 218 | return [limit timeIntervalSinceNow] > 0.0; 219 | } 220 | 221 | @end 222 | -------------------------------------------------------------------------------- /src/TDPipeline.m: -------------------------------------------------------------------------------- 1 | // 2 | // TDPipeline.m 3 | // TDThreadUtils 4 | // 5 | // Created by Todd Ditchendorf on 4/14/25. 6 | // Copyright © 2025 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface TDPipelineStage () 13 | - (void)setUpWithInputChannel:(id )ic outputChannel:(id )oc; 14 | @end 15 | 16 | @interface TDPipeline () 17 | @property (nonatomic, retain, readwrite) id launcher; 18 | @property (nonatomic, retain, readwrite) id receiver; 19 | @property (nonatomic, copy, readwrite) NSArray *stages; 20 | @end 21 | 22 | @implementation TDPipeline 23 | 24 | + (TDPipeline *)pipleineWithLauncher:(id )l receiver:(id )r stages:(NSArray *)stages { 25 | return [[[self alloc] initWithLauncher:l receiver:r stages:stages] autorelease]; 26 | } 27 | 28 | 29 | - (instancetype)initWithLauncher:(id )l receiver:(id )r stages:(NSArray *)stages { 30 | self = [super init]; 31 | if (self) { 32 | self.launcher = l; 33 | self.receiver = r; 34 | self.stages = stages; 35 | } 36 | return self; 37 | } 38 | 39 | 40 | - (void)dealloc { 41 | self.launcher = nil; 42 | self.receiver = nil; 43 | self.stages = nil; 44 | 45 | self.delegate = nil; 46 | [super dealloc]; 47 | } 48 | 49 | 50 | #pragma mark - 51 | #pragma mark Public 52 | 53 | - (BOOL)runWithError:(NSError **)outErr { 54 | BOOL success = YES; 55 | 56 | self.launcherProgress = 0.0; 57 | self.receiverProgress = 0.0; 58 | 59 | id ic = [[self newChannel] autorelease]; 60 | id oc = nil; 61 | 62 | NSAssert(_stages, @""); 63 | for (TDPipelineStage *stage in _stages) { 64 | stage.delegate = self; 65 | 66 | oc = [[self newChannel] autorelease]; 67 | 68 | [stage setUpWithInputChannel:ic outputChannel:oc]; 69 | 70 | ic = oc; 71 | } 72 | 73 | oc = _stages.firstObject.inputChannel; // yes ic of first stage is the oc for launcher. 74 | ic = _stages.lastObject.outputChannel; // yes oc of last stage is the ic for receiver. 75 | 76 | [NSThread detachNewThreadWithBlock:^{ 77 | NSAssert(_launcher, @""); 78 | [_launcher launchWithPipeline:self outputChannel:oc]; 79 | }]; 80 | 81 | [NSThread detachNewThreadWithBlock:^{ 82 | NSAssert(_receiver, @""); 83 | [_receiver receiveWithPipeline:self inputChannel:ic]; 84 | }]; 85 | 86 | return success; 87 | } 88 | 89 | 90 | #pragma mark - 91 | #pragma mark Private 92 | 93 | - (id )newChannel { 94 | return [[TDBoundedBuffer alloc] initWithSize:5]; // TODO how to configure this? 95 | } 96 | 97 | 98 | #pragma mark - 99 | #pragma mark TDPipelineStageDelegate 100 | 101 | - (void)pipelineStageProgressDidUpdate:(TDPipelineStage *)ps { 102 | NSAssert([_stages containsObject:ps], @""); 103 | if (_delegate) { 104 | [(id)_delegate performSelectorOnMainThread:@selector(pipelineProgressDidUpdate:) withObject:self waitUntilDone:NO]; 105 | } 106 | } 107 | 108 | @end 109 | -------------------------------------------------------------------------------- /src/TDPipelineStage.m: -------------------------------------------------------------------------------- 1 | // 2 | // TDPipelineStage.m 3 | // TDThreadUtils 4 | // 5 | // Created by Todd Ditchendorf on 4/14/25. 6 | // Copyright © 2025 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "TDRunner.h" 12 | 13 | @interface TDPipelineStage () 14 | @property (nonatomic, assign, readwrite) TDPipelineStageType type; 15 | @property (nonatomic, retain, readwrite) Class workerClass; 16 | 17 | @property (nonatomic, assign, readwrite) NSUInteger runnerCount; 18 | @property (nonatomic, copy, readwrite) NSArray *runners; 19 | 20 | // Stage private API 21 | - (void)setUpWithInputChannel:(id )ic outputChannel:(id )oc; 22 | @property (nonatomic, retain, readwrite) id inputChannel; 23 | @property (nonatomic, retain, readwrite) id outputChannel; 24 | @end 25 | 26 | @implementation TDPipelineStage 27 | 28 | + (TDPipelineStage *)pipelineStageWithType:(TDPipelineStageType)type runnableClass:(Class)cls runnerCount:(NSUInteger)c { 29 | return [[[self alloc] initWithType:type runnableClass:cls runnerCount:c] autorelease]; 30 | } 31 | 32 | 33 | - (instancetype)initWithType:(TDPipelineStageType)type runnableClass:(Class)cls runnerCount:(NSUInteger)c { 34 | self = [super init]; 35 | if (self) { 36 | self.type = type; 37 | self.workerClass = cls; 38 | self.runnerCount = c; 39 | } 40 | return self; 41 | } 42 | 43 | 44 | - (void)dealloc { 45 | self.workerClass = nil; 46 | self.inputChannel = nil; 47 | self.outputChannel = nil; 48 | 49 | self.runners = nil; 50 | 51 | self.delegate = nil; 52 | [super dealloc]; 53 | } 54 | 55 | 56 | #pragma mark - 57 | #pragma mark Private 58 | 59 | - (void)setUpWithInputChannel:(id )ic outputChannel:(id )oc { 60 | NSAssert(ic, @""); 61 | NSAssert(oc, @""); 62 | 63 | self.inputChannel = ic; 64 | self.outputChannel = oc; 65 | 66 | NSMutableArray *runners = [NSMutableArray arrayWithCapacity:_runnerCount]; 67 | 68 | for (NSUInteger i = 0; i < _runnerCount; ++i) { 69 | id runnable = [[[_workerClass alloc] init] autorelease]; 70 | 71 | TDRunner *runner = [TDRunner runnerWithRunnable:runnable inputChannel:ic outputChannel:oc number:i+1]; 72 | runnable.delegate = runner; 73 | 74 | [runner addObserver:self forKeyPath:@"progress" options:0 context:NULL]; 75 | [runners addObject:runner]; 76 | } 77 | 78 | self.runners = runners; 79 | 80 | for (TDRunner *runner in _runners) { 81 | [NSThread detachNewThreadWithBlock:^{ 82 | [runner run]; 83 | }]; 84 | } 85 | } 86 | 87 | 88 | #pragma mark - 89 | #pragma mark KVO 90 | 91 | - (void)observeValueForKeyPath:(NSString *)path ofObject:(id)obj change:(NSDictionary *)change context:(void *)ctx { 92 | NSAssert([_runners containsObject:obj], @""); 93 | [_delegate pipelineStageProgressDidUpdate:self]; 94 | } 95 | 96 | @end 97 | -------------------------------------------------------------------------------- /src/TDPool.m: -------------------------------------------------------------------------------- 1 | // 2 | // TDPool.m 3 | // TDThreadUtils 4 | // 5 | // Created by Todd Ditchendorf on 5/21/15. 6 | // Copyright (c) 2015 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface TDPool () 13 | @property (retain) TDSemaphore *permits; 14 | @property (retain) NSMutableArray *available; 15 | @property (retain) NSMutableSet *busy; // `busy` collection is used only to ensure that returned objs were in fact previously taken from this pool. 16 | @end 17 | 18 | @implementation TDPool 19 | 20 | + (instancetype)poolWithItems:(NSArray *)items { 21 | return [[[self alloc] initWithItems:items] autorelease]; 22 | } 23 | 24 | 25 | - (instancetype)initWithItems:(NSArray *)items { 26 | NSParameterAssert(items); 27 | self = [super init]; 28 | if (self) { 29 | NSUInteger size = [items count]; 30 | NSAssert(NSNotFound != size, @""); 31 | NSAssert(size > 0, @""); 32 | 33 | self.permits = [TDSemaphore semaphoreWithValue:size]; 34 | self.available = [NSMutableArray arrayWithArray:items]; 35 | self.busy = [NSMutableSet setWithCapacity:size]; 36 | } 37 | return self; 38 | } 39 | 40 | 41 | - (void)dealloc { 42 | self.permits = nil; 43 | self.available = nil; 44 | self.busy = nil; 45 | [super dealloc]; 46 | } 47 | 48 | 49 | #pragma mark - 50 | #pragma mark Public 51 | 52 | - (id)takeItem { 53 | NSAssert(_permits, @""); 54 | [_permits acquire]; 55 | 56 | id obj = [self doTake]; 57 | NSAssert(obj, @""); 58 | return obj; 59 | } 60 | 61 | 62 | - (void)returnItem:(id)obj { 63 | NSParameterAssert(obj); 64 | 65 | if ([self doReturn:obj]) { 66 | NSAssert(_permits, @""); 67 | [_permits relinquish]; 68 | } 69 | } 70 | 71 | 72 | #pragma mark - 73 | #pragma mark Private 74 | 75 | - (id)doTake { 76 | id obj = nil; 77 | 78 | @synchronized(_permits) { 79 | NSAssert([_available count], @""); 80 | obj = [[[_available lastObject] retain] autorelease]; 81 | [_available removeLastObject]; 82 | 83 | NSAssert(_busy, @""); 84 | NSAssert(![_busy containsObject:obj], @""); 85 | [_busy addObject:obj]; 86 | } 87 | 88 | return obj; 89 | } 90 | 91 | 92 | - (BOOL)doReturn:(id)obj { 93 | NSParameterAssert(obj); 94 | BOOL result = YES; 95 | 96 | @synchronized(_permits) { 97 | NSAssert(_busy, @""); 98 | if ([_busy containsObject:obj]) { 99 | NSAssert(_available, @""); 100 | [_available addObject:obj]; 101 | 102 | [_busy removeObject:obj]; 103 | } else { 104 | result = NO; 105 | NSAssert(0, @""); 106 | } 107 | } 108 | 109 | return result; 110 | } 111 | 112 | @end 113 | -------------------------------------------------------------------------------- /src/TDRunner.h: -------------------------------------------------------------------------------- 1 | // 2 | // TDWorker.h 3 | // TDThreadUtils 4 | // 5 | // Created by Todd Ditchendorf on 4/14/25. 6 | // Copyright © 2025 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @protocol TDChannel; 13 | 14 | @interface TDRunner : NSObject 15 | 16 | + (TDRunner *)runnerWithRunnable:(id )runnable inputChannel:(id )ic outputChannel:(id )oc number:(NSUInteger)i; 17 | - (instancetype)initWithRunnable:(id )runnable inputChannel:(id )ic outputChannel:(id )oc number:(NSUInteger)i; 18 | 19 | - (void)run; 20 | 21 | @property (nonatomic, assign) CGFloat progress; 22 | @property (nonatomic, copy) NSString *titleText; 23 | @property (nonatomic, copy) NSString *infoText; 24 | @end 25 | -------------------------------------------------------------------------------- /src/TDRunner.m: -------------------------------------------------------------------------------- 1 | // 2 | // TDWorker.m 3 | // TDThreadUtils 4 | // 5 | // Created by Todd Ditchendorf on 4/14/25. 6 | // Copyright © 2025 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | @interface TDRunner () 14 | @property (nonatomic, retain) id runnable; 15 | @property (nonatomic, retain) id inputChannel; 16 | @property (nonatomic, retain) id outputChannel; 17 | @property (nonatomic, assign) NSUInteger number; 18 | @end 19 | 20 | @implementation TDRunner 21 | 22 | + (TDRunner *)runnerWithRunnable:(id )runnable inputChannel:(id )ic outputChannel:(id )oc number:(NSUInteger)i { 23 | return [[[self alloc] initWithRunnable:runnable inputChannel:ic outputChannel:oc number:i] autorelease]; 24 | } 25 | 26 | 27 | - (instancetype)initWithRunnable:runnable inputChannel:(id )ic outputChannel:(id )oc number:(NSUInteger)i { 28 | self = [super init]; 29 | if (self) { 30 | self.runnable = runnable; 31 | self.inputChannel = ic; 32 | self.outputChannel = oc; 33 | self.number = i; 34 | } 35 | return self; 36 | } 37 | 38 | 39 | - (void)dealloc { 40 | self.runnable = nil; 41 | self.inputChannel = nil; 42 | self.outputChannel = nil; 43 | 44 | self.titleText = nil; 45 | self.infoText = nil; 46 | [super dealloc]; 47 | } 48 | 49 | 50 | #pragma mark - 51 | #pragma mark Public 52 | 53 | - (void)run { 54 | NSAssert(_inputChannel, @""); 55 | 56 | self.progress = 0.0; 57 | 58 | for (;;) { 59 | id input = [_inputChannel take]; 60 | 61 | NSAssert(_runnable, @""); 62 | NSError *err = nil; 63 | id output = [_runnable runWithInput:input error:&err]; 64 | 65 | NSAssert(_outputChannel, @""); 66 | [_outputChannel put:output]; 67 | } 68 | } 69 | 70 | 71 | #pragma mark - 72 | #pragma mark TDRunnableDelegate 73 | 74 | - (void)runnable:(id)r updateProgress:(double)d { 75 | NSAssert(_runnable == r, @""); 76 | self.progress = d; 77 | } 78 | 79 | 80 | - (void)runnable:(id)r updateTitleText:(NSString *)title infoText:(NSString *)info { 81 | NSAssert(_runnable == r, @""); 82 | self.titleText = title; 83 | self.infoText = info; 84 | } 85 | 86 | @end 87 | -------------------------------------------------------------------------------- /src/TDSemaphore.m: -------------------------------------------------------------------------------- 1 | // 2 | // TDSemaphore.m 3 | // TDThreadUtils 4 | // 5 | // Created by Todd Ditchendorf on 5/31/13. 6 | // Copyright (c) 2013 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TDSemaphore () 12 | @property (assign) NSInteger value; 13 | @property (retain) NSCondition *monitor; 14 | @end 15 | 16 | @implementation TDSemaphore 17 | 18 | + (instancetype)semaphoreWithValue:(NSInteger)value { 19 | return [[[self alloc] initWithValue:value] autorelease]; 20 | } 21 | 22 | 23 | - (instancetype)initWithValue:(NSInteger)value { 24 | self = [super init]; 25 | if (self) { 26 | self.value = value; 27 | self.monitor = [[[NSCondition alloc] init] autorelease]; 28 | } 29 | return self; 30 | } 31 | 32 | 33 | - (void)dealloc { 34 | self.monitor = nil; 35 | [super dealloc]; 36 | } 37 | 38 | 39 | - (NSString *)description { 40 | return [NSString stringWithFormat:@"<%@ %p %ld>", [self class], self, _value]; 41 | } 42 | 43 | 44 | #pragma mark - 45 | #pragma mark Public 46 | 47 | - (BOOL)attempt { 48 | [[self retain] autorelease]; 49 | 50 | [self lock]; 51 | 52 | BOOL success = [self available]; 53 | 54 | if (success) { 55 | [self decrement]; 56 | } 57 | 58 | [self unlock]; 59 | 60 | return success; 61 | } 62 | 63 | 64 | - (BOOL)attemptBeforeDate:(NSDate *)limit { 65 | NSParameterAssert([self isValidDate:limit]); 66 | [[self retain] autorelease]; 67 | 68 | [self lock]; 69 | 70 | while ([self isValidDate:limit] && ![self available]) { 71 | [self waitUntilDate:limit]; 72 | } 73 | 74 | BOOL success = [self available]; 75 | 76 | if (success) { 77 | [self decrement]; 78 | } 79 | 80 | [self unlock]; 81 | 82 | return success; 83 | } 84 | 85 | 86 | - (void)acquire { 87 | [[self retain] autorelease]; 88 | [self lock]; 89 | 90 | while (![self available]) { 91 | [self wait]; 92 | } 93 | 94 | [self decrement]; 95 | [self unlock]; 96 | } 97 | 98 | 99 | - (void)relinquish { 100 | [[self retain] autorelease]; 101 | [self lock]; 102 | [self increment]; 103 | 104 | if ([self available]) { 105 | [self signal]; 106 | } 107 | 108 | [self unlock]; 109 | } 110 | 111 | 112 | #pragma mark - 113 | #pragma mark Private Business 114 | 115 | - (void)decrement { 116 | self.value--; 117 | } 118 | 119 | 120 | - (void)increment { 121 | self.value++; 122 | } 123 | 124 | 125 | - (BOOL)available { 126 | return _value > 0; 127 | } 128 | 129 | 130 | #pragma mark - 131 | #pragma mark Private Convenience 132 | 133 | - (void)lock { 134 | NSAssert(_monitor, @""); 135 | [_monitor lock]; 136 | } 137 | 138 | 139 | - (void)unlock { 140 | NSAssert(_monitor, @""); 141 | [_monitor unlock]; 142 | } 143 | 144 | 145 | - (void)wait { 146 | NSAssert(_monitor, @""); 147 | [_monitor wait]; 148 | } 149 | 150 | 151 | - (void)waitUntilDate:(NSDate *)date { 152 | NSAssert(_monitor, @""); 153 | [_monitor waitUntilDate:date]; 154 | } 155 | 156 | 157 | - (void)signal { 158 | NSAssert(_monitor, @""); 159 | [_monitor signal]; 160 | } 161 | 162 | 163 | - (BOOL)isValidDate:(NSDate *)limit { 164 | NSParameterAssert(limit); 165 | return [limit timeIntervalSinceNow] > 0.0; 166 | } 167 | 168 | @end 169 | -------------------------------------------------------------------------------- /src/TDSynchronousChannel.m: -------------------------------------------------------------------------------- 1 | // 2 | // TDSynchronousChannel.m 3 | // TDThreadUtils 4 | // 5 | // Created by Todd Ditchendorf on 5/19/14. 6 | // 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface TDSynchronousChannel () 13 | @property (retain) TDSemaphore *putPermit; 14 | @property (retain) TDSemaphore *takePermit; 15 | @property (retain) TDSemaphore *taken; 16 | @property (retain) id item; 17 | @end 18 | 19 | @implementation TDSynchronousChannel 20 | 21 | + (instancetype)synchronousChannel { 22 | return [[[self alloc] init] autorelease]; 23 | } 24 | 25 | 26 | - (instancetype)init { 27 | self = [super init]; 28 | if (self) { 29 | self.putPermit = [TDSemaphore semaphoreWithValue:1]; 30 | self.takePermit = [TDSemaphore semaphoreWithValue:0]; 31 | self.taken = [TDSemaphore semaphoreWithValue:0]; 32 | } 33 | return self; 34 | } 35 | 36 | 37 | - (void)dealloc { 38 | self.putPermit = nil; 39 | self.takePermit = nil; 40 | self.taken = nil; 41 | self.item = nil; 42 | [super dealloc]; 43 | } 44 | 45 | 46 | - (void)put:(id)obj { 47 | NSParameterAssert(obj); 48 | NSAssert(_putPermit, @""); 49 | NSAssert(_takePermit, @""); 50 | NSAssert(_taken, @""); 51 | 52 | [_putPermit acquire]; 53 | NSAssert(!_item, @""); 54 | self.item = obj; 55 | [_takePermit relinquish]; 56 | 57 | [_taken acquire]; // wait for someone to take it 58 | } 59 | 60 | 61 | - (id)take { 62 | NSAssert(_putPermit, @""); 63 | NSAssert(_takePermit, @""); 64 | NSAssert(_taken, @""); 65 | 66 | [_takePermit acquire]; 67 | NSAssert(_item, @""); 68 | id obj = [[_item retain] autorelease]; 69 | self.item = nil; 70 | [_putPermit relinquish]; 71 | 72 | [_taken relinquish]; // signal you've taken it 73 | 74 | NSAssert(obj, @""); 75 | return obj; 76 | } 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /src/TDThreshold.m: -------------------------------------------------------------------------------- 1 | // 2 | // TDThreshold.m 3 | // TDThreadUtils 4 | // 5 | // Created by Todd Ditchendorf on 1/12/15. 6 | // Copyright (c) 2015 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface TDThreshold () 13 | - (void)lock; 14 | - (void)unlock; 15 | 16 | - (void)decrement; 17 | 18 | - (BOOL)reached; 19 | - (void)wait; 20 | 21 | @property (assign) NSInteger value; 22 | @property (retain) NSCondition *monitor; 23 | @end 24 | 25 | @implementation TDThreshold 26 | 27 | + (instancetype)thresholdWithValue:(NSInteger)value { 28 | return [[[self alloc] initWithValue:value] autorelease]; 29 | } 30 | 31 | 32 | - (instancetype)initWithValue:(NSInteger)value { 33 | self = [super init]; 34 | if (self) { 35 | self.value = value; 36 | self.monitor = [[[NSCondition alloc] init] autorelease]; 37 | } 38 | return self; 39 | } 40 | 41 | 42 | - (void)dealloc { 43 | self.monitor = nil; 44 | [super dealloc]; 45 | } 46 | 47 | 48 | #pragma mark - 49 | #pragma mark TDSync 50 | 51 | - (void)await { 52 | [[self retain] autorelease]; 53 | [self lock]; 54 | 55 | [self decrement]; 56 | 57 | if ([self reached]) { 58 | [self broadcast]; 59 | } 60 | 61 | while (![self reached]) { 62 | [self wait]; 63 | } 64 | 65 | [self unlock]; 66 | } 67 | 68 | 69 | #pragma mark - 70 | #pragma mark Private Business 71 | 72 | - (void)decrement { 73 | self.value--; 74 | } 75 | 76 | 77 | - (BOOL)reached { 78 | return _value <= 0; 79 | } 80 | 81 | 82 | #pragma mark - 83 | #pragma mark Private Convenience 84 | 85 | - (void)lock { 86 | NSAssert(_monitor, @""); 87 | [_monitor lock]; 88 | } 89 | 90 | 91 | - (void)unlock { 92 | NSAssert(_monitor, @""); 93 | [_monitor unlock]; 94 | } 95 | 96 | 97 | - (void)wait { 98 | NSAssert(_monitor, @""); 99 | [_monitor wait]; 100 | } 101 | 102 | 103 | - (void)broadcast { 104 | NSAssert(_monitor, @""); 105 | [_monitor broadcast]; 106 | } 107 | 108 | @end 109 | -------------------------------------------------------------------------------- /src/TDTrigger.m: -------------------------------------------------------------------------------- 1 | // 2 | // TDTrigger.m 3 | // TDThreadUtils 4 | // 5 | // Created by Todd Ditchendorf on 1/12/15. 6 | // Copyright (c) 2015 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface TDTrigger () 13 | - (void)lock; 14 | - (void)unlock; 15 | 16 | - (BOOL)fired; 17 | - (void)wait; 18 | 19 | @property (assign) BOOL fired; 20 | @property (retain) NSCondition *monitor; 21 | @end 22 | 23 | @implementation TDTrigger 24 | 25 | + (instancetype)trigger { 26 | return [[[self alloc] init] autorelease]; 27 | } 28 | 29 | 30 | - (instancetype)init { 31 | self = [super init]; 32 | if (self) { 33 | self.fired = NO; 34 | self.monitor = [[[NSCondition alloc] init] autorelease]; 35 | } 36 | return self; 37 | } 38 | 39 | 40 | - (void)dealloc { 41 | self.monitor = nil; 42 | [super dealloc]; 43 | } 44 | 45 | 46 | #pragma mark - 47 | #pragma mark TDSync 48 | 49 | - (void)await { 50 | [[self retain] autorelease]; 51 | [self lock]; 52 | 53 | while (!self.fired) { 54 | [self wait]; 55 | } 56 | 57 | [self unlock]; 58 | } 59 | 60 | 61 | - (void)fire { 62 | [[self retain] autorelease]; 63 | [self lock]; 64 | 65 | self.fired = YES; 66 | 67 | [self broadcast]; 68 | 69 | [self unlock]; 70 | } 71 | 72 | 73 | #pragma mark - 74 | #pragma mark Private Convenience 75 | 76 | - (void)lock { 77 | NSAssert(_monitor, @""); 78 | [_monitor lock]; 79 | } 80 | 81 | 82 | - (void)unlock { 83 | NSAssert(_monitor, @""); 84 | [_monitor unlock]; 85 | } 86 | 87 | 88 | - (void)wait { 89 | NSAssert(_monitor, @""); 90 | [_monitor wait]; 91 | } 92 | 93 | 94 | - (void)broadcast { 95 | NSAssert(_monitor, @""); 96 | [_monitor broadcast]; 97 | } 98 | 99 | @end 100 | -------------------------------------------------------------------------------- /tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /tests/TDBaseTestCase.h: -------------------------------------------------------------------------------- 1 | // 2 | // TDBaseTestCase.m 3 | // TDBaseTestCase 4 | // 5 | // Created by Todd Ditchendorf on 1/12/15. 6 | // Copyright (c) 2015 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import "TDTest.h" 10 | 11 | @interface TDBaseTestCase : XCTestCase { 12 | XCTestExpectation *done; 13 | TDThreshold *threshold; 14 | BOOL flag; 15 | NSInteger counter; 16 | } 17 | 18 | - (void)performAtomic:(void(^)(void))block; 19 | - (void)performAtomic:(NSTimeInterval)delay :(void(^)(void))block; 20 | - (void)performAtomicInBackground:(void(^)(void))block; 21 | - (void)performAtomicInBackground:(NSTimeInterval)delay :(void(^)(void))block; 22 | 23 | @property (retain) XCTestExpectation *done; 24 | @property (retain) TDThreshold *threshold; 25 | @property (assign) BOOL flag; 26 | @property (assign) NSInteger counter; 27 | @end 28 | -------------------------------------------------------------------------------- /tests/TDBaseTestCase.m: -------------------------------------------------------------------------------- 1 | // 2 | // TDBaseTestCase.m 3 | // TDBaseTestCase 4 | // 5 | // Created by Todd Ditchendorf on 1/12/15. 6 | // Copyright (c) 2015 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import "TDBaseTestCase.h" 10 | 11 | @interface TDBaseTestCase () 12 | @property (assign) NSUInteger runnerCounter; 13 | @end 14 | 15 | @implementation TDBaseTestCase 16 | 17 | - (void)dealloc { 18 | self.done = nil; 19 | self.threshold = nil; 20 | [super dealloc]; 21 | } 22 | 23 | - (void)setUp { 24 | [super setUp]; 25 | [[NSThread currentThread] setName:@"MAIN"]; 26 | 27 | self.runnerCounter = 0; 28 | 29 | self.done = [self expectationWithDescription:@"done"]; 30 | self.flag = NO; 31 | self.counter = 0; 32 | } 33 | 34 | - (void)tearDown { 35 | self.done = nil; 36 | [super tearDown]; 37 | } 38 | 39 | - (void)performAtomic:(void(^)(void))block { 40 | TDNotNil(block); 41 | @synchronized(self) { 42 | block(); 43 | } 44 | } 45 | 46 | - (void)performAtomic:(NSTimeInterval)delay :(void(^)(void))block { 47 | TDPerformOnMainThreadAfterDelay(delay, ^{ 48 | TDNotNil(block); 49 | @synchronized(self) { 50 | block(); 51 | } 52 | }); 53 | } 54 | 55 | - (void)performAtomicInBackground:(void(^)(void))block { 56 | TDPerformOnBackgroundThread(^{ 57 | TDNotNil(block); 58 | @synchronized(self) { 59 | [self nameCurrentBackgroundThread]; 60 | block(); 61 | } 62 | }); 63 | } 64 | 65 | - (void)performAtomicInBackground:(NSTimeInterval)delay :(void(^)(void))block { 66 | TDPerformOnBackgroundThreadAfterDelay(delay, ^{ 67 | TDNotNil(block); 68 | @synchronized(self) { 69 | [self nameCurrentBackgroundThread]; 70 | block(); 71 | } 72 | }); 73 | } 74 | 75 | - (void)nameCurrentBackgroundThread { // PRE: lock held 76 | [[NSThread currentThread] setName:[NSString stringWithFormat:@"BG#%lu", ++self.runnerCounter]]; 77 | } 78 | 79 | @synthesize done=done; 80 | @synthesize threshold=threshold; 81 | @synthesize flag=flag; 82 | @synthesize counter=counter; 83 | @end 84 | -------------------------------------------------------------------------------- /tests/TDBoundedBufferTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // TDBoundedBufferTests.m 3 | // TDBoundedBufferTests 4 | // 5 | // Created by Todd Ditchendorf on 1/12/15. 6 | // Copyright (c) 2015 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import "TDBaseTestCase.h" 10 | 11 | #define ONE @"one" 12 | #define TWO @"two" 13 | #define THREE @"three" 14 | #define FOUR @"four" 15 | 16 | @interface TDBoundedBufferTests : TDBaseTestCase 17 | @property (retain) TDBoundedBuffer *buff; 18 | @end 19 | 20 | @implementation TDBoundedBufferTests 21 | 22 | - (void)setUp { 23 | [super setUp]; 24 | // here 25 | } 26 | 27 | - (void)tearDown { 28 | self.buff = nil; 29 | [super tearDown]; 30 | } 31 | 32 | - (void)test1Size1Obj1Thread { 33 | 34 | self.buff = [TDBoundedBuffer boundedBufferWithSize:1]; 35 | [buff put:ONE]; 36 | TDEqualObjects(ONE, [buff take]); 37 | 38 | [done fulfill]; 39 | 40 | [self waitForExpectationsWithTimeout:0.0 handler:^(NSError *err) { 41 | TDNil(err); 42 | }]; 43 | } 44 | 45 | - (void)test2Size2Obj1Thread { 46 | 47 | self.buff = [TDBoundedBuffer boundedBufferWithSize:2]; 48 | [buff put:ONE]; 49 | [buff put:TWO]; 50 | TDEqualObjects(ONE, [buff take]); 51 | [buff put:TWO]; 52 | TDEqualObjects(TWO, [buff take]); 53 | 54 | [done fulfill]; 55 | 56 | [self waitForExpectationsWithTimeout:0.0 handler:^(NSError *err) { 57 | TDNil(err); 58 | }]; 59 | } 60 | 61 | - (void)test1Size1Obj2Threads { 62 | 63 | self.buff = [TDBoundedBuffer boundedBufferWithSize:1]; 64 | 65 | TDPerformOnBackgroundThreadAfterDelay(0.3, ^{ 66 | self.counter++; 67 | [buff put:ONE]; 68 | }); 69 | 70 | TDEqualObjects(ONE, [buff take]); 71 | TDEquals(1, counter); 72 | 73 | [done fulfill]; 74 | 75 | [self waitForExpectationsWithTimeout:0.0 handler:^(NSError *err) { 76 | TDNil(err); 77 | TDEquals(1, counter); 78 | }]; 79 | } 80 | 81 | - (void)test1Size2Objs2Threads { 82 | 83 | self.buff = [TDBoundedBuffer boundedBufferWithSize:1]; 84 | 85 | TDPerformOnBackgroundThreadAfterDelay(0.5, ^{ 86 | self.counter++; 87 | [buff put:ONE]; 88 | self.counter++; 89 | [buff put:TWO]; 90 | }); 91 | 92 | TDEqualObjects(ONE, [buff take]); 93 | TDEqualObjects(TWO, [buff take]); 94 | TDEquals(2, counter); 95 | 96 | [done fulfill]; 97 | 98 | [self waitForExpectationsWithTimeout:0.0 handler:^(NSError *err) { 99 | TDNil(err); 100 | TDEquals(2, counter); 101 | }]; 102 | } 103 | 104 | - (void)test1Size2Objs3Threads { 105 | 106 | self.buff = [TDBoundedBuffer boundedBufferWithSize:1]; 107 | 108 | TDPerformOnBackgroundThread(^{ 109 | self.counter++; 110 | [buff put:ONE]; 111 | }); 112 | 113 | TDPerformOnBackgroundThreadAfterDelay(0.5, ^{ 114 | self.counter++; 115 | [buff put:TWO]; 116 | }); 117 | 118 | TDEqualObjects(ONE, [buff take]); 119 | TDEqualObjects(TWO, [buff take]); 120 | TDEquals(2, counter); 121 | 122 | [done fulfill]; 123 | 124 | [self waitForExpectationsWithTimeout:0.0 handler:^(NSError *err) { 125 | TDNil(err); 126 | TDFalse(flag); 127 | TDEquals(2, counter); 128 | }]; 129 | } 130 | 131 | - (void)test1Size2Objs4ThreadsPutOnMain { 132 | 133 | self.buff = [TDBoundedBuffer boundedBufferWithSize:1]; 134 | self.threshold = [TDThreshold thresholdWithValue:4]; 135 | 136 | TDPerformOnBackgroundThread(^{ 137 | id obj = [buff take]; 138 | TDTrue([obj isEqual:ONE] || [obj isEqual:TWO]); 139 | self.counter++; 140 | [threshold await]; 141 | }); 142 | 143 | TDPerformOnBackgroundThreadAfterDelay(0.5, ^{ 144 | [buff put:TWO]; 145 | self.counter++; 146 | [threshold await]; 147 | }); 148 | 149 | TDPerformOnBackgroundThread(^{ 150 | id obj = [buff take]; 151 | TDTrue([obj isEqual:ONE] || [obj isEqual:TWO]); 152 | self.counter++; 153 | [threshold await]; 154 | }); 155 | 156 | [buff put:ONE]; 157 | 158 | [threshold await]; 159 | 160 | TDEquals(3, counter); 161 | [done fulfill]; 162 | 163 | [self waitForExpectationsWithTimeout:0.0 handler:^(NSError *err) { 164 | TDNil(err); 165 | }]; 166 | } 167 | 168 | - (void)test1Size2Objs4ThreadsTakeOnMain { 169 | 170 | self.buff = [TDBoundedBuffer boundedBufferWithSize:1]; 171 | self.threshold = [TDThreshold thresholdWithValue:4]; 172 | 173 | TDPerformOnBackgroundThread(^{ 174 | id obj = [buff take]; 175 | TDTrue([obj isEqual:ONE] || [obj isEqual:TWO]); 176 | self.counter++; 177 | [threshold await]; 178 | }); 179 | 180 | TDPerformOnBackgroundThreadAfterDelay(0.5, ^{ 181 | [buff put:TWO]; 182 | self.counter++; 183 | [threshold await]; 184 | }); 185 | 186 | TDPerformOnBackgroundThread(^{ 187 | [buff put:ONE]; 188 | self.counter++; 189 | [threshold await]; 190 | }); 191 | 192 | id obj = [buff take]; 193 | TDTrue([obj isEqual:ONE] || [obj isEqual:TWO]); 194 | 195 | [threshold await]; 196 | TDEquals(3, counter); 197 | [done fulfill]; 198 | 199 | [self waitForExpectationsWithTimeout:0.0 handler:^(NSError *err) { 200 | TDNil(err); 201 | }]; 202 | } 203 | 204 | - (void)test1Size2Objs5Threads { 205 | 206 | self.buff = [TDBoundedBuffer boundedBufferWithSize:1]; 207 | self.threshold = [TDThreshold thresholdWithValue:5]; 208 | 209 | TDPerformOnBackgroundThread(^{ 210 | id obj = [buff take]; 211 | TDTrue([obj isEqual:ONE] || [obj isEqual:TWO]); 212 | self.counter++; 213 | [threshold await]; 214 | }); 215 | 216 | TDPerformOnBackgroundThreadAfterDelay(0.5, ^{ 217 | [buff put:TWO]; 218 | self.counter++; 219 | [threshold await]; 220 | }); 221 | 222 | TDPerformOnBackgroundThread(^{ 223 | [buff put:ONE]; 224 | self.counter++; 225 | [threshold await]; 226 | }); 227 | 228 | TDPerformOnBackgroundThread(^{ 229 | id obj = [buff take]; 230 | TDTrue([obj isEqual:ONE] || [obj isEqual:TWO]); 231 | 232 | self.counter++; 233 | [threshold await]; 234 | }); 235 | 236 | [threshold await]; 237 | TDEquals(4, counter); 238 | [done fulfill]; 239 | 240 | [self waitForExpectationsWithTimeout:0.0 handler:^(NSError *err) { 241 | TDNil(err); 242 | }]; 243 | } 244 | 245 | @synthesize buff=buff; 246 | @end 247 | -------------------------------------------------------------------------------- /tests/TDExchangerTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // TDExchangerTests.m 3 | // TDExchangerTests 4 | // 5 | // Created by Todd Ditchendorf on 1/12/15. 6 | // Copyright (c) 2015 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import "TDBaseTestCase.h" 10 | 11 | #define FOO @"foo" 12 | #define BAR @"bar" 13 | 14 | @interface TDExchangerTests : TDBaseTestCase 15 | @property (retain) TDExchanger *exchanger; 16 | @end 17 | 18 | @implementation TDExchangerTests 19 | 20 | - (void)setUp { 21 | [super setUp]; 22 | // here 23 | } 24 | 25 | - (void)tearDown { 26 | self.exchanger = nil; 27 | [super tearDown]; 28 | } 29 | 30 | - (void)test1Permit2Threads { 31 | 32 | self.exchanger = [TDExchanger exchanger]; 33 | 34 | TDAtomicOnBackgroundThread(^{ 35 | TDFalse(flag); 36 | self.flag = YES; 37 | TDEqualObjects(FOO, [exchanger exchange:BAR]); 38 | }); 39 | 40 | TDFalse(flag); 41 | TDEqualObjects(BAR, [exchanger exchange:FOO]); 42 | TDTrue(flag); 43 | 44 | [done fulfill]; 45 | 46 | [self waitForExpectationsWithTimeout:0.0 handler:^(NSError *err) { 47 | TDNil(err); 48 | TDTrue(flag); 49 | }]; 50 | } 51 | 52 | - (void)test1Permit2ThreadsDelay { 53 | 54 | self.exchanger = [TDExchanger exchanger]; 55 | 56 | TDAtomicOnBackgroundThreadAfterDelay(0.5, ^{ 57 | TDFalse(flag); 58 | self.flag = YES; 59 | TDEqualObjects(FOO, [exchanger exchange:BAR]); 60 | }); 61 | 62 | TDFalse(flag); 63 | TDEqualObjects(BAR, [exchanger exchange:FOO]); 64 | TDTrue(flag); 65 | 66 | [done fulfill]; 67 | 68 | [self waitForExpectationsWithTimeout:0.0 handler:^(NSError *err) { 69 | TDNil(err); 70 | TDTrue(flag); 71 | }]; 72 | } 73 | 74 | @synthesize exchanger=exchanger; 75 | @end 76 | -------------------------------------------------------------------------------- /tests/TDGamePlayerTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // TDPoolTests.m 3 | // TDPoolTests 4 | // 5 | // Created by Todd Ditchendorf on 1/12/15. 6 | // Copyright (c) 2015 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import "TDBaseTestCase.h" 10 | 11 | @interface ThreadSafeCounter : NSObject { 12 | NSUInteger _c; 13 | } 14 | - (void)increment; 15 | - (NSUInteger)sample; 16 | @end 17 | 18 | @implementation ThreadSafeCounter 19 | 20 | - (void)increment { 21 | @synchronized(self) { 22 | _c++; 23 | } 24 | } 25 | 26 | 27 | - (NSUInteger)sample { 28 | NSUInteger c = 0; 29 | @synchronized(self) { 30 | c = _c; 31 | } 32 | return c; 33 | } 34 | 35 | @end 36 | 37 | @interface Incrementer : NSObject 38 | @property (retain) ThreadSafeCounter *tsc; 39 | @property (assign) BOOL wantsEven; 40 | @end 41 | 42 | @implementation Incrementer 43 | 44 | - (id)gamePlayer:(TDGamePlayer *)p doTurnWithInput:(id)ignored { 45 | TDAssert(![NSThread isMainThread]); 46 | [self.tsc increment]; 47 | 48 | NSUInteger c = [self.tsc sample]; 49 | 50 | BOOL isEven = (c % 2 == 0); 51 | BOOL wantsEven = self.wantsEven; 52 | 53 | TDAssert((isEven && wantsEven) || (!isEven && !wantsEven)); 54 | return nil; 55 | } 56 | 57 | @end 58 | 59 | @interface Incrementer2 : NSObject 60 | @property (retain) ThreadSafeCounter *tsc; 61 | @property (assign) BOOL wantsEven; 62 | @end 63 | 64 | @implementation Incrementer2 65 | 66 | - (id)gamePlayer:(TDGamePlayer *)p doTurnWithInput:(ThreadSafeCounter *)tsc { 67 | TDAssert(![NSThread isMainThread]); 68 | TDAssert([tsc isKindOfClass:[ThreadSafeCounter class]]); 69 | [tsc increment]; 70 | 71 | NSUInteger c = [tsc sample]; 72 | 73 | BOOL isEven = (c % 2 == 0); 74 | BOOL wantsEven = self.wantsEven; 75 | 76 | TDAssert((isEven && wantsEven) || (!isEven && !wantsEven)); 77 | return tsc; 78 | } 79 | 80 | @end 81 | 82 | @interface TDGamePlayerTests : TDBaseTestCase 83 | @property (retain) TDGamePlayer *p1; 84 | @property (retain) TDGamePlayer *p2; 85 | @end 86 | 87 | @implementation TDGamePlayerTests 88 | 89 | - (void)setUp { 90 | [super setUp]; 91 | // here 92 | } 93 | 94 | - (void)tearDown { 95 | [p1 stop]; 96 | [p2 stop]; 97 | 98 | self.p1 = nil; 99 | self.p2 = nil; 100 | [super tearDown]; 101 | } 102 | 103 | - (void)testGameWithGlobalCounter { 104 | ThreadSafeCounter *tsc = [[[ThreadSafeCounter alloc] init] autorelease]; 105 | 106 | Incrementer *inc1 = [[[Incrementer alloc] init] autorelease]; 107 | inc1.tsc = tsc; 108 | inc1.wantsEven = NO; 109 | 110 | Incrementer *inc2 = [[[Incrementer alloc] init] autorelease]; 111 | inc2.tsc = tsc; 112 | inc2.wantsEven = YES; 113 | 114 | self.p1 = [[[TDGamePlayer alloc] initWithDelegate:inc1] autorelease]; 115 | self.p2 = [[[TDGamePlayer alloc] initWithDelegate:inc2] autorelease]; 116 | 117 | p1.opponent = p2; 118 | p2.opponent = p1; 119 | 120 | [p1 giveFirstTurnWithInput:nil]; 121 | 122 | TDPerformOnBackgroundThread(^{ 123 | [p1 run]; 124 | }); 125 | TDPerformOnBackgroundThread(^{ 126 | [p2 run]; 127 | }); 128 | 129 | TDPerformOnMainThreadAfterDelay(2.0, ^{ 130 | [p1 stop]; 131 | [p2 stop]; 132 | 133 | TDPerformOnMainThreadAfterDelay(0.0, ^{ 134 | [done fulfill]; 135 | }); 136 | }); 137 | 138 | [self waitForExpectationsWithTimeout:10.0 handler:^(NSError *err) { 139 | NSUInteger c = [tsc sample]; 140 | NSLog(@"Turns taken : %@", @(c)); 141 | NSLog(@""); 142 | }]; 143 | } 144 | 145 | 146 | - (void)testGameWithArgumentCounter { 147 | ThreadSafeCounter *tsc = [[[ThreadSafeCounter alloc] init] autorelease]; 148 | 149 | Incrementer2 *inc1 = [[[Incrementer2 alloc] init] autorelease]; 150 | inc1.wantsEven = NO; 151 | 152 | Incrementer2 *inc2 = [[[Incrementer2 alloc] init] autorelease]; 153 | inc2.wantsEven = YES; 154 | 155 | self.p1 = [[[TDGamePlayer alloc] initWithDelegate:inc1] autorelease]; 156 | self.p2 = [[[TDGamePlayer alloc] initWithDelegate:inc2] autorelease]; 157 | 158 | p1.opponent = p2; 159 | p2.opponent = p1; 160 | 161 | [p1 giveFirstTurnWithInput:tsc]; 162 | 163 | TDPerformOnBackgroundThread(^{ 164 | [p1 run]; 165 | }); 166 | TDPerformOnBackgroundThread(^{ 167 | [p2 run]; 168 | }); 169 | 170 | TDPerformOnMainThreadAfterDelay(2.0, ^{ 171 | [p1 stop]; 172 | [p2 stop]; 173 | 174 | TDPerformOnMainThreadAfterDelay(0.0, ^{ 175 | [done fulfill]; 176 | }); 177 | }); 178 | 179 | [self waitForExpectationsWithTimeout:10.0 handler:^(NSError *err) { 180 | NSUInteger c = [tsc sample]; 181 | NSLog(@"Turns taken : %@", @(c)); 182 | NSLog(@""); 183 | }]; 184 | } 185 | 186 | @synthesize p1=p1; 187 | @synthesize p2=p2; 188 | @end 189 | -------------------------------------------------------------------------------- /tests/TDLinkedQueueTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // TDLinkedQueueTests.m 3 | // TDLinkedQueueTests 4 | // 5 | // Created by Todd Ditchendorf on 1/12/15. 6 | // Copyright (c) 2015 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import "TDBaseTestCase.h" 10 | 11 | @interface TDLinkedQueueTests : TDBaseTestCase 12 | @property (retain) TDLinkedQueue *queue; 13 | @end 14 | 15 | @implementation TDLinkedQueueTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // here 20 | } 21 | 22 | - (void)tearDown { 23 | self.queue = nil; 24 | [super tearDown]; 25 | } 26 | 27 | - (void)test1Item1Thread { 28 | 29 | id obj1 = @"one"; 30 | self.queue = [[[TDLinkedQueue alloc] init] autorelease]; 31 | 32 | [queue put:obj1]; 33 | id obj = [queue take]; 34 | TDEqualObjects(@"one", obj); 35 | 36 | [done fulfill]; 37 | 38 | [self waitForExpectationsWithTimeout:0.0 handler:^(NSError *err) { 39 | TDNil(err); 40 | }]; 41 | } 42 | 43 | - (void)test2Items1Thread { 44 | 45 | id obj1 = @"one"; 46 | id obj2 = @"two"; 47 | self.queue = [[[TDLinkedQueue alloc] init] autorelease]; 48 | [queue put:obj1]; 49 | [queue put:obj2]; 50 | 51 | id poll1 = [queue take]; 52 | TDEqualObjects(@"one", poll1); 53 | 54 | id poll2 = [queue take]; 55 | TDEqualObjects(@"two", poll2); 56 | 57 | [done fulfill]; 58 | 59 | [self waitForExpectationsWithTimeout:0.0 handler:^(NSError *err) { 60 | TDNil(err); 61 | }]; 62 | } 63 | 64 | - (void)test1Item2ThreadsSwap { 65 | 66 | id obj1 = @"one"; 67 | id obj2 = @"two"; 68 | self.queue = [[[TDLinkedQueue alloc] init] autorelease]; 69 | [queue put:obj1]; 70 | [queue put:obj2]; 71 | 72 | self.threshold = [TDThreshold thresholdWithValue:1]; 73 | 74 | TDPerformOnBackgroundThread(^{ 75 | id obj = [queue take]; 76 | TDEqualObjects(@"one", obj); 77 | [queue put:obj]; 78 | self.counter++; 79 | [self.threshold await]; 80 | [done fulfill]; 81 | }); 82 | 83 | [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *err) { 84 | TDNil(err); 85 | TDEquals(1, counter); 86 | 87 | id took1 = [queue take]; 88 | TDEqualObjects(@"two", took1); 89 | id took2 = [queue take]; 90 | TDEqualObjects(@"one", took2); 91 | }]; 92 | } 93 | 94 | - (void)test2Items2Threads { 95 | 96 | id obj1 = @"one"; 97 | id obj2 = @"two"; 98 | self.queue = [[[TDLinkedQueue alloc] init] autorelease]; 99 | [queue put:obj1]; 100 | [queue put:obj2]; 101 | 102 | self.threshold = [TDThreshold thresholdWithValue:2]; 103 | 104 | TDPerformOnBackgroundThreadAfterDelay(0.1, ^{ 105 | id obj = [queue take]; 106 | TDEqualObjects(@"two", obj); 107 | [queue put:obj]; 108 | self.counter++; 109 | [self.threshold await]; 110 | }); 111 | 112 | id obj = [queue take]; 113 | TDEqualObjects(@"one", obj); 114 | self.counter++; 115 | [self.threshold await]; 116 | [queue put:obj]; 117 | 118 | [done fulfill]; 119 | 120 | [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *err) { 121 | TDNil(err); 122 | TDEquals(2, counter); 123 | 124 | id took1 = [queue take]; 125 | TDEqualObjects(@"two", took1); 126 | id took2 = [queue take]; 127 | TDEqualObjects(@"one", took2); 128 | }]; 129 | } 130 | 131 | @synthesize queue=queue; 132 | @end 133 | -------------------------------------------------------------------------------- /tests/TDPoolTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // TDPoolTests.m 3 | // TDPoolTests 4 | // 5 | // Created by Todd Ditchendorf on 1/12/15. 6 | // Copyright (c) 2015 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import "TDBaseTestCase.h" 10 | 11 | @interface TDPoolTests : TDBaseTestCase 12 | @property (retain) TDPool *pool; 13 | @end 14 | 15 | @implementation TDPoolTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // here 20 | } 21 | 22 | - (void)tearDown { 23 | self.pool = nil; 24 | [super tearDown]; 25 | } 26 | 27 | - (void)test1Item1Thread { 28 | 29 | id obj1 = @"one"; 30 | self.pool = [TDPool poolWithItems:@[obj1]]; 31 | 32 | id obj = [pool takeItem]; 33 | TDEqualObjects(@"one", obj); 34 | 35 | [pool returnItem:obj]; 36 | [done fulfill]; 37 | 38 | [self waitForExpectationsWithTimeout:0.0 handler:^(NSError *err) { 39 | TDNil(err); 40 | }]; 41 | } 42 | 43 | - (void)test2Items1Thread { 44 | 45 | id obj1 = @"one"; 46 | id obj2 = @"two"; 47 | self.pool = [TDPool poolWithItems:@[obj1, obj2]]; 48 | 49 | id took1 = [pool takeItem]; 50 | TDEqualObjects(@"two", took1); 51 | 52 | id took2 = [pool takeItem]; 53 | TDEqualObjects(@"one", took2); 54 | 55 | [pool returnItem:took1]; 56 | [pool returnItem:took2]; 57 | [done fulfill]; 58 | 59 | [self waitForExpectationsWithTimeout:0.0 handler:^(NSError *err) { 60 | TDNil(err); 61 | }]; 62 | } 63 | 64 | 65 | - (void)test2Items1ThreadSwap { 66 | 67 | id obj1 = @"one"; 68 | id obj2 = @"two"; 69 | self.pool = [TDPool poolWithItems:@[obj1, obj2]]; 70 | 71 | id took1 = [pool takeItem]; 72 | TDEqualObjects(@"two", took1); 73 | 74 | id took2 = [pool takeItem]; 75 | TDEqualObjects(@"one", took2); 76 | 77 | [pool returnItem:took2]; 78 | 79 | id took3 = [pool takeItem]; 80 | TDEqualObjects(@"one", took3); 81 | 82 | [pool returnItem:took1]; 83 | [pool returnItem:took3]; 84 | [done fulfill]; 85 | 86 | [self waitForExpectationsWithTimeout:0.0 handler:^(NSError *err) { 87 | TDNil(err); 88 | }]; 89 | } 90 | 91 | - (void)test1Item2Threads { 92 | 93 | id obj1 = @"one"; 94 | id obj2 = @"two"; 95 | self.pool = [TDPool poolWithItems:@[obj1, obj2]]; 96 | self.threshold = [TDThreshold thresholdWithValue:1]; 97 | 98 | TDPerformOnBackgroundThread(^{ 99 | id obj = [pool takeItem]; 100 | TDEqualObjects(@"two", obj); 101 | [pool returnItem:obj]; 102 | self.counter++; 103 | [self.threshold await]; 104 | [done fulfill]; 105 | }); 106 | 107 | [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *err) { 108 | TDNil(err); 109 | TDEquals(1, counter); 110 | 111 | id took1 = [pool takeItem]; 112 | TDEqualObjects(@"two", took1); 113 | id took2 = [pool takeItem]; 114 | TDEqualObjects(@"one", took2); 115 | }]; 116 | } 117 | 118 | - (void)test2Items2Threads { 119 | 120 | id obj1 = @"one"; 121 | id obj2 = @"two"; 122 | self.pool = [TDPool poolWithItems:@[obj1, obj2]]; 123 | self.threshold = [TDThreshold thresholdWithValue:2]; 124 | 125 | TDPerformOnBackgroundThreadAfterDelay(0.1, ^{ 126 | id obj = [pool takeItem]; 127 | TDEqualObjects(@"one", obj); 128 | [pool returnItem:obj]; 129 | self.counter++; 130 | [self.threshold await]; 131 | }); 132 | 133 | id obj = [pool takeItem]; 134 | TDEqualObjects(@"two", obj); 135 | self.counter++; 136 | [self.threshold await]; 137 | [pool returnItem:obj]; 138 | 139 | [done fulfill]; 140 | 141 | [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *err) { 142 | TDNil(err); 143 | TDEquals(2, counter); 144 | 145 | id took1 = [pool takeItem]; 146 | TDEqualObjects(@"two", took1); 147 | id took2 = [pool takeItem]; 148 | TDEqualObjects(@"one", took2); 149 | }]; 150 | } 151 | 152 | - (void)test2Items3Threads { 153 | 154 | id obj1 = @"one"; 155 | id obj2 = @"two"; 156 | self.pool = [TDPool poolWithItems:@[obj1, obj2]]; 157 | self.threshold = [TDThreshold thresholdWithValue:3]; 158 | 159 | TDPerformOnBackgroundThreadAfterDelay(0.1, ^{ 160 | id obj = [pool takeItem]; 161 | TDEqualObjects(@"one", obj); 162 | TDEquals(1, counter); 163 | self.counter++; 164 | sleep(2); 165 | [pool returnItem:obj]; 166 | [self.threshold await]; 167 | }); 168 | 169 | TDPerformOnBackgroundThreadAfterDelay(0.5, ^{ 170 | TDEquals(2, counter); 171 | id obj = [pool takeItem]; 172 | TDEqualObjects(@"one", obj); 173 | self.counter++; 174 | [self.threshold await]; 175 | [pool returnItem:obj]; 176 | }); 177 | 178 | id obj = [pool takeItem]; 179 | TDEqualObjects(@"two", obj); 180 | TDEquals(0, counter); 181 | self.counter++; 182 | [self.threshold await]; 183 | [pool returnItem:obj]; 184 | 185 | [done fulfill]; 186 | 187 | [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *err) { 188 | TDNil(err); 189 | TDEquals(3, counter); 190 | 191 | id took1 = [pool takeItem]; 192 | TDEqualObjects(@"two", took1); 193 | id took2 = [pool takeItem]; 194 | TDEqualObjects(@"one", took2); 195 | }]; 196 | } 197 | 198 | @synthesize pool=pool; 199 | @end 200 | -------------------------------------------------------------------------------- /tests/TDSemaphoreTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // TDSemaphoreTests.m 3 | // TDSemaphoreTests 4 | // 5 | // Created by Todd Ditchendorf on 1/12/15. 6 | // Copyright (c) 2015 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import "TDBaseTestCase.h" 10 | 11 | @interface TDSemaphore () 12 | @property (assign) NSInteger value; 13 | @end 14 | 15 | @interface TDSemaphoreTests : TDBaseTestCase 16 | @property (retain) TDSemaphore *sem; 17 | @end 18 | 19 | @implementation TDSemaphoreTests 20 | 21 | - (void)setUp { 22 | [super setUp]; 23 | // here 24 | } 25 | 26 | - (void)tearDown { 27 | self.sem = nil; 28 | [super tearDown]; 29 | } 30 | 31 | - (void)test1Permit2Threads { 32 | 33 | self.sem = [TDSemaphore semaphoreWithValue:1]; 34 | self.threshold = [TDThreshold thresholdWithValue:2]; 35 | TDEquals(1, sem.value); 36 | 37 | [sem acquire]; 38 | TDEquals(0, sem.value); 39 | 40 | TDPerformOnBackgroundThreadAfterDelay(0.1, ^{ 41 | TDEquals(0, sem.value); 42 | [sem relinquish]; 43 | TDEquals(1, sem.value); 44 | 45 | [threshold await]; 46 | }); 47 | 48 | [threshold await]; 49 | [done fulfill]; 50 | 51 | [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *err) { 52 | TDNil(err); 53 | TDEquals(1, sem.value); 54 | }]; 55 | } 56 | 57 | - (void)test2Permits2Threads { 58 | 59 | self.sem = [TDSemaphore semaphoreWithValue:2]; 60 | self.threshold = [TDThreshold thresholdWithValue:2]; 61 | TDEquals(2, sem.value); 62 | 63 | [sem acquire]; 64 | TDEquals(1, sem.value); 65 | [sem acquire]; 66 | TDEquals(0, sem.value); 67 | 68 | TDPerformOnBackgroundThreadAfterDelay(0.1, ^{ 69 | TDEquals(0, sem.value); 70 | [sem relinquish]; 71 | TDEquals(1, sem.value); 72 | 73 | [sem relinquish]; 74 | TDEquals(2, sem.value); 75 | 76 | [threshold await]; 77 | }); 78 | 79 | [threshold await]; 80 | [done fulfill]; 81 | 82 | [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *err) { 83 | TDNil(err); 84 | TDEquals(2, sem.value); 85 | }]; 86 | } 87 | 88 | - (void)test3Permits2Threads { 89 | 90 | self.sem = [TDSemaphore semaphoreWithValue:3]; 91 | self.threshold = [TDThreshold thresholdWithValue:2]; 92 | TDEquals(3, sem.value); 93 | 94 | [sem acquire]; 95 | TDEquals(2, sem.value); 96 | [sem acquire]; 97 | TDEquals(1, sem.value); 98 | [sem acquire]; 99 | TDEquals(0, sem.value); 100 | 101 | TDPerformOnBackgroundThreadAfterDelay(0.1, ^{ 102 | TDEquals(0, sem.value); 103 | [sem relinquish]; 104 | TDEquals(1, sem.value); 105 | [sem relinquish]; 106 | TDEquals(2, sem.value); 107 | [sem relinquish]; 108 | TDEquals(3, sem.value); 109 | 110 | [threshold await]; 111 | }); 112 | 113 | [threshold await]; 114 | [done fulfill]; 115 | 116 | [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *err) { 117 | TDNil(err); 118 | TDEquals(3, sem.value); 119 | }]; 120 | } 121 | 122 | - (void)test2Permits5Threads { 123 | 124 | self.sem = [TDSemaphore semaphoreWithValue:2]; 125 | self.threshold = [TDThreshold thresholdWithValue:5]; 126 | TDEquals(2, sem.value); 127 | 128 | TDPerformOnBackgroundThreadAfterDelay(0.1, ^{ 129 | [sem acquire]; 130 | TDTrue(sem.value < 2); 131 | [threshold await]; 132 | }); 133 | TDPerformOnBackgroundThread(^{ 134 | [sem acquire]; 135 | TDTrue(sem.value < 2); 136 | [threshold await]; 137 | }); 138 | 139 | TDPerformOnBackgroundThreadAfterDelay(0.1, ^{ 140 | [sem relinquish]; 141 | TDTrue(sem.value > 0 && sem.value <= 2); 142 | [threshold await]; 143 | }); 144 | TDPerformOnBackgroundThreadAfterDelay(0.2, ^{ 145 | [sem relinquish]; 146 | TDTrue(sem.value > 0 && sem.value <= 2); 147 | [threshold await]; 148 | }); 149 | 150 | [threshold await]; 151 | [done fulfill]; 152 | 153 | [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *err) { 154 | TDNil(err); 155 | TDEquals(2, sem.value); 156 | }]; 157 | } 158 | 159 | @synthesize sem=sem; 160 | @end 161 | -------------------------------------------------------------------------------- /tests/TDSynchronousChannelTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // TDSynchronousChannelTests.m 3 | // TDSynchronousChannelTests 4 | // 5 | // Created by Todd Ditchendorf on 1/12/15. 6 | // Copyright (c) 2015 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import "TDBaseTestCase.h" 10 | 11 | #define FOOBAR @"foobar" 12 | 13 | @interface TDSynchronousChannel () 14 | @property (retain) id item; 15 | @end 16 | 17 | @interface TDSynchronousChannelTests : TDBaseTestCase 18 | @property (retain) TDSynchronousChannel *chan; 19 | @end 20 | 21 | @implementation TDSynchronousChannelTests 22 | 23 | - (void)setUp { 24 | [super setUp]; 25 | // here 26 | } 27 | 28 | - (void)tearDown { 29 | self.chan = nil; 30 | [super tearDown]; 31 | } 32 | 33 | - (void)test1Permit2Threads { 34 | 35 | self.chan = [TDSynchronousChannel synchronousChannel]; 36 | 37 | TDAtomicOnBackgroundThread(^{ 38 | TDFalse(flag); 39 | self.flag = YES; 40 | TDEqualObjects(FOOBAR, [chan take]); 41 | TDNil(chan.item); 42 | }); 43 | 44 | TDFalse(flag); 45 | [chan put:FOOBAR]; 46 | TDTrue(flag); 47 | TDEqualObjects(nil, chan.item); 48 | 49 | [done fulfill]; 50 | 51 | [self waitForExpectationsWithTimeout:0.0 handler:^(NSError *err) { 52 | TDNil(err); 53 | TDNil(chan.item); 54 | TDTrue(flag); 55 | }]; 56 | } 57 | 58 | - (void)test1Permit2ThreadsDelay { 59 | 60 | self.chan = [TDSynchronousChannel synchronousChannel]; 61 | 62 | TDAtomicOnBackgroundThreadAfterDelay(0.5, ^{ 63 | TDFalse(flag); 64 | self.flag = YES; 65 | TDEqualObjects(FOOBAR, [chan take]); 66 | TDNil(chan.item); 67 | }); 68 | 69 | TDFalse(flag); 70 | [chan put:FOOBAR]; 71 | TDTrue(flag); 72 | TDEqualObjects(nil, chan.item); 73 | 74 | [done fulfill]; 75 | 76 | [self waitForExpectationsWithTimeout:0.0 handler:^(NSError *err) { 77 | TDNil(err); 78 | TDNil(chan.item); 79 | TDTrue(flag); 80 | }]; 81 | } 82 | 83 | @synthesize chan=chan; 84 | @end 85 | -------------------------------------------------------------------------------- /tests/TDTest.h: -------------------------------------------------------------------------------- 1 | // 2 | // TDTest.h 3 | // TDTestUtils 4 | // 5 | // Created by Todd Ditchendorf on 12/6/14. 6 | // Copyright (c) 2014 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | #define TDTrue(e) XCTAssertTrue((e), @"") 14 | #define TDFalse(e) XCTAssertFalse((e), @"") 15 | #define TDNil(e) XCTAssertNil((e), @"") 16 | #define TDNotNil(e) XCTAssertNotNil((e), @"") 17 | #define TDEquals(e1, e2) XCTAssertEqual((e1), (e2), @"") 18 | #define TDEqualObjects(e1, e2) XCTAssertEqualObjects((e1), (e2), @"") 19 | #define TDFail() XCTFail(@"") 20 | 21 | #define TDAssert(expr) NSAssert((expr), @"assertion failure in %s.", __PRETTY_FUNCTION__); 22 | #define TDCAssert(expr) NSCAssert((expr), @"assertion failure in %s.", __PRETTY_FUNCTION__); 23 | 24 | #define TDAtomicOnMainThread(block) [self performAtomic:(block)] 25 | #define TDAtomicOnMainThreadAfterDelay(delay, block) [self performAtomic:(delay) :(block)] 26 | #define TDAtomicOnBackgroundThread(block) [self performAtomicInBackground:(block)] 27 | #define TDAtomicOnBackgroundThreadAfterDelay(delay, block) [self performAtomicInBackground:(delay) :(block)] 28 | 29 | void TDPerformOnMainThread(void (^block)(void)); 30 | void TDPerformOnBackgroundThread(void (^block)(void)); 31 | void TDPerformOnMainThreadAfterDelay(double delay, void (^block)(void)); 32 | void TDPerformOnBackgroundThreadAfterDelay(double delay, void (^block)(void)); 33 | -------------------------------------------------------------------------------- /tests/TDTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // TDTest.m 3 | // TDTestUtils 4 | // 5 | // Created by Todd Ditchendorf on 12/6/14. 6 | // Copyright (c) 2014 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import "TDTest.h" 10 | 11 | void TDPerformOnMainThread(void (^block)(void)) { 12 | TDCAssert(block); 13 | dispatch_async(dispatch_get_main_queue(), block); 14 | } 15 | 16 | 17 | void TDPerformOnBackgroundThread(void (^block)(void)) { 18 | TDCAssert(block); 19 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block); 20 | } 21 | 22 | 23 | void TDPerformOnMainThreadAfterDelay(double delay, void (^block)(void)) { 24 | TDCAssert(block); 25 | TDCAssert(delay >= 0.0); 26 | 27 | double delayInSeconds = delay; 28 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); 29 | dispatch_after(popTime, dispatch_get_main_queue(), block); 30 | } 31 | 32 | 33 | void TDPerformOnBackgroundThreadAfterDelay(double delay, void (^block)(void)) { 34 | TDCAssert(block); 35 | TDCAssert(delay >= 0.0); 36 | 37 | double delayInSeconds = delay; 38 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); 39 | dispatch_after(popTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block); 40 | } 41 | -------------------------------------------------------------------------------- /tests/TDThresholdTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // TDThresholdTests.m 3 | // TDThresholdTests 4 | // 5 | // Created by Todd Ditchendorf on 1/12/15. 6 | // Copyright (c) 2015 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import "TDBaseTestCase.h" 10 | 11 | @interface TDThresholdTests : TDBaseTestCase 12 | @property (retain) TDThreshold *th; 13 | @end 14 | 15 | @implementation TDThresholdTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // here 20 | } 21 | 22 | - (void)tearDown { 23 | self.th = nil; 24 | [super tearDown]; 25 | } 26 | 27 | - (void)test1Permit1Thread { 28 | 29 | self.th = [TDThreshold thresholdWithValue:1]; 30 | 31 | self.counter++; 32 | [th await]; 33 | 34 | [done fulfill]; 35 | 36 | [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *err) { 37 | TDNil(err); 38 | TDEquals(1, counter); 39 | }]; 40 | } 41 | 42 | - (void)test1Permit2Threads { 43 | 44 | self.th = [TDThreshold thresholdWithValue:1]; 45 | 46 | TDPerformOnBackgroundThread(^{ 47 | self.counter++; 48 | [th await]; 49 | [done fulfill]; 50 | }); 51 | 52 | [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *err) { 53 | TDNil(err); 54 | TDEquals(1, counter); 55 | }]; 56 | } 57 | 58 | - (void)test2Permits2Threads { 59 | 60 | self.th = [TDThreshold thresholdWithValue:2]; 61 | 62 | TDPerformOnBackgroundThreadAfterDelay(0.1, ^{ 63 | self.counter++; 64 | [th await]; 65 | }); 66 | 67 | self.counter++; 68 | [th await]; 69 | [done fulfill]; 70 | 71 | [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *err) { 72 | TDNil(err); 73 | TDEquals(2, counter); 74 | }]; 75 | } 76 | 77 | - (void)test3Permits3Threads { 78 | 79 | self.th = [TDThreshold thresholdWithValue:3]; 80 | 81 | TDPerformOnBackgroundThreadAfterDelay(0.1, ^{ 82 | self.counter++; 83 | [th await]; 84 | }); 85 | 86 | TDPerformOnBackgroundThreadAfterDelay(0.2, ^{ 87 | self.counter++; 88 | [th await]; 89 | }); 90 | 91 | self.counter++; 92 | [th await]; 93 | [done fulfill]; 94 | 95 | [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *err) { 96 | TDNil(err); 97 | TDEquals(3, counter); 98 | }]; 99 | } 100 | 101 | - (void)test4Permits4Threads { 102 | 103 | self.th = [TDThreshold thresholdWithValue:4]; 104 | 105 | TDPerformOnBackgroundThreadAfterDelay(0.1, ^{ 106 | self.counter++; 107 | [th await]; 108 | }); 109 | 110 | TDPerformOnBackgroundThreadAfterDelay(0.2, ^{ 111 | self.counter++; 112 | [th await]; 113 | }); 114 | 115 | TDPerformOnBackgroundThreadAfterDelay(0.3, ^{ 116 | self.counter++; 117 | [th await]; 118 | }); 119 | 120 | self.counter++; 121 | [th await]; 122 | [done fulfill]; 123 | 124 | [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *err) { 125 | TDNil(err); 126 | TDEquals(4, counter); 127 | }]; 128 | } 129 | 130 | @synthesize th=th; 131 | @end 132 | -------------------------------------------------------------------------------- /tests/TDTriggerTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // TDTriggerTests.m 3 | // TDTriggerTests 4 | // 5 | // Created by Todd Ditchendorf on 1/12/15. 6 | // Copyright (c) 2015 Todd Ditchendorf. All rights reserved. 7 | // 8 | 9 | #import "TDBaseTestCase.h" 10 | 11 | @interface TDTriggerTests : TDBaseTestCase 12 | @property (retain) TDTrigger *trig; 13 | @end 14 | 15 | @implementation TDTriggerTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // here 20 | } 21 | 22 | - (void)tearDown { 23 | self.trig = nil; 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testThread { 28 | 29 | self.trig = [TDTrigger trigger]; 30 | 31 | self.counter++; 32 | [trig fire]; 33 | [done fulfill]; 34 | 35 | [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *err) { 36 | TDNil(err); 37 | TDEquals(1, counter); 38 | }]; 39 | } 40 | 41 | - (void)test2Threads { 42 | 43 | self.trig = [TDTrigger trigger]; 44 | 45 | TDPerformOnBackgroundThread(^{ 46 | self.counter++; 47 | [trig fire]; 48 | }); 49 | 50 | [trig await]; 51 | [done fulfill]; 52 | 53 | [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *err) { 54 | TDNil(err); 55 | TDEquals(1, counter); 56 | }]; 57 | } 58 | 59 | - (void)test2ThreadsDelay { 60 | 61 | self.trig = [TDTrigger trigger]; 62 | 63 | TDPerformOnBackgroundThreadAfterDelay(0.2, ^{ 64 | self.counter++; 65 | [trig fire]; 66 | }); 67 | 68 | [trig await]; 69 | [done fulfill]; 70 | 71 | [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *err) { 72 | TDNil(err); 73 | TDEquals(1, counter); 74 | }]; 75 | } 76 | 77 | - (void)test3Permits3Threads { 78 | 79 | self.trig = [TDTrigger trigger]; 80 | self.threshold = [TDThreshold thresholdWithValue:3]; 81 | 82 | TDPerformOnBackgroundThread(^{ 83 | [trig await]; 84 | self.counter++; 85 | [threshold await]; 86 | }); 87 | 88 | TDPerformOnBackgroundThread(^{ 89 | [trig await]; 90 | self.counter++; 91 | [threshold await]; 92 | }); 93 | 94 | TDPerformOnBackgroundThreadAfterDelay(0.2, ^{ 95 | TDEquals(0, counter); 96 | [trig fire]; 97 | 98 | [threshold await]; 99 | TDEquals(2, counter); 100 | 101 | [done fulfill]; 102 | }); 103 | 104 | [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *err) { 105 | TDNil(err); 106 | TDEquals(2, counter); 107 | }]; 108 | } 109 | 110 | - (void)test3Permits3ThreadsDelay { 111 | 112 | self.trig = [TDTrigger trigger]; 113 | self.threshold = [TDThreshold thresholdWithValue:3]; 114 | 115 | TDPerformOnBackgroundThreadAfterDelay(0.1, ^{ 116 | [trig await]; 117 | self.counter++; 118 | [threshold await]; 119 | }); 120 | 121 | TDPerformOnBackgroundThreadAfterDelay(0.2, ^{ 122 | [trig await]; 123 | self.counter++; 124 | [threshold await]; 125 | }); 126 | 127 | TDPerformOnBackgroundThreadAfterDelay(0.5, ^{ 128 | TDEquals(0, counter); 129 | [trig fire]; 130 | 131 | [threshold await]; 132 | TDEquals(2, counter); 133 | 134 | [done fulfill]; 135 | }); 136 | 137 | [self waitForExpectationsWithTimeout:5.0 handler:^(NSError *err) { 138 | TDNil(err); 139 | TDEquals(2, counter); 140 | }]; 141 | } 142 | 143 | @synthesize trig=trig; 144 | @end 145 | --------------------------------------------------------------------------------