woshidan's loose leaf

ぼんやり勉強しています

Swift3でCountdownLatchを作る、あるいはSemaphoreのtimeoutをSwift3で書く

テストの都合でラッチが欲しかったので、GCDのSemaphoreのラッパークラスを書こうと思ったんですね。

それで、 https://github.com/zhuhaow/CountdownLatch を参考にすれば割と簡単では! と思ったんですが、GCDの記法がSwift3で変わりすぎていて死ぬかと思った。のでメモ。

// 参考元: https://github.com/zhuhaow/CountdownLatch
import Foundation

class CountDownLatch {
    var count: Int
    private let dispatchQueue = DispatchQueue(label: "CountdownQueue")
    // 参考: https://qiita.com/codelynx/items/56ce2f91cd3f4f409aeb
    let semaphore = DispatchSemaphore(value: 0)
    let timeInSec : Int
    
    init?(count: Int, timeInSec: Int) {
        guard count > 0 else {
            return nil
        }
        
        self.count = count
        self.timeInSec = timeInSec
    }
    
    func countdown() {
        dispatchQueue.async {
            self.count -= 1
            if self.count == 0 {
                self.semaphore.signal()
            }
        }
    }
    
    func await() -> Bool {
        // 参考: http://www.brilliantraven.com/code-snippets/grand-central-dispatch.html
        let result = semaphore.wait(timeout: DispatchTime.now() + DispatchTimeInterval.seconds(self.timeInSec))
        if (result == .success) {
            return true
        } else {
            return false
        }
    }
}

CouuntDownLatch を使う側のコードは以下

    func doTask() {
        let countDownLatch = CountDownLatch(count: 1, timeInSec: 30)!
        checkTimeoutBeforeCompletion(latch: countDownLatch)

        // なんか重い処理する

        countDownLatch.countdown()
    }

    func checkTimeoutBeforeCompletion(latch: CountDownLatch) {
        let queue = DispatchQueue(label: "background", attributes: .concurrent)
        queue.async {
            let processedOnTime = latch.await()
            if (processedOnTime) {
                NSLog("processed on time")
            } else {
                assertionFailure("遅いぞ!!!!")
            }
        }
    }