#include <9pm/u.h> #include <9pm/libc.h> #include <9pm/ns.h> typedef struct Tag Tag; struct Tag { ulong tag; ulong val; Proc *proc; Tag *hash; Tag *free; }; enum { NHASH = 32 }; static Tag *ht[NHASH]; static Tag *ft; static Lock rendlock; ulong rendezvous(ulong tag, ulong val) { uint h; ulong ret; Tag *t, **l; Proc *p; lock(&rendlock); h = tag&(NHASH-1); for(l=&ht[h]; t=*l; l=&(*l)->hash){ if(t->tag == tag){ ret = t->val; t->val = val; p = t->proc; *l = t->hash; t->proc = nil; pm_wake(p); /* lock passes to process being wakened */ return ret; } } if((t=ft) == nil){ t = mallocz(sizeof(*t), 1); assert(t != nil); }else ft = ft->free; t->tag = tag; t->val = val; t->hash = *l; t->proc = getproc(); *l = t; unlock(&rendlock); /* * there's a potential race here: if we get woken before we nap, * the wake may be a no-op. thus nap only naps for small * amounts of time and we poll t->tag. since we're using * rendezvous as our synchronization primitive, i'm hesitant * to build still other primitives and build rendezvous with them. * losing this race is not at all likely: the time between * unlocking the lock and napping is only a handful of instructions. * * depending on the implementation of pm_wake, the race may * not be there at all: on some systems, pm_wake waits for the process * to nap before waking it. */ while(t->proc != nil) pm_nap(); /* lock inherited from waker, tag is unlinked from hash list */ ret = t->val; t->free = ft; ft = t; unlock(&rendlock); return ret; }