diff options
Diffstat (limited to 'src/lib9/rendez-pthread.c')
-rw-r--r-- | src/lib9/rendez-pthread.c | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/src/lib9/rendez-pthread.c b/src/lib9/rendez-pthread.c new file mode 100644 index 00000000..9b5d4342 --- /dev/null +++ b/src/lib9/rendez-pthread.c @@ -0,0 +1,168 @@ +/* + NAME + rendezvous - user level process synchronization + + SYNOPSIS + ulong rendezvous(ulong tag, ulong value) + + DESCRIPTION + The rendezvous system call allows two processes to synchro- + nize and exchange a value. In conjunction with the shared + memory system calls (see segattach(2) and fork(2)), it + enables parallel programs to control their scheduling. + + Two processes wishing to synchronize call rendezvous with a + common tag, typically an address in memory they share. One + process will arrive at the rendezvous first; it suspends + execution until a second arrives. When a second process + meets the rendezvous the value arguments are exchanged + between the processes and returned as the result of the + respective rendezvous system calls. Both processes are + awakened when the rendezvous succeeds. + + The set of tag values which two processes may use to + rendezvous-their tag space-is inherited when a process + forks, unless RFREND is set in the argument to rfork; see + fork(2). + + If a rendezvous is interrupted the return value is ~0, so + that value should not be used in normal communication. + + * This assumes we're using pthreads and simulates rendezvous using + * shared memory and mutexes. + */ + +#include <pthread.h> +#include <lib9.h> + +enum +{ + VOUSHASH = 257, +}; + +typedef struct Vous Vous; +struct Vous +{ + Vous *link; + Lock lk; + ulong val; + ulong tag; + pthread_mutex_t mutex; +}; + +static void +ign(int x) +{ + USED(x); +} + +void /*__attribute__((constructor))*/ +ignusr1(void) +{ + signal(SIGUSR1, ign); +} + +static Vous vouspool[2048]; +static int nvousused; +static Vous *vousfree; +static Vous *voushash[VOUSHASH]; +static Lock vouslock; + +static Vous* +getvous(void) +{ + Vous *v; + + if(vousfree){ + v = vousfree; + vousfree = v->link; + }else if(nvousused < nelem(vouspool)){ + v = &vouspool[nvousused++]; + pthread_mutex_init(&v->mutex, NULL); + }else + abort(); + return v; +} + +static void +putvous(Vous *v) +{ + lock(&vouslock); + v->link = vousfree; + vousfree = v; + unlock(&vouslock); +} + +static Vous* +findvous(ulong tag, ulong val, int *found) +{ + int h; + Vous *v, **l; + + lock(&vouslock); + h = tag%VOUSHASH; + for(l=&voushash[h], v=*l; v; l=&(*l)->link, v=*l){ + if(v->tag == tag){ + *l = v->link; + *found = 1; + unlock(&vouslock); + return v; + } + } + v = getvous(); + v->link = voushash[h]; + v->val = val; + v->tag = tag; + lock(&v->lk); + voushash[h] = v; + unlock(&vouslock); + *found = 0; + return v; +} + +#define DBG 0 +ulong +rendezvous(ulong tag, ulong val) +{ + int found; + ulong rval; + Vous *v; + + v = findvous(tag, val, &found); + if(!found){ + if(DBG)fprint(2, "tag %lux, sleeping on %p\n", tag, v); + /* + * No rendezvous partner was found; the next guy + * through will find v and wake us, so we must go + * to sleep. Do this by locking the mutex (it is + * unlocked) and then locking it again (our waker will + * unlock it for us). + */ + if(pthread_mutex_lock(&v->mutex) != 0) + abort(); + unlock(&v->lk); + if(pthread_mutex_lock(&v->mutex) != 0) + abort(); + rval = v->val; + pthread_mutex_unlock(&v->mutex); + if(DBG)fprint(2, " awake on %p\n", v); + unlock(&v->lk); + putvous(v); + }else{ + /* + * Found someone to meet. Wake him: + * + * A. lock v->lk (waits for him to lock the mutex once. + * B. unlock the mutex (wakes him up) + */ + if(DBG)fprint(2, "found tag %lux on %p, waking\n", tag, v); + lock(&v->lk); + rval = v->val; + v->val = val; + if(pthread_mutex_unlock(&v->mutex) != 0) + abort(); + /* lock passes to him */ + } + return rval; +} + |