tera
1
I am getting thread sanitiser "Swift access race" on pthread_mutex_lock line. How do I use mutex properly in Swift?
import Foundation
class MiniTest {
var mutex = pthread_mutex_t()
init() {
pthread_mutex_init(&mutex, nil);
}
func threadProc() {
while true {
pthread_mutex_lock(&mutex) // ***
pthread_mutex_unlock(&mutex)
// *** Swift access race in MiniTest.MiniTest.mutex.modify : __C._opaque_pthread_mutex_t at 0x7b1400011260
}
}
func test() {
print("start")
let t0 = Thread(block: threadProc)
let t1 = Thread(block: threadProc)
t0.start()
t1.start()
print("sleep")
sleep(100)
print("done")
}
}
let x = MiniTest()
x.test()
print(x)
1 Like
Then again, you cannot use the ampersand operator with pthread_mutex_lock() (and generally with low-level locks and atomics). This is a programming error that everybody makes. See this, this and this .
8 Likes
lukasa
(Cory Benfield)
3
As @tclementdev correctly points out, you cannot store a pthread_mutext_t in a var and then use the ampersand operator to pass it by pointer. Specifically, it is possible that the pthread_mutex_t is not a reference to the mutex but instead is the mutex, and so must have a stable memory location (such that multiple threads can access it). This cannot be achieved in safe Swift.
The correct way to use pthread_mutex_t is to heap-allocate a pointer. The easiest example is in SwiftNIO: https://github.com/apple/swift-nio/blob/0467886d0b21599fdf011cd9a89c5c593dd650a7/Sources/NIOConcurrencyHelpers/lock.swift#L30-L53. Note that you still need to wrap this in a class to perform appropriate lifecycle management and to free the underlying pointer.
Of course, the better solution is not to use pthread_mutex_t at all but to use some wrapper around it. NSLock is fine for this use-case.
4 Likes