1

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;
}
Artyom
  • 31,019
  • 21
  • 127
  • 215

2 Answers2

1

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

senojsitruc
  • 370
  • 2
  • 11
  • 1
    That's only undefined if you try rdlock when you currently have wrlock. This situation is a single thread doing two rdlocks. – paxdiablo Jan 10 '12 at 14:19
0

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.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • Quoting "A thread may hold multiple concurrent read locks on rwlock (that is, successfully call the pthread_rwlock_rdlock() function n times). If so, the thread must perform matching unlocks (that is, it must call the pthread_rwlock_unlock() function n times)." Also the first citation you had given refers to "acquiring read lock" not the case when such lock already acquired by the thread. It is correct that if there pending write lock new "read locks" can't be acquired but existing read locks can be reacquired" – Artyom Jan 10 '12 at 15:25
  • @Artyom, that's a matter of interpretation which is where I believe the implementations differ. _Acquiring the read lock_ as an action may include the recursive acquisition (you seem to assume the a second rdlock is not acquisition, I'm saying it may be considered so), so it would be blocked pending a waiting write lock request. In other words, while it states multiple rdlocks from a single thread are okay, it doesn't seem to fully specify what happens when you do a second rdlock when a wrlock is pending. – paxdiablo Jan 11 '12 at 00:02
  • I understand this but if you say that recursive locks allowed (it says this explicitly) then saying that you may not write lock in such situation as it may deadlock makes all recursive locking useless by definition. So even if you "interpret" the manual this way it still make no sense to support recursive locking. For example for Windows Vista/7 Slim RWLock it says explicitly that recursive locks are forbidden: http://msdn.microsoft.com/en-us/library/windows/desktop/aa904937%28v=vs.85%29.aspx – Artyom Jan 11 '12 at 09:47