5

How can I see (on linux) which threads own a pthread_rwlock_t (or std::shared_mutex) ?

For a regular mutex there's Is it possible to determine the thread holding a mutex? but how to do this for a r/w lock?

Folkert van Heusden
  • 433
  • 4
  • 17
  • 38

1 Answers1

1

Your good question has a few problems with a complete answer:

  1. It is dependent upon which OS kernel you are running, and possibly even which version.
  2. It is dependent upon which libc/libpthread you are using, and possibly even which compiler.

Presuming you can untangle a specific configuration above, we are looking at two different outcomes: there is a single writer, or a set of readers who currently cause pthread_rw_lock() to block for some callers. In contrast, a mutex has only ever one owner and only that owner can release it.

So a first test in finding out whether your system has a findable owner set is to see if it actually records the ownership. A quick hack to this would be have one thread acquire the rwlock for read or write, then signal a second one to release it. If the release fails, your implementation properly records it, and you have a chance; if not, your implementation likely implements rwlocks with mutex + condvar + counter; so it has no idea of ownership.

So, some code:

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

pthread_mutex_t lock;
pthread_cond_t cv;
pthread_rwlock_t rwl;
int ready;

void *rlse(void *_)
{
    int *t = _;
    pthread_mutex_lock(&lock);
    while (ready == 0) {
        pthread_cond_wait(&cv, &lock);
    }
    ready = 0;
    pthread_cond_signal(&cv);
    pthread_mutex_unlock(&lock);
    *t = pthread_rwlock_unlock(&rwl);
    return 0;
}

void *get(void *_)
{
    int *t = _;
    *t = (*t) ? pthread_rwlock_wrlock(&rwl) : pthread_rwlock_rdlock(&rwl);
    if (*t == 0) {
        pthread_mutex_lock(&lock);
        ready = 1;
        pthread_cond_signal(&cv);
        while (ready == 1) {
            pthread_cond_wait(&cv, &lock);
        }
        pthread_mutex_unlock(&lock);
    }
    return 0;
}

int main()
{
    pthread_t acq, rel;
    int v0, v1;
    int i;
    for (i = 0; i < 2; i++) {
        pthread_rwlock_init(&rwl, 0);
        pthread_mutex_init(&lock, 0);
        pthread_cond_init(&cv, 0);
        v0 = i;
        pthread_create(&acq, 0, get, &v0);
        pthread_create(&rel, 0, rlse, &v1);
        pthread_join(acq, 0);
        pthread_join(rel, 0);
        printf("%s: %d %d\n", i ? "write" : "read", v0, v1);
    }
    return 0;
}

which we run as:

u18:src $ cc rw.c -lpthread -o rw
u18:src $ ./rw
read: 0 0
write: 0 0

This is telling us that in either case (rdlock, wrlock), a thread different from the calling thread can release the rwlock, thus it fundamentally has no owner.

With a little less sense of discovery, we could have found out a bit by reading the manpage for pthread_rwlock_unlock, which states that this condition is undefined behavior, which is the great cop-out.

Posix establishes a base, not a limit, so it is possible your implementation can support this sort of ownership. I program like the one above is a good investigative tool; if it turns up something like ENOTOWNER; but EINVAL would be pretty non-committal.

The innards of glic's rwlock (sysdeps/htl/bits/types/struct___pthread_rwlock.h):

struct __pthread_rwlock
{
  __pthread_spinlock_t __held;
  __pthread_spinlock_t __lock;
  int __readers;
  struct __pthread *__readerqueue;
  struct __pthread *__writerqueue;
  struct __pthread_rwlockattr *__attr;
  void *__data;
};

confirms our suspicion; at first I was hopeful, with the queues and all, but a little diving through the code revealed them to be the waiting lists, not the owner lists.

The above was run on ubuntu 18.04; linux 4.15.0-112-generic; gcc 7.5.0; glibc 2.27 libpthread 2.27.

mevets
  • 10,070
  • 1
  • 21
  • 33