9vx: explain pipes and pthread_cond_t in sched.c - vx32 - Local 9vx git repository for patches.
       
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
 (DIR) commit 9c2411e859f0163421193d3fd739a82d07577769
 (DIR) parent 1fb4c49fa2db6a2ac6c8ee22673c38554af552a0
 (HTM) 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(-)
       ---
 (DIR) 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;
 (DIR) 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
       +}
       +