Make empth_rwlock_t prefer writers
LWP and Windows implementations already did that. Rewrite the pthreads implementation. The write-bias makes the stupid play_wrlock_wanted busy wait in dispatch() unnecessary. Remove it.
This commit is contained in:
parent
079fb286c5
commit
d052239a7a
3 changed files with 47 additions and 28 deletions
|
@ -56,10 +56,10 @@
|
||||||
|
|
||||||
/* Abstract data types */
|
/* Abstract data types */
|
||||||
|
|
||||||
/* empth_t * represents a thread. */
|
/* A thread. */
|
||||||
typedef struct lwpProc empth_t;
|
typedef struct lwpProc empth_t;
|
||||||
|
|
||||||
/* empth_rwlock_t * represents a read-write lock */
|
/* A read-write lock, perferring writers */
|
||||||
typedef struct lwp_rwlock empth_rwlock_t;
|
typedef struct lwp_rwlock empth_rwlock_t;
|
||||||
|
|
||||||
/* Flags for empth_select(): whether to sleep on input or output */
|
/* Flags for empth_select(): whether to sleep on input or output */
|
||||||
|
@ -189,7 +189,7 @@ int empth_wait_for_signal(void);
|
||||||
/*
|
/*
|
||||||
* Create a read-write lock.
|
* Create a read-write lock.
|
||||||
* NAME is its name, it is used for debugging.
|
* NAME is its name, it is used for debugging.
|
||||||
* Return the reade-write lock, or NULL on error.
|
* Return the read-write lock, or NULL on error.
|
||||||
*/
|
*/
|
||||||
empth_rwlock_t *empth_rwlock_create(char *name);
|
empth_rwlock_t *empth_rwlock_create(char *name);
|
||||||
|
|
||||||
|
@ -209,9 +209,9 @@ void empth_rwlock_wrlock(empth_rwlock_t *rwlock);
|
||||||
/*
|
/*
|
||||||
* Lock RWLOCK for reading.
|
* Lock RWLOCK for reading.
|
||||||
* A read-write lock can be locked for reading only when it is not
|
* A read-write lock can be locked for reading only when it is not
|
||||||
* locked for writing. If this is not the case, put the current
|
* locked for writing, and no other thread is attempting to lock it
|
||||||
* thread to sleep until it is. Must not starve writers, and may
|
* for writing. If this is not the case, put the current thread to
|
||||||
* sleep to avoid that.
|
* sleep until it is.
|
||||||
*/
|
*/
|
||||||
void empth_rwlock_rdlock(empth_rwlock_t *rwlock);
|
void empth_rwlock_rdlock(empth_rwlock_t *rwlock);
|
||||||
|
|
||||||
|
|
|
@ -62,8 +62,12 @@ struct empth_t {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct empth_rwlock_t {
|
struct empth_rwlock_t {
|
||||||
|
/* Can't use pthread_rwlock_t, because it needn't prefer writers */
|
||||||
char *name;
|
char *name;
|
||||||
pthread_rwlock_t lock;
|
int nread; /* #active readers */
|
||||||
|
int nwrite; /* total #writers (active and waiting) */
|
||||||
|
pthread_cond_t can_read;
|
||||||
|
pthread_cond_t can_write;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Thread-specific data key */
|
/* Thread-specific data key */
|
||||||
|
@ -397,19 +401,22 @@ empth_rwlock_create(char *name)
|
||||||
if (!rwlock)
|
if (!rwlock)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (pthread_rwlock_init(&rwlock->lock, NULL) != 0) {
|
if (pthread_cond_init(&rwlock->can_read, NULL) != 0
|
||||||
|
|| pthread_cond_init(&rwlock->can_write, NULL) != 0) {
|
||||||
free(rwlock);
|
free(rwlock);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
rwlock->name = strdup(name);
|
rwlock->name = strdup(name);
|
||||||
|
rwlock->nread = rwlock->nwrite = 0;
|
||||||
return rwlock;
|
return rwlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
empth_rwlock_destroy(empth_rwlock_t *rwlock)
|
empth_rwlock_destroy(empth_rwlock_t *rwlock)
|
||||||
{
|
{
|
||||||
pthread_rwlock_destroy(&rwlock->lock);
|
pthread_cond_destroy(&rwlock->can_read);
|
||||||
|
pthread_cond_destroy(&rwlock->can_write);
|
||||||
free(rwlock->name);
|
free(rwlock->name);
|
||||||
free(rwlock);
|
free(rwlock);
|
||||||
}
|
}
|
||||||
|
@ -417,23 +424,45 @@ empth_rwlock_destroy(empth_rwlock_t *rwlock)
|
||||||
void
|
void
|
||||||
empth_rwlock_wrlock(empth_rwlock_t *rwlock)
|
empth_rwlock_wrlock(empth_rwlock_t *rwlock)
|
||||||
{
|
{
|
||||||
pthread_mutex_unlock(&mtx_ctxsw);
|
empth_status("wrlock %s %d %d",
|
||||||
pthread_rwlock_wrlock(&rwlock->lock);
|
rwlock->name, rwlock->nread, rwlock->nwrite);
|
||||||
pthread_mutex_lock(&mtx_ctxsw);
|
rwlock->nwrite++;
|
||||||
|
while (rwlock->nread != 0 || rwlock->nwrite != 1) {
|
||||||
|
empth_status("waiting for wrlock %s", rwlock->name);
|
||||||
|
pthread_cond_wait(&rwlock->can_write, &mtx_ctxsw);
|
||||||
|
empth_status("got wrlock %s %d %d",
|
||||||
|
rwlock->name, rwlock->nread, rwlock->nwrite);
|
||||||
empth_restorectx();
|
empth_restorectx();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
empth_rwlock_rdlock(empth_rwlock_t *rwlock)
|
empth_rwlock_rdlock(empth_rwlock_t *rwlock)
|
||||||
{
|
{
|
||||||
pthread_mutex_unlock(&mtx_ctxsw);
|
empth_status("rdlock %s %d %d",
|
||||||
pthread_rwlock_rdlock(&rwlock->lock);
|
rwlock->name, rwlock->nread, rwlock->nwrite);
|
||||||
pthread_mutex_lock(&mtx_ctxsw);
|
while (rwlock->nwrite) {
|
||||||
|
empth_status("waiting for rdlock %s", rwlock->name);
|
||||||
|
pthread_cond_wait(&rwlock->can_read, &mtx_ctxsw);
|
||||||
|
empth_status("got rdlock %s %d %d",
|
||||||
|
rwlock->name, rwlock->nread, rwlock->nwrite);
|
||||||
empth_restorectx();
|
empth_restorectx();
|
||||||
}
|
}
|
||||||
|
rwlock->nread++;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
empth_rwlock_unlock(empth_rwlock_t *rwlock)
|
empth_rwlock_unlock(empth_rwlock_t *rwlock)
|
||||||
{
|
{
|
||||||
pthread_rwlock_unlock(&rwlock->lock);
|
if (CANT_HAPPEN(!rwlock->nread && !rwlock->nwrite))
|
||||||
|
return;
|
||||||
|
if (rwlock->nread) { /* holding read lock */
|
||||||
|
if (!--rwlock->nread)
|
||||||
|
pthread_cond_signal(&rwlock->can_write);
|
||||||
|
} else {
|
||||||
|
rwlock->nwrite--;
|
||||||
|
pthread_cond_signal(&rwlock->can_write);
|
||||||
|
}
|
||||||
|
if (rwlock->nwrite == 0)
|
||||||
|
pthread_cond_broadcast(&rwlock->can_read);
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,16 +85,6 @@ dispatch(char *buf, char *redir)
|
||||||
pr("Command not implemented\n");
|
pr("Command not implemented\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
* Rwlocks can hand out read locks while a write lock is wanted.
|
|
||||||
* Unfair to writers, but possible. This lets commands run with
|
|
||||||
* !player->aborted, which can then block on input and thus delay
|
|
||||||
* the update indefinitely. We could avoid that by setting
|
|
||||||
* player->aborted here, but spinning until the update is done is
|
|
||||||
* nicer to players.
|
|
||||||
*/
|
|
||||||
while (play_wrlock_wanted)
|
|
||||||
empth_yield();
|
|
||||||
player->command = command;
|
player->command = command;
|
||||||
empth_rwlock_rdlock(play_lock);
|
empth_rwlock_rdlock(play_lock);
|
||||||
if (redir) {
|
if (redir) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue