/* * cow.sched * * A specialized scheduler for CoW (the COoperative-multithreading Webserver) * This scheduler is for the PC version of CoW (which runs under normal * Unix-like operating systems). See cmthreadcow.sched for a scheduler for * the embedded version of CoW that runs on embedded Rabbit processors. */ scheduler cow { thread { int state; int ismain; int bytesleft_internal; /* copied from thread import */ } threadimports { int isresponding default 0; /* handler in response state? */ int bytesleft default 0; /* response bytes remaining */ } data { threadref current; /* current thread */ threadref next; /* next thread (named yield) */ queue RQ; /* ready threads */ pqueue RQ2 reverse sortable on bytesleft_internal; /* response queue */ queue WQ; /* waiting for event */ queue SQ; /* suspended */ int created_main = 0; /* Have we spawned main yet? */ const int NEW = 1; const int READY = 2; const int WAITING = 3; const int DEAD = 4; const int SUSPENDED = 5; } imports { int noof default 0; /* Number of open files */ int idlehands default 1; /* Number of idle handlers */ } event init { created_main = 0; {* fprintf(stderr, "CoW scheduler initialized\n"); *} } event newthread(t) { if (created_main == 0) { t.ismain = 1; created_main = 1; } else { t.ismain = 0; } /* * Unlike the PTH scheduler, we don't keep a separate NQ; we just * stick threads on the RQ immediately. */ t.state = NEW; t => RQ; } event schedule { /* Find next thread to run */ if (|next| == 1) next => current; else if (|RQ2| > 0) RQ2 => current; else { threadref tmp; RQ => tmp; if (tmp.ismain == 1 && (noof >= 1000 || idlehands == 0)) { /* * This is the main thread, but we have too many files open * to accept any new connections. Just move it back to the * end of the RQ and schedule the next one instead. */ tmp => RQ; RQ => tmp; } tmp => current; } dispatch current; } event switch_out { int resp; if (1 == (resp = current.isresponding)) /* * Copy thread import into an internal variable that RQ2 can be * sorted on */ current.bytesleft_internal = current.bytesleft; /* Did the last thread end? */ if (current.state == DEAD) destroy current; else if (current.state == WAITING) current => WQ; else if (current.state == SUSPENDED) current => SQ; else if (resp == 1) current => RQ2; else current => RQ; } event event_raised(t) { t.state = READY; if (t.isresponding == 1) t => RQ2; else t => RQ; } event set_next_thread(t) { t => next; } query threads_new { return 0; } query threads_ready { return |RQ| + |RQ2|; } query threads_waiting { return |WQ|; } query threads_suspended { return |SQ|; } query threads_total { return |RQ| + |RQ2| + |WQ| + |SQ| + 1; } event set_waiting(fixed tid) { tid.state = WAITING; } event set_dead(fixed tid) { tid.state = DEAD; } query is_new(tid) { return tid.state == NEW; } query is_ready(tid) { return tid.state == READY; } query is_waiting(tid) { return tid.state == WAITING; } query first_waiting { return *WQ; } query can_switch_to(tid) { return (tid.state == NEW || tid.state == READY); } event suspend_thread(tid) { tid.state = SUSPENDED; /* * Was this the current thread (i.e., did this empty the 'current' * threadref)? If so, put it back and let the scheduler handle it; * otherwise, move it to the SQ. */ if (|current| == 0) tid => current; else tid => SQ; } event resume_thread(tid) { tid.state = READY; if (tid.isresponding == 1) tid => RQ2; else tid => RQ; } } // vim: filetype=c