#include "utils.h" #include "scheduler.h" #include #include #include #include /** * Schedule activation after 'delay' time units, * and passivate */ void sched_hold(struct scheduler *self, int delay) { sched_reactivate(self, self->current, delay); sched_next_event(self, false); } /** * Make current process non-active */ void sched_passivate(struct scheduler *self) { if (process_scheduled(self->current)) sched_unschedule(self, self->current); if (process_terminated(self->current)) { process_print(self->current); utils_fatal_error("Passivate of terminated process.\n"); } sched_next_event(self, false); } /** * Like activate(p, 0); */ void sched_activate_now(struct scheduler *self, struct process *p) { sched_activate(self, p, 0); } /** * if p is not running or scheduled, schedule * it after time 'delay'; otherwise, do nothing */ void sched_activate(struct scheduler *self, struct process *p, int delay) { // TBD: think about how to protect accesses to `p`'s field here if (process_active(p)) goto out; if (p->state == TERMINATED) { process_print(p); utils_fatal_error("Activate of terminated process.\n"); } /* p must be not active */ sched_schedule(self, p, self->clock + delay); out: } /** * like reactivate(p, 0); */ void sched_reactivate_now(struct scheduler *self, struct process *p) { sched_reactivate(self, p, 0); } /** * if p is not running or scheduled, schedule it with delay t * if p is running or scheduled, reschedule it with delay t */ void sched_reactivate(struct scheduler *self, struct process *p, int t) { // TBD: think about how to protect accesses to `p`'s field here if (p->state == TERMINATED) { process_print(p); utils_fatal_error("Reactivate of terminated process.\n"); } if (process_scheduled(p)) sched_unschedule(self, p); sched_schedule(self, p, self->clock + t); } /** * Start simulation (event queue must be non-empty) * Must be called by the main thread. * Returns when the simulation is done. */ void sched_run_simulation(struct scheduler *self) { self->current = NULL; sched_next_event(self, false); printf("Simulation done\n"); } /** remove event notice for process */ void sched_unschedule(struct scheduler *sched, struct process *p) { if (p->ev_time < 0) { process_print(p); utils_fatal_error("Unschedule of unscheduled process"); } list_remove(&p->eventelem); p->state = IDLE; p->ev_time = -1; } // end of public interface static void sched_pass_baton_impl(struct scheduler *sched, struct process *p, bool die); static bool event_time_comparator (const struct list_elem *a, const struct list_elem *b, void *aux) { struct process *pa = list_entry(a, struct process, eventelem); struct process *pb = list_entry(b, struct process, eventelem); return pa->ev_time < pb->ev_time; } /** insert p in event list, scheduled to execute at given time */ void sched_schedule(struct scheduler *self, struct process *p, int time) { if (process_scheduled(p) || process_terminated(p)) { process_print(p); sched_print_ev_list(self); utils_fatal_error("Schedule of terminated or active process."); } if (time < self->clock) { process_print(p); sched_print_ev_list(self); printf("(ev_time=%d, clock=%d)", time, self->clock); utils_fatal_error("Event scheduled in past"); } p->ev_time = time; list_insert_ordered(&self->event_list, &p->eventelem, event_time_comparator, NULL); } /* make next event happen */ void sched_next_event(struct scheduler *self, bool die) { /* if run out of events, signal main thread to return */ if (list_empty(&self->event_list)) { self->live_threads = 0; /* Signal the main thread here */ return; } /* Internal check that event list is consistent with simulation time */ if (self->clock > list_entry(list_front(&self->event_list), struct process, eventelem)->ev_time) { sched_print_ev_list(self); utils_fatal_error("Event list not time ordered.\n"); } /* get next event and advance clock */ struct process *next = list_entry(list_pop_front(&self->event_list), struct process, eventelem); self->clock = next->ev_time; next->ev_time = -1; /* switch to next thread */ sched_pass_baton_impl(self, next, die); } /** * pass_baton() is a directed yield operation * * The current thread signals `p`, then waits to be * signaled again. */ void sched_pass_baton(struct scheduler *self, struct process *p) { sched_pass_baton_impl(self, p, false); } /* * Like sched_pass_baton, except that the current thread * should signal `p` and then exit. */ void sched_pass_baton_and_die(struct scheduler *self, struct process *p) { sched_pass_baton_impl(self, p, true); } /** * Pass baton to next process 'p' unless 'p' is the current thread * Exit if 'die' is true. */ static void sched_pass_baton_impl(struct scheduler *self, struct process *p, bool die) { assert(p != NULL); struct process *caller = self->current; assert(!die || p != caller); // die => p != caller assert(caller != NULL || !die); // caller == NULL => !die /* nothing to do if passing baton to same thread */ if (caller == p) return; if (p->state == TERMINATED) { utils_fatal_error("Trying to pass baton to terminated thread"); } self->current = p; /* give next thread a go */ process_unblock(p); if (caller == NULL) { /* called by main thread */ /* wait till no more live threads or no more events */ } else if (!die) { /* wait for someone to pass back the baton */ caller->state = IDLE; process_block(caller); } else { /* if a thread terminates, signal main thread */ caller->state = TERMINATED; if (--self->live_threads == 0) { /* Signal main thread here */ } pthread_exit(NULL); } } /* event list print function */ void sched_print_ev_list(struct scheduler *self) { printf("Event list at time %d:\n", self->clock); for (struct list_elem *e = list_begin(&self->event_list); e != list_end(&self->event_list); e = list_next(e)) { struct process *p = list_entry(e, struct process, eventelem); process_print(p); } printf("End of event list\n"); } /* * Create a new scheduler and return it. */ struct scheduler * sched_create(void) { struct scheduler *self = calloc(1, sizeof(*self)); list_init(&self->event_list); /* Implement this: * initialize any internals of a scheduler here */ return self; }