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?
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?
Your good question has a few problems with a complete answer:
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.