With SE-0433 adding Mutex to the standard library. I'm wondering what the general advice is regarding using Mutex vs actor.
Here is a very simple case of a FIFO using Array as storage to illustrate.
FIFO has two methods of enqueuing and dequeuing. In the first case, FIFO is implemented using an actor and an Array as storage and in the second case, FIFO is a class using an Array as storage protected via the new Mutex.
// Tested with swift-PR-71383-1223.xctoolchain
import Foundation
import Synchronization
actor FIFOWithActor {
private var _storage = Array<Int>([])
func enqueue(item: Int) {
_storage.append(item)
}
func dequeue() -> Int? {
guard !_storage.isEmpty else { return nil }
return _storage.removeFirst()
}
}
class FIFOWithMutex {
private let _storage = Mutex<Array<Int>>([])
init() { }
func enqueue(item: Int) {
_storage.withLock { storage in
storage.append(item)
}
}
func dequeue() -> Int? {
_storage.withLock { storage in
guard !storage.isEmpty else { return nil }
return storage.removeFirst()
}
}
}
let fifoA = FIFOWithActor()
let fifoM = FIFOWithMutex()
let sender = Task.detached {
for i in 0..<100 {
print(">FIFOWithActor enqueue \(i)")
await fifoA.enqueue(item: i)
print(" >FIFOWithMutex enqueue \(i)")
fifoM.enqueue(item: i)
try await Task.sleep(for: .milliseconds(10))
}
return "finish sending"
}
let receiver = Task.detached {
let fifoM = FIFOWithMutex()
while true {
if let item = await fifoA.dequeue() {
print("<FIFOWithActor dequeue \(item)")
}
if let item = fifoM.dequeue() {
print(" <FIFOWithMutex dequeue \(item)")
}
try await Task.sleep(for: .milliseconds(10))
}
}
let result = await sender.result
print(result)
Beyond the fact that the implementation with Mutex is not restricted to use in an async/await concurrency context, what is the general advice regarding the use of Mutex vs actor?