Skip to content

Commit 8f7497c

Browse files
pcercueiQuzarDC
authored andcommitted
rwsem: Use semaphore instead of mutex for read lock
The problem with using a mutex for the read lock, is that the thread which locks the mutex must be the one to unlock it. This does not work well with R/W semaphores, because the reader thread that will release the lock may not be the one that obtained it. This happens for instance in the TCP stack. Address this issue by using a regular semaphore as our read lock instead of a mutex. A consequence of this is that a writer that waits on the read lock now won't try to boost the thread holding it. That should be fine, because once again, the thread that first got the lock may have released it long ago, and other readers are holding the R/W semaphore. Signed-off-by: Paul Cercueil <paul@crapouillou.net>
1 parent d950bd6 commit 8f7497c

2 files changed

Lines changed: 17 additions & 15 deletions

File tree

include/kos/rwsem.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ __BEGIN_DECLS
4545

4646
#include <stddef.h>
4747
#include <kos/mutex.h>
48+
#include <kos/sem.h>
4849

4950
/** \brief Reader/writer semaphore structure.
5051
@@ -58,11 +59,11 @@ typedef struct rw_semaphore {
5859
int read_count;
5960

6061
mutex_t write_lock;
61-
mutex_t read_lock;
62+
semaphore_t read_sem;
6263
} rw_semaphore_t;
6364

6465
/** \brief Initializer for a transient reader/writer semaphore */
65-
#define RWSEM_INITIALIZER { 0, MUTEX_INITIALIZER, MUTEX_INITIALIZER }
66+
#define RWSEM_INITIALIZER { 0, MUTEX_INITIALIZER, SEM_INITIALIZER(1) }
6667

6768
/** \brief Initialize a reader/writer semaphore.
6869

kernel/thread/rwsem.c

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@ typedef enum rwsem_update_type {
2323
int rwsem_init(rw_semaphore_t *s) {
2424
s->read_count = 0;
2525
s->write_lock = (mutex_t)MUTEX_INITIALIZER;
26-
s->read_lock = (mutex_t)MUTEX_INITIALIZER;
26+
s->read_sem = (semaphore_t)SEM_INITIALIZER(1);
2727

2828
return 0;
2929
}
3030

3131
/* Destroy a reader/writer semaphore */
3232
int rwsem_destroy(rw_semaphore_t *s) {
33-
if(mutex_is_locked(&s->write_lock) || mutex_is_locked(&s->read_lock)) {
33+
if(mutex_is_locked(&s->write_lock) || sem_count(&s->read_sem) == 0) {
3434
errno = EBUSY;
3535
return -1;
3636
}
@@ -64,18 +64,19 @@ static int rwsem_update_timed(rw_semaphore_t *s, unsigned int timeout,
6464
if(type == UPDATE_TYPE_UPGRADE)
6565
rwsem_read_unlock(s);
6666

67-
if(mutex_lock_timed(&s->read_lock, timeout)) {
67+
if(sem_wait_timed(&s->read_sem, timeout)) {
6868
if(type == UPDATE_TYPE_READ) {
6969
atomic_fetch_sub(&s->read_count, 1);
7070
} else if(type == UPDATE_TYPE_UPGRADE
7171
&& atomic_fetch_add(&s->read_count, 1) == 0) {
72-
/* mutex_locked_timed() timed out, but the read count we just
72+
/* sem_wait_timed() timed out, but the read count we just
7373
* updated was zero, which means that whatever was holding up
74-
* the mutex may have unlocked it since then, or will unlock it
75-
* the next time it runs without delay. This is guaranteed
76-
* because we hold up the write mutex, so no other reader or
77-
* writer can lock up the read mutex before we do. */
78-
mutex_lock(&s->read_lock);
74+
* the semaphore may have unlocked it since then, or will
75+
* unlock it the next time it runs without delay. This is
76+
* guaranteed because we hold up the write mutex, so no other
77+
* reader or writer can lock up the read semaphore before we
78+
* do. */
79+
sem_wait(&s->read_sem);
7980
}
8081

8182
mutex_unlock(&s->write_lock);
@@ -117,14 +118,14 @@ int rwsem_write_lock_irqsafe(rw_semaphore_t *s) {
117118
/* Unlock a reader/writer semaphore from a read lock. */
118119
int rwsem_read_unlock(rw_semaphore_t *s) {
119120
if(atomic_fetch_sub(&s->read_count, 1) == 1)
120-
mutex_unlock(&s->read_lock);
121+
sem_signal(&s->read_sem);
121122

122123
return 0;
123124
}
124125

125126
/* Unlock a reader/writer semaphore from a write lock. */
126127
int rwsem_write_unlock(rw_semaphore_t *s) {
127-
mutex_unlock(&s->read_lock);
128+
sem_signal(&s->read_sem);
128129
mutex_unlock(&s->write_lock);
129130

130131
return 0;
@@ -147,7 +148,7 @@ int rwsem_read_trylock(rw_semaphore_t *s) {
147148
}
148149

149150
if(atomic_fetch_add(&s->read_count, 1) == 0
150-
&& mutex_trylock(&s->read_lock)) {
151+
&& sem_trywait(&s->read_sem)) {
151152
atomic_fetch_sub(&s->read_count, 1);
152153
mutex_unlock(&s->write_lock);
153154
errno = EWOULDBLOCK;
@@ -166,7 +167,7 @@ int rwsem_write_trylock(rw_semaphore_t *s) {
166167
return -1;
167168
}
168169

169-
if(mutex_trylock(&s->read_lock)) {
170+
if(sem_trywait(&s->read_sem)) {
170171
mutex_unlock(&s->write_lock);
171172
errno = EWOULDBLOCK;
172173
return -1;

0 commit comments

Comments
 (0)