Last active
September 16, 2016 13:38
-
-
Save sanekgusev/2c15212db889f1157607d62c19c77da3 to your computer and use it in GitHub Desktop.
Ensure that global GCD queues are drained (useful for avoiding undesired side-effects in unit tests)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // this method ensures that blocks enqueued onto global GCD queues by the previous test method | |
| // have finished executing and can have no side effects on the following test methods | |
| // NOTE: This does not affect any private queues: any queued, but unfinished blocks may still be running off those | |
| // unless properly cancelled by the code managing those queues | |
| func ensureGlobalQueuesDrained() { | |
| // all of the below only makes sense if we're running on the main thread | |
| assert(NSThread.isMainThread()) | |
| // construct an array of all concurrent global queues | |
| let globalQueues = [ | |
| QOS_CLASS_USER_INTERACTIVE, | |
| QOS_CLASS_USER_INITIATED, | |
| QOS_CLASS_UTILITY, | |
| QOS_CLASS_BACKGROUND | |
| ].map({ dispatch_get_global_queue($0, 0) }) | |
| // create a dispatch group | |
| let dispatchGroup = dispatch_group_create() | |
| // submit a barrier block to all of the global concurrent queues | |
| // the barrier blocks will only be executed after all the previously submitted blocks | |
| // have finished executing on their respective background queues | |
| globalQueues.forEach { | |
| dispatch_group_async(dispatchGroup, $0, dispatch_block_create(DISPATCH_BLOCK_BARRIER) {}) | |
| } | |
| // get a reference to main thread's CFRunLoop object | |
| let runloop = CFRunLoopGetCurrent() | |
| // schedule a block to be submitted onto the main dispatch queue after | |
| // all background queues have executed our blocks | |
| // by the time out block is called on the main queue, we can safely | |
| // assume that the global concurrent queues are drained | |
| // (the barrier blocks we enqueued above were the last ones to be run on them) | |
| // since main queue is a serial queue, it also means that by the time | |
| // the below block is started executing on the main queue, all the | |
| // previously submitted to the main queue blocks have finished running, | |
| // so the main queue is drained as well | |
| dispatch_group_notify(dispatchGroup, | |
| dispatch_get_main_queue()) { | |
| CFRunLoopStop(runloop) | |
| } | |
| // run a nested runloop on the main thread, so that main GCD queue can continue | |
| // processing enqueued blocks, including ours above | |
| // this nested runloop will be stopped by a CFRunLoopStop() call from the | |
| // block we've scheduled onto the main queue | |
| CFRunLoopRun() | |
| // at this point all global concurrent GCD queues are drained | |
| // AND the serial main GCD queue is drained as well | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment