|  |  |  | //  Copyright (c) 2018 Open Whisper Systems. All rights reserved. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import Foundation | 
					
						
							|  |  |  | import SignalCoreKit | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // This is intended to be a drop-in replacement for DispatchQueue | 
					
						
							|  |  |  | // that processes its queue in reverse order. | 
					
						
							|  |  |  | @objc | 
					
						
							|  |  |  | public class ReverseDispatchQueue: NSObject { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private static let isVerbose: Bool = false | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private let label: String | 
					
						
							|  |  |  |     private let serialQueue: DispatchQueue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // TODO: We could allow creation with various QOS. | 
					
						
							|  |  |  |     @objc | 
					
						
							|  |  |  |     public required init(label: String) { | 
					
						
							|  |  |  |         self.label = label | 
					
						
							|  |  |  |         serialQueue = DispatchQueue(label: label) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         super.init() | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public typealias WorkBlock = () -> Void | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private class Item { | 
					
						
							|  |  |  |         let workBlock: WorkBlock | 
					
						
							|  |  |  |         let index: UInt64 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         required init(workBlock : @escaping WorkBlock, index: UInt64) { | 
					
						
							|  |  |  |             self.workBlock = workBlock | 
					
						
							|  |  |  |             self.index = index | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // These properties should only be accessed on serialQueue. | 
					
						
							|  |  |  |     private var items = [Item]() | 
					
						
							|  |  |  |     private var indexCounter: UInt64 = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @objc | 
					
						
							|  |  |  |     public func async(workBlock : @escaping WorkBlock) { | 
					
						
							|  |  |  |         serialQueue.async { | 
					
						
							|  |  |  |             self.indexCounter = self.indexCounter + 1 | 
					
						
							|  |  |  |             let index = self.indexCounter | 
					
						
							|  |  |  |             let item = Item(workBlock: workBlock, index: index ) | 
					
						
							|  |  |  |             self.items.append(item) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if ReverseDispatchQueue.isVerbose { | 
					
						
							|  |  |  |                 Logger.verbose("Enqueued[\(self.label)]: \(item.index)") | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             self.process() | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private func process() { | 
					
						
							|  |  |  |         serialQueue.async { | 
					
						
							|  |  |  |             // Note that we popLast() so that we process | 
					
						
							|  |  |  |             // the queue in the _reverse_ order from | 
					
						
							|  |  |  |             // which it was enqueued. | 
					
						
							|  |  |  |             guard let item = self.items.popLast() else { | 
					
						
							|  |  |  |                 // No enqueued work to do. | 
					
						
							|  |  |  |                 return | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if ReverseDispatchQueue.isVerbose { | 
					
						
							|  |  |  |                 Logger.verbose("Processing[\(self.label)]: \(item.index)") | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             item.workBlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             self.process() | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |