| Summary: | pthread_rwlock_rdlock can deadlock | ||
|---|---|---|---|
| Product: | Base System | Reporter: | earl_chew <earl_chew> |
| Component: | kern | Assignee: | freebsd-threads (Nobody) <threads> |
| Status: | Closed FIXED | ||
| Severity: | Affects Only Me | ||
| Priority: | Normal | ||
| Version: | 3.3-RELEASE | ||
| Hardware: | Any | ||
| OS: | Any | ||
|
Description
earl_chew
2001-01-25 17:50:00 UTC
Responsible Changed From-To: freebsd-bugs->jasone I'll take a look at this. Responsible Changed From-To: jasone->freebsd-bugs Responsible Changed From-To: freebsd-bugs->freebsd-threads Assign to threads mailing list The Thodes wrote: > First, Earl, GCC objected to your test program. Apologies. I compiled this as a C++ program (g++) hence the missing parameter names. > The patch given is needed to make it compile (in my case, at least). Yes, your patch will fill in the "junk" names, and allow the C compiler to parse the source successfully. > Also, it (your test program) > doesn't even get to trying to acquire the second read lock. It hangs > trying to acquire the write lock. The output is provided in this (long) > follow-up. Oops... there was a cut-and-paste error. Please look at the amended program below. Earl -- #include <pthread.h> #include <stdio.h> #include <unistd.h> #include <assert.h> static pthread_rwlock_t rwlock1 = PTHREAD_RWLOCK_INITIALIZER; static volatile int wrStarted; void * wrfunc(void *unused) { printf("Attempt to acquire write lock\n"); assert(pthread_rwlock_wrlock(&rwlock1) == 0); printf("Acquired write lock\n"); assert(pthread_rwlock_unlock(&rwlock1) == 0); return 0; } static volatile int rdStarted; void * rdfunc(void *unused) { printf("Attempt to acquire read lock first\n"); assert(pthread_rwlock_rdlock(&rwlock1) == 0); printf("Acquired read lock first\n"); rdStarted = 1; while (wrStarted == 0) sleep(1); printf("Attempt to acquire read lock second\n"); assert(pthread_rwlock_rdlock(&rwlock1) == 0); printf("Acquired read lock second\n"); assert(pthread_rwlock_unlock(&rwlock1) == 0); assert(pthread_rwlock_unlock(&rwlock1) == 0); return 0; } int main(int argc, char **argv) { pthread_t wrt; pthread_t rdt; pthread_attr_t a; assert(pthread_rwlock_init(&rwlock1, 0) == 0); assert(pthread_attr_init(&a) == 0); assert(pthread_create(&rdt, &a, rdfunc, NULL) == 0); while (rdStarted == 0) sleep(1); assert(pthread_create(&wrt, &a, wrfunc, NULL) == 0); assert(pthread_join(wrt, 0) == 0); assert(pthread_join(rdt, 0) == 0); assert(pthread_rwlock_destroy(&rwlock1) == 0); assert(pthread_detach(wrt) == 0); assert(pthread_detach(rdt) == 0); return 0; } aspiesrule@mcleodusa.net wrote: > Same results, except for the fact that stepping thru your code in GDB yields > a segfault in KERNEL32!IsBadWritePtr. It must be the holidays! Ok, I've added the missing line at the beginning of wrfunc(). Earl -- #include <pthread.h> #include <stdio.h> #include <unistd.h> #include <assert.h> static pthread_rwlock_t rwlock1 = PTHREAD_RWLOCK_INITIALIZER; static volatile int wrStarted; void * wrfunc(void *unused) { wrStarted = 1; printf("Attempt to acquire write lock\n"); assert(pthread_rwlock_wrlock(&rwlock1) == 0); printf("Acquired write lock\n"); assert(pthread_rwlock_unlock(&rwlock1) == 0); return 0; } static volatile int rdStarted; void * rdfunc(void *unused) { printf("Attempt to acquire read lock first\n"); assert(pthread_rwlock_rdlock(&rwlock1) == 0); printf("Acquired read lock first\n"); rdStarted = 1; while (wrStarted == 0) sleep(1); printf("Attempt to acquire read lock second\n"); assert(pthread_rwlock_rdlock(&rwlock1) == 0); printf("Acquired read lock second\n"); assert(pthread_rwlock_unlock(&rwlock1) == 0); assert(pthread_rwlock_unlock(&rwlock1) == 0); return 0; } int main(int argc, char **argv) { pthread_t wrt; pthread_t rdt; pthread_attr_t a; assert(pthread_rwlock_init(&rwlock1, 0) == 0); assert(pthread_attr_init(&a) == 0); assert(pthread_create(&rdt, &a, rdfunc, NULL) == 0); while (rdStarted == 0) sleep(1); assert(pthread_create(&wrt, &a, wrfunc, NULL) == 0); assert(pthread_join(wrt, 0) == 0); assert(pthread_join(rdt, 0) == 0); assert(pthread_rwlock_destroy(&rwlock1) == 0); assert(pthread_detach(wrt) == 0); assert(pthread_detach(rdt) == 0); return 0; } aspiesrule@mcleodusa.net wrote: > Oops, running your new program yields an assertion failure at line 37 and a > core dump. The output is as follows: > > --snip-- > Attempt to acquire read lock first > Acquired read lock first > Attempt to acquire write lock > Attempt to acquire read lock second > assertion "pthread_rwlock_rdlock(&rwlock1) == 0" failed: file "test.c", line > 37 > Aborted (core dumped) Right. The test is (correctly) showing an inadequacy in the rwlock implementation. I refer you to the original bug report: http://www.freebsd.org/cgi/query-pr.cgi?pr=24641 and the referenced articles: http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=slrn87nusa.rsv.kaz%40ashi.FootPrints.net http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=38828D22.7A98%40LambdaCS.com Earl State Changed
From-To: open->closed
Problem does not occur in libpthread in FreeBSD 6.x.
Your testcase needs to be modified slightly.
If you call pthread_join(),
then if you call pthread_detach() later on, pthread_detach
may return EINVAL, because the thread may already be
terminated and detached, so you can't detach it.
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
static pthread_rwlock_t rwlock1 = PTHREAD_RWLOCK_INITIALIZER;
static volatile int wrStarted;
void * wrfunc(void *unused)
{
wrStarted = 1;
printf("Attempt to acquire write lockn");
assert(pthread_rwlock_wrlock(&rwlock1) == 0);
printf("Acquired write lockn");
assert(pthread_rwlock_unlock(&rwlock1) == 0);
return 0;
}
static volatile int rdStarted;
void * rdfunc(void *unused)
{
printf("Attempt to acquire read lock firstn");
assert(pthread_rwlock_rdlock(&rwlock1) == 0);
printf("Acquired read lock firstn");
rdStarted = 1;
while (wrStarted == 0)
sleep(1);
printf("Attempt to acquire read lock secondn");
assert(pthread_rwlock_rdlock(&rwlock1) == 0);
printf("Acquired read lock secondn");
assert(pthread_rwlock_unlock(&rwlock1) == 0);
assert(pthread_rwlock_unlock(&rwlock1) == 0);
return 0;
}
int
main(int argc, char **argv)
{
pthread_t wrt;
pthread_t rdt;
pthread_attr_t a;
int ret;
assert(pthread_rwlock_init(&rwlock1, 0) == 0);
assert(pthread_attr_init(&a) == 0);
assert(pthread_create(&rdt, &a, rdfunc, NULL) == 0);
while (rdStarted == 0)
sleep(1);
assert(pthread_create(&wrt, &a, wrfunc, NULL) == 0);
assert(pthread_join(wrt, 0) == 0);
assert(pthread_join(rdt, 0) == 0);
assert(pthread_rwlock_destroy(&rwlock1) == 0);
if ((ret = pthread_detach(wrt)) != 0) {
if (ret == EINVAL) {
printf("wrt already detachedn");
}
}
if ((ret = pthread_detach(rdt)) != 0) {
if (errno == EINVAL) {
printf("rdt already detachedn");
}
}
return 0;
}
|