/* 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; }
/* 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; }
/* * Alternative down of a Sem in ss[]. * The logic is similar to multiple downs, see comments in semsleep(). */ static int semalt(Sem *ss[], int n) { Proc *up = externup(); int i, j, r; Sem *s; DBG("semalt up %#p ss[0] %#p\n", up, ss[0]->np); r = -1; for(i = 0; i < n; i++){ s = ss[i]; n = semadec(s->np); if(n >= 0){ r = i; goto Done; } lock(&s->l); s->q = realloc(s->q, (s->nq+1) * sizeof s->q[0]); if(s->q == nil) panic("semalt: not enough memory"); s->q[s->nq++] = up; unlock(&s->l); } DBG("semalt up %#p blocked\n", up); up->state = Semdown; sched(); Done: DBG("semalt up %#p awaken\n", up); for(j = 0; j < i; j++){ assert(ss[j] != nil); if(ss[j] != up->waitsem) semdequeue(ss[j]); else r = j; } if(r < 0) panic("semalt"); return r; }