126 lines
2.8 KiB
C
126 lines
2.8 KiB
C
/* Copyright (C) 2010 Tony Garnock-Jones. All rights reserved. */
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <ucontext.h>
|
|
|
|
typedef unsigned char u_char;
|
|
#include <event.h>
|
|
|
|
#include "cmsg_private.h"
|
|
#include "harness.h"
|
|
|
|
#ifdef __APPLE__
|
|
/* Bollocks. Looks like OS X chokes unless STACK_SIZE is a multiple of 32k. */
|
|
#define STACK_SIZE 32768
|
|
#elif linux
|
|
#define STACK_SIZE 4096
|
|
#else
|
|
#error Define STACK_SIZE for your platform.
|
|
#endif
|
|
|
|
Process *current_process = NULL;
|
|
|
|
static ucontext_t scheduler;
|
|
static ProcessQueue runlist = { 0, NULL, NULL };
|
|
static ProcessQueue deadlist = { 0, NULL, NULL };
|
|
|
|
static void zero_queue(ProcessQueue *pq) {
|
|
pq->count = 0;
|
|
pq->head = NULL;
|
|
pq->tail = NULL;
|
|
}
|
|
|
|
static void enqueue(ProcessQueue *pq, Process *p) {
|
|
p->link = NULL;
|
|
if (pq->head == NULL) {
|
|
pq->head = p;
|
|
} else {
|
|
pq->tail->link = p;
|
|
}
|
|
pq->tail = p;
|
|
pq->count++;
|
|
}
|
|
|
|
static Process *dequeue(ProcessQueue *pq) {
|
|
if (pq->head == NULL) {
|
|
return NULL;
|
|
} else {
|
|
Process *p = pq->head;
|
|
pq->head = p->link;
|
|
if (pq->head == NULL) {
|
|
pq->tail = NULL;
|
|
}
|
|
pq->count--;
|
|
return p;
|
|
}
|
|
}
|
|
|
|
void yield(void) {
|
|
if (current_process == NULL) {
|
|
ICHECK(setcontext(&scheduler), "yield setcontext");
|
|
} else {
|
|
enqueue(&runlist, current_process);
|
|
ICHECK(swapcontext(¤t_process->context, &scheduler), "yield swapcontext");
|
|
}
|
|
}
|
|
|
|
void killproc(void) {
|
|
enqueue(&deadlist, current_process);
|
|
current_process = NULL;
|
|
yield();
|
|
}
|
|
|
|
static void driver(void (*f)(void *), void *arg) {
|
|
f(arg);
|
|
killproc();
|
|
}
|
|
|
|
void spawn(void (*f)(void *), void *arg) {
|
|
Process *p = calloc(1, sizeof(*p));
|
|
PCHECK(p, "spawn calloc");
|
|
|
|
p->stack_base = malloc(STACK_SIZE); /* what is a sane value here? 32k for mac... */
|
|
PCHECK(p->stack_base, "stack pointer malloc");
|
|
|
|
ICHECK(getcontext(&p->context), "spawn getcontext");
|
|
p->context.uc_link = NULL;
|
|
p->context.uc_stack.ss_sp = p->stack_base;
|
|
p->context.uc_stack.ss_size = STACK_SIZE;
|
|
p->context.uc_stack.ss_flags = 0;
|
|
makecontext(&p->context, (void (*)(void)) driver, 2, f, arg);
|
|
|
|
enqueue(&runlist, p);
|
|
}
|
|
|
|
static void clean_dead_processes(void) {
|
|
Process *deadp;
|
|
while ((deadp = dequeue(&deadlist)) != NULL) {
|
|
free(deadp->stack_base);
|
|
free(deadp);
|
|
}
|
|
}
|
|
|
|
void boot_harness(void) {
|
|
ICHECK(getcontext(&scheduler), "boot_harness getcontext");
|
|
while (1) {
|
|
while (runlist.count) {
|
|
ProcessQueue work = runlist;
|
|
zero_queue(&runlist);
|
|
info("Processing %d jobs\n", work.count);
|
|
while ((current_process = dequeue(&work)) != NULL) {
|
|
ICHECK(swapcontext(&scheduler, ¤t_process->context), "boot_harness swapcontext");
|
|
clean_dead_processes();
|
|
}
|
|
info("Polling for events\n");
|
|
event_loop(EVLOOP_NONBLOCK);
|
|
}
|
|
info("Blocking for events\n");
|
|
event_loop(EVLOOP_ONCE);
|
|
}
|
|
}
|