/* * 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" #include struct { uint32_t rlock; uint32_t rlockq; uint32_t wlock; uint32_t wlockq; uint32_t qlock; uint32_t qlockq; } rwstats; void qlock(QLock *q) { Proc *p; void (*pt)(Proc*, int, int64_t, int64_t); if(m->ilockdepth != 0) jehanne_print("qlock: %#p: ilockdepth %d", getcallerpc(), m->ilockdepth); if(up != nil && up->nlocks) jehanne_print("qlock: %#p: nlocks %d", getcallerpc(), up->nlocks); lock(&q->use); rwstats.qlock++; if(!q->locked) { q->locked = 1; q->qpc = getcallerpc(); unlock(&q->use); return; } if(up == nil) panic("qlock"); rwstats.qlockq++; p = q->tail; if(p == 0) q->head = up; else p->qnext = up; q->tail = up; up->qnext = 0; up->state = Queueing; up->qpc = getcallerpc(); if(up->trace && (pt = proctrace) != nil) pt(up, SSleep, 0, Queueing | (up->qpc<<8)); unlock(&q->use); sched(); } int canqlock(QLock *q) { if(!canlock(&q->use)) return 0; if(q->locked){ unlock(&q->use); return 0; } q->locked = 1; q->qpc = getcallerpc(); unlock(&q->use); return 1; } void qunlock(QLock *q) { Proc *p; lock(&q->use); if (q->locked == 0) jehanne_print("qunlock called with qlock not held, from %#p\n", getcallerpc()); p = q->head; if(p){ q->head = p->qnext; if(q->head == 0) q->tail = 0; unlock(&q->use); ready(p); return; } q->locked = 0; q->qpc = 0; unlock(&q->use); } void priqlock(QLock *q) { Proc *p; void (*pt)(Proc*, int, int64_t, int64_t); if(m->ilockdepth != 0) jehanne_print("priqlock: %#p: ilockdepth %d\n", getcallerpc(), m->ilockdepth); if(up != nil && up->nlocks) jehanne_print("priqlock: %#p: nlocks %d\n", getcallerpc(), up->nlocks); lock(&q->use); if(!q->locked) { //q->p = up; q->locked = 1; q->qpc = getcallerpc(); unlock(&q->use); return; } if(up == nil) panic("priqlock"); // if(q->p == up) // panic("qlock deadlock. pid=%ld cpc=%lux qpc=%lux\n", up->pid, getcallerpc(), up->qpc); p = up->qnext = q->head; if(p == nil) q->tail = up; q->head = up; up->state = Queueing; up->qpc = getcallerpc(); if(up->trace && (pt = proctrace) != nil) pt(up, SSleep, 0, Queueing | (up->qpc<<8)); // if(kproflock) // kproflock(up->qpc); unlock(&q->use); sched(); } void rlock(RWlock *q) { Proc *p; void (*pt)(Proc*, int, int64_t, int64_t); uintptr_t pc; lock(&q->use); rwstats.rlock++; if(q->writer == 0 && q->head == nil){ /* no writer, go for it */ q->readers++; unlock(&q->use); return; } rwstats.rlockq++; p = q->tail; if(up == nil) panic("rlock"); if(p == 0) q->head = up; else p->qnext = up; q->tail = up; up->qnext = 0; up->state = QueueingR; if(up->trace && (pt = proctrace) != nil){ pc = getcallerpc(); pt(up, SSleep, 0, QueueingR | (pc<<8)); } unlock(&q->use); sched(); } void runlock(RWlock *q) { Proc *p; lock(&q->use); p = q->head; if(--(q->readers) > 0 || p == nil){ unlock(&q->use); return; } /* start waiting writer */ if(p->state != QueueingW) panic("runlock"); q->head = p->qnext; if(q->head == 0) q->tail = 0; q->writer = 1; unlock(&q->use); ready(p); } void wlock(RWlock *q) { Proc *p; uintptr_t pc; void (*pt)(Proc*, int, int64_t, int64_t); lock(&q->use); rwstats.wlock++; if(q->readers == 0 && q->writer == 0){ /* noone waiting, go for it */ q->wpc = getcallerpc(); q->wproc = up; q->writer = 1; unlock(&q->use); return; } /* wait */ rwstats.wlockq++; p = q->tail; if(up == nil) panic("wlock"); if(p == nil) q->head = up; else p->qnext = up; q->tail = up; up->qnext = 0; up->state = QueueingW; if(up->trace && (pt = proctrace) != nil){ pc = getcallerpc(); pt(up, SSleep, 0, QueueingW|(pc<<8)); } unlock(&q->use); sched(); } void wunlock(RWlock *q) { Proc *p; lock(&q->use); p = q->head; if(p == nil){ q->writer = 0; unlock(&q->use); return; } if(p->state == QueueingW){ /* start waiting writer */ q->head = p->qnext; if(q->head == nil) q->tail = nil; unlock(&q->use); ready(p); return; } if(p->state != QueueingR) panic("wunlock"); /* waken waiting readers */ while(q->head != nil && q->head->state == QueueingR){ p = q->head; q->head = p->qnext; q->readers++; ready(p); } if(q->head == nil) q->tail = nil; q->writer = 0; unlock(&q->use); } /* same as rlock but punts if there are any writers waiting */ int canrlock(RWlock *q) { lock(&q->use); rwstats.rlock++; if(q->writer == 0 && q->head == nil){ /* no writer, go for it */ q->readers++; unlock(&q->use); return 1; } unlock(&q->use); return 0; }