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:
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
+}
+