/* * This file is part of Jehanne. * * Copyright (C) 2015-2016 Giacomo Tesio * * Jehanne is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2 of the License. * * Jehanne is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Jehanne. If not, see . */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" typedef struct PendingWakeup PendingWakeup; struct PendingWakeup { uint64_t time; Proc *p; PendingWakeup *next; }; /* alarms: linked list, sorted by wakeup time, protected by qlock(&l) * wakeupafter inserts new items, forgivewakeup remove them, * awakekproc consume the expired ones and clearwakeups remove those * survived to their process. */ static PendingWakeup *alarms; static QLock l; static Rendez awaker; /* * Actually wakeup a process */ static void wakeupProc(Proc *p) { Mpl pl; Rendez *r; Proc *d, **l; /* this loop is to avoid lock ordering problems. */ for(;;){ pl = splhi(); lock(&p->rlock); r = p->r; /* waiting for a wakeup? */ if(r == nil) break; /* no */ /* try for the second lock */ if(canlock(&r->l)){ if(p->state != Wakeme || r->p != p) panic("wakeupProc: state %d %d %d", r->p != p, p->r != r, p->state); p->r = nil; r->p = nil; ready(p); unlock(&r->l); break; } /* give other process time to get out of critical section and try again */ unlock(&p->rlock); splx(pl); sched(); } unlock(&p->rlock); splx(pl); if(p->state != Rendezvous) return; /* Try and pull out of a rendezvous */ lock(&p->rgrp->l); if(p->state == Rendezvous) { p->rendval = ~0; l = &REND(p->rgrp, p->rendtag); for(d = *l; d; d = d->rendhash) { if(d == p) { *l = p->rendhash; break; } l = &d->rendhash; } ready(p); } unlock(&p->rgrp->l); } void awakekproc(void* v) { Proc *p; PendingWakeup *toAwake, *tail; uint64_t now; for(;;){ now = sys->ticks; toAwake = nil; /* search for processes to wakeup */ qlock(&l); tail = alarms; while(tail && tail->time <= now){ if(tail->p->pendingWakeup > tail->p->lastWakeup && tail->p->state >= Ready) break; toAwake = tail; --toAwake->p->wakeups; toAwake->p->pendingWakeup = toAwake->time; tail = tail->next; } if(toAwake != nil){ toAwake->next = nil; toAwake = alarms; alarms = tail; } qunlock(&l); /* wakeup sleeping processes */ while(toAwake != nil){ p = toAwake->p; if(p->lastWakeup < toAwake->time && p->state > Ready) { /* debugged processes will miss wakeups, * but the alternatives seem even worse */ if(canqlock(&p->debug)){ if(!waserror()){ wakeupProc(p); poperror(); } qunlock(&p->debug); } } tail = toAwake->next; jehanne_free(toAwake); toAwake = tail; } sleep(&awaker, return0, 0); } } /* * called on pexit */ void clearwakeups(Proc *p) { PendingWakeup *w, **next, *freelist, **fend; freelist = nil; /* find all PendingWakeup* to free and remove them from alarms */ qlock(&l); if(p->wakeups){ /* note: qlock(&l) && p->wakeups > 0 => alarms != nil */ next = &alarms; fend = &freelist; clearnext: w = *next; while (w != nil && w->p == p) { /* found one to remove */ *fend = w; /* append to freelist */ *next = w->next; /* remove from alarms */ fend = &w->next; /* move fend to end of freelist */ *fend = nil; /* clean the end of freelist */ --p->wakeups; w = *next; /* next to analyze */ } /* while exited => w == nil || w->p != p */ if(p->wakeups){ /* note: p->wakeups > 0 => w != nil * p->wakeups > 0 && w->p != p => w->next != nil */ next = &w->next; goto clearnext; } } qunlock(&l); /* free the found PendingWakeup* (out of the lock) */ w = freelist; while(w != nil) { freelist = w->next; jehanne_free(w); w = freelist; } } /* * called every clock tick */ void checkwakeups(void) { PendingWakeup *p; uint64_t now; p = alarms; now = sys->ticks; if(p && p->time <= now) wakeup(&awaker); } static int64_t wakeupafter(int64_t ms) { PendingWakeup *w, *new, **last; int64_t when; when = ms2tk(ms) + sys->ticks + 2; /* +2 against round errors and cpu's clocks misalignment */ new = jehanne_mallocz(sizeof(PendingWakeup), 1); if(new == nil) return 0; new->p = up; new->time = when; qlock(&l); last = &alarms; for(w = *last; w != nil && w->time <= when; w = w->next) { last = &w->next; } new->next = w; *last = new; ++up->wakeups; qunlock(&l); return -when; } static int64_t forgivewakeup(int64_t time) { PendingWakeup *w, **last; if(up->lastWakeup >= time || up->pendingWakeup >= time) return 0; qlock(&l); if(alarms == nil || up->wakeups == 0){ qunlock(&l); return 0; // nothing to do } last = &alarms; for(w = *last; w != nil && w->time < time; w = w->next) { last = &w->next; } while(w != nil && w->time == time && w->p != up){ last = &w->next; w = w->next; } if(w == nil || w->time > time || w->p != up){ /* wakeup not found */ qunlock(&l); return 0; } *last = w->next; --up->wakeups; qunlock(&l); jehanne_free(w); return -time; } int64_t procawake(int64_t ms) { if(ms == 0) return -up->lastWakeup; // nothing to do if(ms < 0) return forgivewakeup(-ms); return wakeupafter(ms); }