Avoid race condition between nap()'s timeout expiring and incoming work calling resume().

This commit is contained in:
Tony Garnock-Jones 2011-03-27 13:23:05 -04:00
parent e7fb92e33a
commit d9d4b02002
3 changed files with 30 additions and 6 deletions

View File

@ -84,9 +84,13 @@ void suspend(void) {
schedule();
}
void resume(Process *p) {
assert(p->state == PROCESS_WAITING);
enqueue_runlist(p);
int resume(Process *p) {
if (p->state == PROCESS_WAITING) {
enqueue_runlist(p);
return 0;
} else {
return -1;
}
}
static void driver(void (*f)(void *), void *arg) {

View File

@ -40,7 +40,7 @@ extern Process *spawn(process_main_t f, void *arg);
extern int nap(long millis); /* 1 for timeout expired; 0 for resumed early */
extern void suspend(void);
extern void resume(Process *p);
extern int resume(Process *p);
extern IOHandle *new_iohandle(int fd);
extern void delete_iohandle(IOHandle *h);

View File

@ -158,12 +158,32 @@ static void shoveller(void *qv) {
}
static void throck_shovel(queue_extension_t *q) {
//int counter = 0;
retry:
//printf("throck %d %d %p\n", counter++, q->shovel_awake, q->shovel);
if (!q->shovel_awake) {
q->shovel_awake = 1;
if (!q->shovel) {
q->shovel_awake = 1;
q->shovel = spawn(shoveller, q);
} else {
resume(q->shovel);
if (resume(q->shovel) == -1) {
/* The nap() in the shoveller returned and scheduled the
shoveller *just* before we got to it, but the shoveller
hasn't had a chance to run yet, so hasn't been able to
clear q->shovel and exit. The resume() attempt failed
because q->shovel's state is PROCESS_RUNNING, now that it
has been scheduled by the return of nap(), so we know that
we should back off and try again from the top. */
yield();
goto retry;
} else {
/* The resume() was successful, i.e. the nap() hadn't returned
before we tried to resume(). We know that nap() will return
zero (since the timeout didn't fire before the process was
resumed), and so the existing shoveller will continue
running. */
q->shovel_awake = 1;
}
}
}
}