Я использую 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 проблема с моделью памяти и т. Д.