Crash when Mutating Array of Tuples with String Property from Multiple Threads

Hi Apple Developer Community,

I'm facing a crash when updating an array of tuples from both a background thread and the main thread simultaneously. Here's a simplified version of the code in a macOS app using AppKit:

class ViewController: NSViewController {
var mainthreadButton = NSButton(title: "test", target: self, action: nil)
var numbers = Array(repeating: (dim: Int, key: String)(0, "default"), count: 1000)
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(mainthreadButton)
mainthreadButton.translatesAutoresizingMaskIntoConstraints = false
mainthreadButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
mainthreadButton.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
mainthreadButton.widthAnchor.constraint(equalToConstant: 100).isActive = true
mainthreadButton.heightAnchor.constraint(equalToConstant: 100).isActive = true
mainthreadButton.target = self
mainthreadButton.action = #selector(arraytest(_:))
}
@objc func arraytest(_ sender: NSButton) {
print("array update started")
// Background update
DispatchQueue.global().async {
for i in 0..<1000 {
self.numbers[i].dim = i
}
}
// Main thread update
var sum = 0
for i in 0..<1000 {
numbers[i].dim = i + 1
sum += numbers[i].dim
print("test \(sum)")
}
mainthreadButton.title = "test = \(sum)"
}
}

This results in a crash with the following message:

malloc: double free for ptr 0x136040c00
malloc: *** set a breakpoint in malloc_error_break to debug

What's interesting:

This crash only happens when the tuple contains a String ((dim: Int, key: String))

If I change the tuple type to use two Int values ((dim: Int, key: Int)), the crash does not occur

My Questions:

Why does mutating an array of tuples containing a String crash when accessed from multiple threads?

Why is the crash avoided when the tuple contains only primitive types like Int?

Is there an underlying memory management issue with value types containing reference types like String?

Any explanation about this behavior and best practices for thread-safe mutation of such arrays would be much appreciated.

Thanks in advance!

Answered by DTS Engineer in 833852022
Written by kalaiyarasan in 779862021
Why does mutating an array of tuples containing a String crash when accessed from multiple threads?

Mutating the same variable from multiple threads is undefined behaviour in Swift [1]. It might work, it might fail, the exact symptoms of that failure can vary, and this might change between different compiler versions, different OS versions, and even from device to device.

Given that, it’s best to avoid writing code that relies on undefined behaviour. Presumably the simple demo you posted is not representative of your real code. If you explain more about what you’re actually trying to do, I should be able to offer advice on the best path forward.

ps You’re using the global concurrent queue. That’s fine for a demo like this, but it’s something to avoid in real code. See Avoid Dispatch Global Concurrent Queues for more on that topic.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Swift is not unique in this. The same applies to all the C-based languages as well.

Written by kalaiyarasan in 779862021
Why does mutating an array of tuples containing a String crash when accessed from multiple threads?

Mutating the same variable from multiple threads is undefined behaviour in Swift [1]. It might work, it might fail, the exact symptoms of that failure can vary, and this might change between different compiler versions, different OS versions, and even from device to device.

Given that, it’s best to avoid writing code that relies on undefined behaviour. Presumably the simple demo you posted is not representative of your real code. If you explain more about what you’re actually trying to do, I should be able to offer advice on the best path forward.

ps You’re using the global concurrent queue. That’s fine for a demo like this, but it’s something to avoid in real code. See Avoid Dispatch Global Concurrent Queues for more on that topic.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Swift is not unique in this. The same applies to all the C-based languages as well.

Crash when Mutating Array of Tuples with String Property from Multiple Threads
 
 
Q