Is this crash in a Swift script caused by a race condition?

Viewed 88

Running the code below usually prints:

hello

Occasionally, it prints:

hello BlockExperiment world

But sometimes I get the following crash:

hello BlockExperiment/usr/bin/swift[0x51f95a4]
/usr/bin/swift[0x51f719e]
/usr/bin/swift[0x51f987c]
/lib/x86_64-linux-gnu/libpthread.so.0(+0x12980)[0x7fc7c0650980]
/usr/lib/swift/linux/libswiftCore.so(+0x40d2c4)[0x7fc7bc9642c4]
/usr/lib/swift/linux/libswiftCore.so(+0x3fecc6)[0x7fc7bc955cc6]
/usr/lib/swift/linux/libswiftCore.so(+0x3fe20b)[0x7fc7bc95520b]
/usr/lib/swift/linux/libswiftCore.so(+0x3e456d)[0x7fc7bc93b56d]

This doesn't seem to break any of the weak/strong/unowned practices that I know of. Is the problem a basic race condition, where self is being accessed in the print statement after it has already been deallocated?

I can't be sure because in that case, I would expect the usual error message saying the access was to a deallocated instance. If it isn't this, how can you tell and in general debug this type of issue?

And if it is a race condition, why does the print statement get cut off sometimes after "hello"?

Code:

import Foundation

final class BlockRunner {
  var block: () -> ()
  init(_ b: @escaping () -> ()) { block = b }
  func run() { block() }
  deinit { print("Deinit: \(self)") }
}

final class BlockExperiment {
  func f() { 
    let queue = DispatchQueue(label: "Queue")
    let runner = BlockRunner { print("hello", self, "world") }
    queue.asyncAfter(deadline: .now() + 1) { runner.run() }
  }
  deinit { print("Deinit: \(self)") }
}

do {
  let e = BlockExperiment()
  e.f()
  RunLoop.current.run(until: Date() + 1 + 0.000000000000001)
}
1 Answers

That is definitely a race condition. If you need to print out anything, you need to do it on the main thread. Therefore, always wrap your print statements with something like this:

DispatchQueue.main.async {
   print("Whatever")
}

I was bitten by this just recently, when I didn't realize that Process() was running my task in a background thread and I was seeing the exactly same type of log text corruption.

Related