Commit c1b544f5 authored by Xavier Thompson's avatar Xavier Thompson

Fix contention detection with mutex instead of atomic operations

parent f34cecbf
...@@ -20,15 +20,6 @@ ...@@ -20,15 +20,6 @@
#define CyObject_NO_OWNER -1 #define CyObject_NO_OWNER -1
#define CyObject_MANY_OWNERS -2 #define CyObject_MANY_OWNERS -2
#define CyObject_WRITER_OFFSET (16)
#define CyObject_FETCH_CONTENDERS_ADD_WRITER(n) n.fetch_add((1 << CyObject_WRITER_OFFSET))
#define CyObject_FETCH_CONTENDERS_SUB_WRITER(n) n.fetch_sub((1 << CyObject_WRITER_OFFSET))
#define CyObject_FETCH_CONTENDERS_ADD_READER(n) n.fetch_add(1)
#define CyObject_FETCH_CONTENDERS_SUB_READER(n) n.fetch_sub(1)
#define CyObject_WRITERS_FROM_CONTENDERS(n) (n >> CyObject_WRITER_OFFSET)
#define CyObject_READERS_FROM_CONTENDERS(n) (n & ((1 << CyObject_WRITER_OFFSET) -1))
#define CyObject_HAS_WRITER_CONTENDERS(n) (n > (1 << CyObject_WRITER_OFFSET) - 1)
#define CyObject_CONTENDING_WRITER_FLAG (1 << 0) #define CyObject_CONTENDING_WRITER_FLAG (1 << 0)
#define CyObject_CONTENDING_READER_FLAG (1 << 1) #define CyObject_CONTENDING_READER_FLAG (1 << 1)
...@@ -50,7 +41,6 @@ ...@@ -50,7 +41,6 @@
pthread_cond_t wait_writer_depart; pthread_cond_t wait_writer_depart;
atomic<pid_t> owner_id; atomic<pid_t> owner_id;
atomic_int32_t readers_nb; atomic_int32_t readers_nb;
atomic_uint32_t contenders;
uint32_t write_count; uint32_t write_count;
public: public:
RecursiveUpgradeableRWLock() { RecursiveUpgradeableRWLock() {
...@@ -60,7 +50,6 @@ ...@@ -60,7 +50,6 @@
this->owner_id = CyObject_NO_OWNER; this->owner_id = CyObject_NO_OWNER;
this->readers_nb = 0; this->readers_nb = 0;
this->write_count = 0; this->write_count = 0;
this->contenders = 0;
} }
void wlock(); void wlock();
void rlock(); void rlock();
...@@ -327,8 +316,6 @@ ...@@ -327,8 +316,6 @@
void RecursiveUpgradeableRWLock::rlock() { void RecursiveUpgradeableRWLock::rlock() {
pid_t caller_id = syscall(SYS_gettid); pid_t caller_id = syscall(SYS_gettid);
CyObject_FETCH_CONTENDERS_ADD_READER(this->contenders);
if (this->owner_id == caller_id) { if (this->owner_id == caller_id) {
++this->readers_nb; ++this->readers_nb;
return; return;
...@@ -348,25 +335,17 @@ void RecursiveUpgradeableRWLock::rlock() { ...@@ -348,25 +335,17 @@ void RecursiveUpgradeableRWLock::rlock() {
int RecursiveUpgradeableRWLock::tryrlock() { int RecursiveUpgradeableRWLock::tryrlock() {
pid_t caller_id = syscall(SYS_gettid); pid_t caller_id = syscall(SYS_gettid);
int contenders = CyObject_FETCH_CONTENDERS_ADD_READER(this->contenders);
if (this->owner_id == caller_id) { if (this->owner_id == caller_id) {
++this->readers_nb; ++this->readers_nb;
return 0; return 0;
} }
if (CyObject_HAS_WRITER_CONTENDERS(contenders)) {
CyObject_FETCH_CONTENDERS_SUB_READER(this->contenders);
return CyObject_CONTENDING_WRITER_FLAG;
}
// we must lock here, because a trylock could fail also when another thread is currently read-locking or read-unlocking // we must lock here, because a trylock could fail also when another thread is currently read-locking or read-unlocking
// but this means we might miss a writer arriving and leaving // but this means we might miss a writer arriving and leaving
pthread_mutex_lock(&this->guard); pthread_mutex_lock(&this->guard);
if (this->write_count > 0) { if (this->write_count > 0) {
pthread_mutex_unlock(&this->guard); pthread_mutex_unlock(&this->guard);
CyObject_FETCH_CONTENDERS_SUB_READER(this->contenders);
return CyObject_CONTENDING_WRITER_FLAG; return CyObject_CONTENDING_WRITER_FLAG;
} }
...@@ -391,15 +370,11 @@ void RecursiveUpgradeableRWLock::unrlock() { ...@@ -391,15 +370,11 @@ void RecursiveUpgradeableRWLock::unrlock() {
} }
pthread_mutex_unlock(&this->guard); pthread_mutex_unlock(&this->guard);
CyObject_FETCH_CONTENDERS_SUB_READER(this->contenders);
} }
void RecursiveUpgradeableRWLock::wlock() { void RecursiveUpgradeableRWLock::wlock() {
pid_t caller_id = syscall(SYS_gettid); pid_t caller_id = syscall(SYS_gettid);
CyObject_FETCH_CONTENDERS_ADD_WRITER(this->contenders);
if (this->owner_id == caller_id) { if (this->owner_id == caller_id) {
if (this->write_count) { if (this->write_count) {
++this->write_count; ++this->write_count;
...@@ -442,8 +417,6 @@ void RecursiveUpgradeableRWLock::wlock() { ...@@ -442,8 +417,6 @@ void RecursiveUpgradeableRWLock::wlock() {
int RecursiveUpgradeableRWLock::trywlock() { int RecursiveUpgradeableRWLock::trywlock() {
pid_t caller_id = syscall(SYS_gettid); pid_t caller_id = syscall(SYS_gettid);
uint32_t contenders = CyObject_FETCH_CONTENDERS_ADD_WRITER(this->contenders);
if (this->owner_id == caller_id) { if (this->owner_id == caller_id) {
if (this->write_count) { if (this->write_count) {
++this->write_count; ++this->write_count;
...@@ -451,35 +424,17 @@ int RecursiveUpgradeableRWLock::trywlock() { ...@@ -451,35 +424,17 @@ int RecursiveUpgradeableRWLock::trywlock() {
} }
} }
if (CyObject_HAS_WRITER_CONTENDERS(contenders) > 0) { pthread_mutex_lock(&this->guard);
CyObject_FETCH_CONTENDERS_SUB_WRITER(this->contenders);
return CyObject_CONTENDING_WRITER_FLAG;
}
if (contenders > 0) {
CyObject_FETCH_CONTENDERS_SUB_WRITER(this->contenders);
return CyObject_CONTENDING_READER_FLAG;
}
if (pthread_mutex_trylock(&this->guard) != 0) {
// another thread is currently doing a lock operation on this lock, but we don't know what it is.
// we could choose to do a blocking lock instead of a trylock here.
// that way we would not detect when an unlock overlaps with this trylock,
// but also all the contending readers and / or writers could leave while we block,
// so we wouldn't detect those contentions either.
return CyObject_CONTENDING_WRITER_FLAG | CyObject_CONTENDING_READER_FLAG;
}
if (this->owner_id != caller_id) { if (this->owner_id != caller_id) {
if (this->readers_nb > 0) { if (this->readers_nb > 0) {
pthread_mutex_unlock(&this->guard); pthread_mutex_unlock(&this->guard);
CyObject_FETCH_CONTENDERS_SUB_WRITER(this->contenders);
return CyObject_CONTENDING_READER_FLAG; return CyObject_CONTENDING_READER_FLAG;
} }
if (this->write_count > 0) { if (this->write_count > 0) {
pthread_mutex_unlock(&this->guard); pthread_mutex_unlock(&this->guard);
CyObject_FETCH_CONTENDERS_SUB_WRITER(this->contenders);
return CyObject_CONTENDING_WRITER_FLAG; return CyObject_CONTENDING_WRITER_FLAG;
} }
...@@ -507,15 +462,12 @@ void RecursiveUpgradeableRWLock::unwlock() { ...@@ -507,15 +462,12 @@ void RecursiveUpgradeableRWLock::unwlock() {
} }
pthread_mutex_unlock(&this->guard); pthread_mutex_unlock(&this->guard);
CyObject_FETCH_CONTENDERS_SUB_WRITER(this->contenders);
} }
void LogLock::rlock() { void LogLock::rlock() {
pid_t caller_id = syscall(SYS_gettid); pid_t caller_id = syscall(SYS_gettid);
int contenders = CyObject_FETCH_CONTENDERS_ADD_READER(this->contenders);
if (this->owner_id == caller_id) { if (this->owner_id == caller_id) {
++this->readers_nb; ++this->readers_nb;
return; return;
...@@ -523,24 +475,14 @@ void LogLock::rlock() { ...@@ -523,24 +475,14 @@ void LogLock::rlock() {
pthread_mutex_lock(&this->guard); pthread_mutex_lock(&this->guard);
if (CyObject_HAS_WRITER_CONTENDERS(contenders)) { if (this->write_count > 0) {
if (this->write_count > 0) { pid_t owner_id = this->owner_id;
pid_t lock_owner = this->owner_id; printf(
printf( "Contention with a writer detected while rlocking lock [%p] in thread #%d:\n"
"Contention with a writer detected while rlocking lock [%p] in thread #%d:\n" " - lock was already wlocked by thread %d\n"
" - lock was already wlocked by thread %d\n" "\n"
"\n" ,this, caller_id, owner_id
,this, caller_id, lock_owner );
);
}
else {
printf(
"Contention with a writer detected while rlocking lock [%p] in thread #%d:\n"
" - the writer was already gone by the time we could inspect who owned the thread\n"
"\n"
,this, caller_id
);
}
} }
while (this->write_count > 0) { while (this->write_count > 0) {
...@@ -556,8 +498,6 @@ void LogLock::wlock() { ...@@ -556,8 +498,6 @@ void LogLock::wlock() {
pid_t caller_id = syscall(SYS_gettid); pid_t caller_id = syscall(SYS_gettid);
uint32_t contenders = CyObject_FETCH_CONTENDERS_ADD_WRITER(this->contenders);
if (this->owner_id == caller_id) { if (this->owner_id == caller_id) {
if (this->write_count) { if (this->write_count) {
++this->write_count; ++this->write_count;
...@@ -567,57 +507,40 @@ void LogLock::wlock() { ...@@ -567,57 +507,40 @@ void LogLock::wlock() {
pthread_mutex_lock(&this->guard); pthread_mutex_lock(&this->guard);
if ((CyObject_HAS_WRITER_CONTENDERS(contenders) > 0) && (this->write_count > 0)) { pid_t owner_id = this->owner_id;
pid_t lock_owner = this->owner_id;
printf(
"Contention with another writer detected while wlocking lock [%p] in thread #%d:\n"
" - lock owner is thread %d\n"
"\n"
,this, caller_id, lock_owner
);
}
else if (CyObject_READERS_FROM_CONTENDERS(contenders) > 0) { if (owner_id != caller_id) {
pid_t lock_owner = this->owner_id;
if (lock_owner > 0) { if (this->readers_nb > 0) {
printf( if (owner_id == CyObject_MANY_OWNERS) {
"Contention with a reader thread detected while wlocking lock [%p] in thread #%d:\n" printf(
" - reader thread is %d\n" "Contention with several other reader threads detected while wlocking lock [%p] in thread #%d:\n"
"\n" "\n"
,this, caller_id, lock_owner ,this, caller_id
); );
}
else {
printf(
"Contention with a reader thread detected while wlocking lock [%p] in thread #%d:\n"
" - reader thread is %d\n"
"\n"
,this, caller_id, owner_id
);
}
} }
else if (lock_owner == CyObject_MANY_OWNERS) {
printf( while (this->readers_nb > 0) {
"Contention with several other reader threads detected while wlocking lock [%p] in thread #%d:\n" pthread_cond_wait(&this->wait_readers_depart, &this->guard);
"\n"
,this, caller_id
);
} }
else {
if (this->write_count > 0) {
printf( printf(
"Contention with unidentified readers detected while wlocking lock [%p] in thread #%d:\n" "Contention with another writer detected while wlocking lock [%p] in thread #%d:\n"
" - the readers were already gone by the time we could inspect lock ownership\n" " - lock owner is thread %d\n"
"\n" "\n"
,this, caller_id ,this, caller_id, owner_id
); );
} }
}
else if (contenders > 0) {
printf(
"Contention with another writer detected while wlocking lock [%p] in thread #%d:\n"
" - the writer was already gone by the time we could inspect who owned the thread\n"
"\n"
,this, caller_id
);
}
if (this->owner_id != caller_id) {
while (this->readers_nb > 0) {
pthread_cond_wait(&this->wait_readers_depart, &this->guard);
}
while (this->write_count > 0) { while (this->write_count > 0) {
pthread_cond_wait(&this->wait_writer_depart, &this->guard); pthread_cond_wait(&this->wait_writer_depart, &this->guard);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment