struct pipe {
char buf[N];
unsigned r, w;
lock_t l;
}
void writec(struct pipe* p, char c) {
bool ok;
while (!ok) {
lock(&p->l);
ok = p->w - p->r > N;
if (ok) {
p->buf[p->w++%N] = c;
}
unlock(&p->l);
}
}
void readc(struct pipe* p, char c) {
lock(&p->l);
while (p->w - p->r > 0) {
return p->buf[p->w++%N];
}
unlock(&p->l);
}
Assumptions:
Lock, unlock are memory barriers—however would slow down faster machine. Do we need to use spin locks? (xchal
, test_and_set
)
lock incl x
: atomic x incrementcompare_and_swap
instruction: cmpxchgl
bool cas(int* addr, int old, int new) {
if (*addr == old) {
*addr = new;
return true;
}
return false;
}
void add1(int* p) {
while (true) {
int old = *p;
if (c_a_s(p, old, f(*p))
return;
}
}
Can we just use loads + stores?
Both are atomic if storing full words.
(Known as read/write coherence)
Aside: what if we’re in a uniprocessor—does that not work?
disable_interrupts()
do...while
enable_interrupts()
Read/write locks
void lock_for_read(struct wlock* p) {
while (true) {
lock(&p->l);
p->readers++;
unlock(&p->l);
}
}
struct mutex {
lock_t l;
bool locked;
proc_t *blocked_list;
}
void acquire(struct mutex* p) {
while (true) {
lock(&p->0);
if (!p->locked) {
p->locked = true;
unlock(&p->l);
return;
}
current_process->runnable = false;
cp->next = p->blocked_list;
p->blocked_list = cp;
unlock(&p->l);
yield();
}
}
void release(struct mutex* p) {
lock(&p->l);
p->locked = false;
while (p->blocked_list) {
p->blocked_list->runnable = true;
p->blocked_list = p->blocked_list->next;
}
unlock(&p->l);
}