#define _GNU_SOURCE #include #include #include #include #include #include #include #include "utils.h" #include "process.h" #include "scheduler.h" // Remove this attribute once process_init is implemented static void *process_body(void *) __attribute__((__unused__)); /* * Initialize process 'p', setting its 'name', associated it with * scheduler 'sched', and storing its body 'body'. * * This should create a new thread that runs `process_body` */ void process_init(struct process *p, const char *name, struct scheduler *sched, process_body_fun_t body) { p->name = name; p->ev_time = -1; /* unscheduled */ p->state = IDLE; p->scheduler = sched; p->body = body; sched->live_threads++; /* Initialize all threading related aspects of a process here. * Start a new detached threads that will run `process_body`, * passing `p` as an argument. */ } /** * Main body for simulated processes. * Simulated processes start in the IDLE state until * they are activated the first time. */ static void * process_body(void *arg) { struct process *self = arg; // TBD: acquire the lock protecting this process process_block(self); // TBD: release the lock protecting this process self->body(self); process_terminate(self); return NULL; } /** * Block until made runnable & notified */ void process_block(struct process *self) { /* Implement this: the calling thread must wait here * while it is IDLE until someone else makes it RUNNING * Hint: you should assume, and possibly assert, that * the lock protecting this process has already been * acquired. */ } /** * unblock process */ void process_unblock(struct process *self) { /* * Hint: you should assume, and possibly assert, that * the lock protecting this process has already been * acquired. */ self->state = RUNNING; /* Implement this: signal the thread (waiting in process_block) */ } /* The remaining functions all access internal state of a process. * The must be called while holding the lock associated with this * process. In order to avoid atomicity violations, this lock will * likely be acquired at appropriate places in scheduler.c */ /** test if process is active, i.e. running or scheduled */ bool process_active(struct process *self) { return self->state == RUNNING || self->ev_time >= 0; } /** test for scheduled (has an event notice; false if running) */ bool process_scheduled(struct process *self) { return self->ev_time >= 0; } /** test if this process is terminated */ bool process_terminated(struct process *self) { return self->state == TERMINATED; } /** make this process idle (unscheduled and not running) */ void process_cancel(struct process *self) { if (process_scheduled(self)) { sched_unschedule(self->scheduler, self); } } /** * make process unrunnable (no longer activatable); */ void process_terminate(struct process *self) { process_cancel(self); self->state = TERMINATED; self->ev_time = -1; if (self->scheduler->current == self) sched_next_event(self->scheduler, true); } /** * enqueue reference to this process in queue q, and passivate */ void process_waitq(struct process *self, struct list *q) { list_push_back(q, &self->elem); sched_passivate(self->scheduler); } /** * print name, state, and scheduled event time for process */ void process_print(struct process *self) { printf("%s: %s ev_time = %d\n", self->name, (self->state == IDLE ? "IDLE" : self->state == RUNNING ? "RUNNING" : "TERMINATED"), self->ev_time); }