Tidwall / Safe mutex тупик - PullRequest
       16

Tidwall / Safe mutex тупик

0 голосов
/ 28 июня 2019

Я использую https://github.com/tidwall/Safe, библиотеку параллелизма Swift, и я думаю, что обнаружил ошибку потоков. (Я использую IOS 12.3.1, iPhone Xs. Swift 4, я думаю; Xcode 10.2.) Сейчас библиотека доступна только для чтения, поэтому я пытаюсь отладить ее самостоятельно. Ошибка действительно очень тонкая, или она вызвана чем-то, чего я даже не представлял, потому что я делаю почти то же самое, что и данная библиотека, и она отлично работает, но сама библиотека блокируется.

Вот тестовый код, который блокируется, когда он не должен:

private func testCompetingDeadlock() {
    NSLog("start")
    let c = Chan<Int32>()
    let b = Chan<Int32>()
    let COUNT = 1000
    let wg = WaitGroup()
    wg.add(1)
    dispatch {
        NSLog("receiver starting")
        for i in 0..<(2*COUNT) {
            Thread.sleep(forTimeInterval: 0.01)
            let v = <-c
            b <- v!
        }
        wg.done()
    }
    sleep(1)
    wg.add(1)
    dispatch {
        NSLog("sender 1 starting")
        for i in 0..<COUNT {
            c <- 1
            <-b
            NSLog("1 : \(i)")
        }
        NSLog("1 done")
        wg.done()
    }
    wg.add(1)
    dispatch {
        NSLog("sender 2 starting")
        for i in 0..<COUNT {
            c <- 2
            <-b
            NSLog("2 : \(i)")
        }
        NSLog("2 done")
        wg.done()
    }
    wg.wait()
    NSLog("Both done")
}

Обратите внимание, что базовая реализация send, она же <-, равна

internal func send(_ msg: T) {
    NSLog("locking (\(Thread.current)) - \(Unmanaged.passUnretained(cond.mutex).toOpaque())")
    cond.mutex.lock()
    NSLog("locked (\(Thread.current)) - \(Unmanaged.passUnretained(cond.mutex).toOpaque())")
    threadCount += 1
    defer {
        threadCount -= 1
        NSLog("unlocking (\(Thread.current)) - \(Unmanaged.passUnretained(cond.mutex).toOpaque())")
        cond.mutex.unlock()
        NSLog("unlocked (\(Thread.current)) - \(Unmanaged.passUnretained(cond.mutex).toOpaque())")
    }
    if threadCount > 1 {
        NSLog("threadCount is \(threadCount)")
    }

    if closed {
        #if os(Linux)
        assertionFailure("Send on closed channel")
        #else
        NSException.raise(NSExceptionName(rawValue: "Exception"), format: "send on closed channel", arguments: getVaList([]))
        #endif
    }
    msgs.append(msg)
    broadcast()
    while msgs.count > cap {
        cond.wait()
    }
}

(Я добавил протоколирование и threadCount. После возникновения тупика, threadCount равно 2. Я попробовал то же самое «inc после блокировки, dec перед разблокировкой» в классе Mutex, и я получил 3 во время тупика ??? Я не знаю как, и я не исследовал это дальше, хотя это может быть важной подсказкой.)

Если запускается testCompetingDeadlock, обычно происходит немедленная взаимоблокировка, когда два потока-отправителя застревают в этой строке cond.wait() из send, оба находятся в пределах заблокированной зоны одного и того же мьютекса. Я не знаю как Я попытался протестировать сам Mutex, так же, как я воспринимаю send, чтобы использовать его, следующим образом:

private func testSafeMutex() {
    let mutex = Mutex()

    dispatch {
        NSLog("1 locking")
        mutex.lock()
        NSLog("1 locked")
        defer {
            NSLog("1 unlocking")
            mutex.unlock()
            NSLog("1 unlocked")
        }

        sleep(1)
    }

    dispatch {
        NSLog("2 locking")
        mutex.lock()
        NSLog("2 locked")
        defer {
            NSLog("2 unlocking")
            mutex.unlock()
            NSLog("2 unlocked")
        }

        sleep(1)
    }
}

Тем не менее, работает нормально - без тупиков.

Я не совсем уверен, что делать, кроме просто добавления более точного и мелкозернистого ведения журнала и попытки объединить два тестовых примера, пока не будет найдено принципиальное различие (что будет трудно, так как трудно поддерживать работоспособность кода). промежуточные версии). Кто-нибудь может помочь мне отладить эту библиотеку? Возможно, есть какая-то специфическая для iOS проблема с моделью памяти и т. Д.

...