Recursive pthread_rwlock_rdlock on Mac OS X/Darwin

1.3k views Asked by At

I have following sample code (see code below) that does by thread:

A: rd-lock
B: wr-lock (waiting)
A: rd-lock (recursive)
A: rd-unlock (recursive)
A: rd-unlock
B: wr-locked (wake after wait)
B: wr-unlock.

Basically read-lock is recursive. It it required by POSIX standards (requires read locks to be recursive, but not specified for write locks). This works on Linux, FreeBSD, Solaris, however it does not work on Darwin/Mac OS X.

The sample below gives following output on Linux:

read locking
read locked
write locking
read locking 2
read locked 2
read unlocked 2
read unlocked
write locked
write unlocked 2

While on Darwin it prints:

read locking
read locked
write locking
read locking 2

And deadlocks here (does not continue), basically it does not respect recursive read lock.

Is there anything that possible to do (flag, defined, link with a special library version) that it would work as expected?


Sample code

#include <pthread.h>
#include <stdio.h>

pthread_rwlock_t lock;

void *thread_r(void *p)
{
    printf("read locking\n");
    pthread_rwlock_rdlock(&lock);
    printf("read locked\n");
    usleep(500*1000);
    printf("read locking 2\n");
    pthread_rwlock_rdlock(&lock);
    printf("read locked 2\n");
    usleep(500*1000);
    pthread_rwlock_unlock(&lock);
    printf("read unlocked 2\n");
    usleep(500*1000);
    pthread_rwlock_unlock(&lock);
    printf("read unlocked\n");
}

void *thread_w(void *p)
{
    usleep(250*1000);
    printf("write locking\n");
    pthread_rwlock_wrlock(&lock);
    printf("write locked\n");
    pthread_rwlock_unlock(&lock);
    printf("write unlocked 2\n");
}

int main()
{
    pthread_t a,b;
    pthread_rwlock_init(&lock,NULL);
    pthread_create(&a,NULL,thread_r,0);
    pthread_create(&b,NULL,thread_w,0);
    pthread_join(a,NULL);
    pthread_join(b,NULL);
    return 0;
}
2

There are 2 answers

3
paxdiablo On

Yes, the locks are read locks on rwlocks are indeed recursive, up to a point. But there is a line in the POSIX docs for pthread_rwlock_rdlock, SUSv2 since that's what Apple suports:

The calling thread acquires the read lock if a writer does not hold the lock and there are no writers blocked on the lock. It is unspecified whether the calling thread acquires the lock when a writer does not hold the lock and there are writers waiting for the lock.

Nothing at all in there about letting a thread with an existing read lock relock for read. Just that the read lock request will block if a writer is blocked (implementations often give preference to write lock to avoid writer starvation).

Apple's own online docs also support this:

The pthread_rwlock_rdlock() function acquires a read lock on rwlock, provided that rwlock is not presently held for writing and no writer threads are presently blocked on the lock.

And, later:

To prevent writer starvation, writers are favored over readers.

Again. no mention of allowing recursive read locks while a write lock is in the queue.

1
senojsitruc On

Only rdlock() supports recursive locking:

http://pubs.opengroup.org/onlinepubs/007908799/xsh/pthread_rwlock_rdlock.html

According to the Unix specification, the behavior for a call to wrlock() is undefined if the thread already holds either a read or write lock:

http://pubs.opengroup.org/onlinepubs/007908799/xsh/pthread_rwlock_trywrlock.html

As you're working with OS X, take a look at NSRecursiveLock:

http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html