// // producer/consumer example // Version One: spinning // struct pcq { void *ptr; }; void* pcqread(struct pcq *q) { void *p; // wait for data while(q->ptr == 0) ; p = q->ptr; // mark as consumed q->ptr = 0; return p; } void pcqwrite(struct pcq *q, void *p) { // wait for consumption of previous data while(q->ptr != 0) ; q->ptr = p; } // // Version Two: first attempt to use sleep()/wakeup() // void* pcqread(struct pcq *q) { void *p; if(q->ptr == 0) sleep(q); p = q->ptr; q->ptr = 0; wakeup(q); // wake pcqwrite return p; } void pcqwrite(struct pcq *q, void *p) { if(q->ptr != 0) sleep(q); q->ptr = p; wakeup(q); // wake pcqread return p; } // // Version Three: sleep()/wakeup() with locks // struct pcq { void *ptr; struct spinlock lock; }; void* pcqread(struct pcq *q) { void *p; acquire(&q->lock); if(q->ptr == 0) sleep(q, &q->lock); p = q->ptr; q->ptr = 0; wakeup(q); release(&q->lock); return p; } void pcqwrite(struct pcq *q, void *p) { acquire(&q->lock); if(q->ptr != 0) sleep(q, &q->lock); q->ptr = p; wakeup(q); release(&q->lock); return p; } // // Version Four: loop around the sleep() // void* pcqread(struct pcq *q) { void *p; acquire(&q->lock); while(q->ptr == 0) sleep(q, &q->lock); p = q->ptr; q->ptr = 0; wakeup(q); release(&q->lock); return p; } void pcqwrite(struct pcq *q, void *p) { acquire(&q->lock); while(q->ptr != 0) sleep(q, &q->lock); q->ptr = p; wakeup(q); release(&q->lock); return p; } // // A first attempt at implementing sleep()/wakeup() // void sleep(void *chan, struct spinlock *lk) { struct proc *p = curproc[cpu()]; release(lk); p->chan = chan; p->state = SLEEPING; sched(); acquire(lk); } void wakeup(void *chan) { for(each proc p) { if(p->state == SLEEPING && p->chan == chan) p->state = RUNNABLE; } }