static void semsleep(Sem *s, int dontblock) { Proc *up = externup(); DBG("semsleep up %#p sem %#p\n", up, s->np); if(dontblock){ /* * User tried to down non-blocking, but someone else * got the ticket between looking at n and adec(n). * we have to safely undo our temporary down here. * Adjust the value of the semaphore to reflect that we * wanted a ticket for a while but no longer want one. * Make sure that no other process is waiting because we * made a temporary down. */ semainc(s->np); semwakeup(s, 1, 1); return; } lock(&s->l); if(*s->np >= 0){ /* * A ticket came, either it came while calling the kernel, * or it was a temporary sleep that didn't block. * Either way, we are done. */ unlock(&s->l); goto Done; } /* * Commited to wait, we'll have to wait until * some other process changes our state. */ s->q = realloc(s->q, (s->nq+1) * sizeof s->q[0]); if(s->q == nil) panic("semsleep: no memory"); s->q[s->nq++] = up; up->waitsem = nil; up->state = Semdown; unlock(&s->l); DBG("semsleep up %#p blocked\n", up); sched(); Done: DBG("semsleep up %#p awaken\n", up); if(up->waitsem == nil){ /* * nobody did awake us, we are probably being * killed; we no longer want a ticket. */ lock(&s->l); semainc(s->np); /* we are no longer waiting; killed */ semwakeup(s, 1, 0); unlock(&s->l); } }
/* Acquire semaphore (subtract 1). */ static int semacquire(Segment *s, long *addr, int block) { int acquired; Sema phore; if(canacquire(addr)) return 1; if(!block) return 0; acquired = 0; semqueue(s, addr, &phore); for(;;){ phore.waiting = 1; coherence(); if(canacquire(addr)){ acquired = 1; break; } if(waserror()) break; sleep(&phore, semawoke, &phore); poperror(); } semdequeue(s, &phore); coherence(); /* not strictly necessary due to lock in semdequeue */ if(!phore.waiting) semwakeup(s, addr, 1); if(!acquired) nexterror(); return 1; }
static void semdequeue(Sem *s) { Proc *up = externup(); int i; assert(s != nil); lock(&s->l); for(i = 0; i < s->nq; i++) if(s->q[i] == up) break; if(i == s->nq){ /* * We didn't perform a down on s, yet we are no longer queued * on it; it must be because someone gave us its * ticket in the mean while. We must put it back. */ semainc(s->np); semwakeup(s, 0, 0); }else{ s->nq--; s->q[i] = s->q[s->nq]; } unlock(&s->l); }
void upsem(Sem *s) { int n; n = semainc(s->np); if(n <= 0) semwakeup(s, 1, 1); }
/* Add delta to semaphore and wake up waiters as appropriate. */ static long semrelease(Segment *s, long *addr, long delta) { long value; do value = *addr; while(!cmpswap(addr, value, value+delta)); semwakeup(s, addr, delta); return value+delta; }
/* Acquire semaphore or timeout */ static int tsemacquire(Segment *s, long *addr, ulong ms) { int acquired; Sema phore; int timedout; ulong t; ulong tsemdbg; if(canacquire(addr)) return 1; if(ms == 0) return 0; acquired = 0; timedout = 0; semqueue(s, addr, &phore); for(;;){ phore.waiting = 1; coherence(); if(canacquire(addr)){ acquired = 1; break; } if(waserror()) break; t = m->ticks; tsleep(&phore, semawoke, &phore, ms); if((tsemdbg = TK2MS(m->ticks - t)) >= ms){ timedout = 1; poperror(); break; } ms -= TK2MS(m->ticks - t); poperror(); } semdequeue(s, &phore); coherence(); /* not strictly necessary due to lock in semdequeue */ if(!phore.waiting) semwakeup(s, addr, 1); if(timedout) return 0; if(!acquired) nexterror(); return 1; }
void syssemwakeup(Ar0* ar0, ...) { Proc *up = externup(); int *np; Sem *s; Segment *sg; va_list list; va_start(list, ar0); /* * void semwakeup(int*); */ np = va_arg(list, int*); np = validaddr(np, sizeof *np, 1); evenaddr(PTR2UINT(np)); if((sg = seg(up, PTR2UINT(np), 0)) == nil) error(Ebadarg); s = segmksem(sg, np); semwakeup(s, 1, 1); va_end(list); }