vx32

Local 9vx git repository for patches.
git clone git://r-36.net/vx32
Log | Files | Refs

commit 9c2411e859f0163421193d3fd739a82d07577769
parent 1fb4c49fa2db6a2ac6c8ee22673c38554af552a0
Author: Russ Cox <rsc@swtch.com>
Date:   Sun, 29 Jun 2008 10:59:30 -0400

9vx: explain pipes and pthread_cond_t in sched.c

Diffstat:
src/9vx/a/dat.h | 2++
src/9vx/sched.c | 214++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
2 files changed, 133 insertions(+), 83 deletions(-)

diff --git a/src/9vx/a/dat.h b/src/9vx/a/dat.h @@ -356,6 +356,8 @@ typedef struct Pwaiter Pwaiter; struct Psleep { pthread_mutex_t mutex; + pthread_cond_t cond; + int condinit; Pwaiter *waiter; int fd[2]; vlong nread; diff --git a/src/9vx/sched.c b/src/9vx/sched.c @@ -11,12 +11,6 @@ #define WANT_M -#ifdef __APPLE__ -#define PIPES 1 -#else -#define PIPES 0 -#endif - #include "u.h" #include <pthread.h> #include <sys/poll.h> @@ -28,83 +22,6 @@ #include "error.h" #include "trace.h" -struct Pwaiter -{ - pthread_cond_t cond; - Pwaiter *next; - int awake; -}; - -void -plock(Psleep *p) -{ - pthread_mutex_lock(&p->mutex); -#if PIPES - if(p->fd[1] == 0){ - pipe(p->fd); - fcntl(p->fd[0], F_SETFL, fcntl(p->fd[0], F_GETFL)|O_NONBLOCK); - } -#endif -} - -void -punlock(Psleep *p) -{ - pthread_mutex_unlock(&p->mutex); -} - -void -psleep(Psleep *p) -{ -#if PIPES - p->nread++; - punlock(p); - char c; - while(read(p->fd[0], &c, 1) < 1){ - struct pollfd pfd; - pfd.fd = p->fd[0]; - pfd.events = POLLIN; - pfd.revents = 0; - poll(&pfd, 1, 1000); - } - plock(p); -#else - Pwaiter w; - memset(&w, 0, sizeof w); - pthread_cond_init(&w.cond, nil); - w.next = p->waiter; - p->waiter = &w; - while(!w.awake) - pthread_cond_wait(&w.cond, &p->mutex); - pthread_cond_destroy(&w.cond); -#endif -} - -void -pwakeup(Psleep *p) -{ -#if PIPES - char c = 0; - int nbad = 0; - if(p->nwrite < p->nread){ - p->nwrite++; - while(write(p->fd[1], &c, 1) < 1){ - if(++nbad%100 == 0) - iprint("pwakeup: write keeps failing\n"); - } - } -#else - Pwaiter *w; - - w = p->waiter; - if(w){ - p->waiter = w->next; - w->awake = 1; - pthread_cond_signal(&w->cond); - } -#endif -} - /* * The cpu0 scheduler calls idlehands when there is * nothing left on the main runqueue (runproc @@ -220,3 +137,134 @@ runproc(void) punlock(&run); return p; } + +/* + * Host OS process sleep and wakeup. + * This is complicated. + * + * Ideally, we'd just use a single pthread_cond_t, have everyone + * pthread_cond_wait on it, and use pthread_cond_signal + * to wake people up. Unfortunately, that fails miserably + * on OS X: sometimes the wakeups just plain get missed. + * Perhaps it has something to do with all the signals that + * are flying around. + * + * To work around the OS X pthreads problems, there is a + * second implementation turned on by #defining PIPES to 1. + * This implementation uses a pipe and reads and writes bytes + * from the pipe to implement sleep and wakeup. Perhaps not + * surprisingly, the naive implementation of this hangs: + * reads miss writes. Instead, the actual implementation uses + * select to poll whether the read would succeed, and once a + * second it tries the read even if select doesn't think it will. + * This timeout lets us make progress when an event gets missed + * (happens only rarely). This is enough to get things going on + * OS X. + * + * On my Athlon 64 running Linux, + * time to run mk -a in /sys/src/9/pc: + * + * 90s default implementation (one pthread_cond_t) + * 85s WAITERS (pthread_cond_t for each waiter) + * 88s PIPES + * + * I implemented per-thread pthread_cond_t's to see if they + * were any faster on non-OS X systems, but I can't see any + * difference. Running the WAITERS version on OS X causes + * mysterious crashes. I'm thoroughly confused. + */ +#define PIPES 0 +#define WAITERS 1 + +#ifdef __APPLE__ +#undef PIPES +#define PIPES 1 +#undef WAITERS +#define WAITERS 0 +#endif + +struct Pwaiter +{ + pthread_cond_t cond; + Pwaiter *next; + int awake; +}; + +void +plock(Psleep *p) +{ + pthread_mutex_lock(&p->mutex); + if(!p->condinit){ + p->condinit = 1; + pthread_cond_init(&p->cond, nil); + } +#if PIPES + if(p->fd[1] == 0){ + pipe(p->fd); + fcntl(p->fd[0], F_SETFL, fcntl(p->fd[0], F_GETFL)|O_NONBLOCK); + } +#endif +} + +void +punlock(Psleep *p) +{ + pthread_mutex_unlock(&p->mutex); +} + +void +psleep(Psleep *p) +{ +#if PIPES + p->nread++; + punlock(p); + char c; + while(read(p->fd[0], &c, 1) < 1){ + struct pollfd pfd; + pfd.fd = p->fd[0]; + pfd.events = POLLIN; + pfd.revents = 0; + poll(&pfd, 1, 1000); + } + plock(p); +#elif WAITERS + Pwaiter w; + memset(&w, 0, sizeof w); + pthread_cond_init(&w.cond, nil); + w.next = p->waiter; + p->waiter = &w; + while(!w.awake) + pthread_cond_wait(&w.cond, &p->mutex); + pthread_cond_destroy(&w.cond); +#else + pthread_cond_wait(&p->cond, &p->mutex); +#endif +} + +void +pwakeup(Psleep *p) +{ +#if PIPES + char c = 0; + int nbad = 0; + if(p->nwrite < p->nread){ + p->nwrite++; + while(write(p->fd[1], &c, 1) < 1){ + if(++nbad%100 == 0) + iprint("pwakeup: write keeps failing\n"); + } + } +#elif WAITERS + Pwaiter *w; + + w = p->waiter; + if(w){ + p->waiter = w->next; + w->awake = 1; + pthread_cond_signal(&w->cond); + } +#else + pthread_cond_signal(&p->cond); +#endif +} +