Thread sanitiser v mutex

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

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