static void yield_running_thread() { minithread_t *first_runnable = NULL; minithread_t *temp = running_thread; int res = multilevel_queue_dequeue(runnable_queue, curr_level, (void **)(&first_runnable)); if (running_thread != scheduler_thread) { multilevel_queue_enqueue(runnable_queue, running_thread->level, running_thread); } if (res != -1) { // if the level of dequeued thread is different from current running, update the curr_level // and also the curr_level_quanta is reset to 0, since we are switching to the next level if (curr_level != first_runnable->level) { curr_level = first_runnable->level; curr_level_quanta = 0; } if (running_thread != first_runnable) { running_thread = first_runnable; minithread_switch(&temp->top, &running_thread->top); } } else { running_thread = scheduler_thread; minithread_switch(&temp->top, &running_thread->top); } }
/* * Initialization. * Initializes reaper and idle threads, starts and initializes main thread. * Also creates the scheduler and other data * */ void minithread_system_initialize(proc_t mainproc, arg_t mainarg) { //allocate room for schedule data (global) schedule_data = (scheduler *) malloc(sizeof(scheduler)); if (schedule_data == NULL) { exit(1); //OOM } schedule_data->cleanup_queue = queue_new(); schedule_data->multi_run_queue = multilevel_queue_new(num_levels); reaper_sema = semaphore_create(); semaphore_initialize(reaper_sema, 0); // create main thread minithread_t* main_thread = minithread_fork(mainproc, mainarg); // initialize idle thread idle_thread = (minithread_t *) malloc(sizeof(minithread_t)); idle_thread->stacktop = NULL; idle_thread->thread_id = -1; //initialize alarm bookeeping data structure (priority queue) alarm_init(); //remove from run queue and run it schedule_data->running_thread = main_thread; main_thread->status = RUNNING; multilevel_queue_dequeue(schedule_data->multi_run_queue, 0, (void *) main_thread); //reaper thread init reaper_thread = minithread_create(reaper_queue_cleanup, NULL); minithread_start(reaper_thread); //Start clock minithread_clock_init(clock_period, clock_handler); //Initialize network network_initialize(network_handler); //START MAIN PROC //minithread_switch also enables clock interrupts minithread_switch(&idle_thread->stacktop, &main_thread->stacktop); //always comes back here to idle in the kernel level (allows freeing resources) while (1) { minithread_t* next = next_runnable(); set_interrupt_level(DISABLED); next->status = RUNNING; schedule_data->running_thread = next; minithread_switch(&idle_thread->stacktop, &next->stacktop); } }
/* * Initialization. * * minithread_system_initialize: * This procedure should be called from your C main procedure * to turn a single threaded UNIX process into a multithreaded * program. * * Initialize any private data structures. * Create the idle thread. * Fork the thread which should call mainproc(mainarg) * Start scheduling. * */ void minithread_system_initialize(proc_t mainproc, arg_t mainarg) { minithread_t clean_up_thread = NULL; int a = 0; void* dummy_ptr = NULL; minithread_t tmp = NULL; tmp = NULL; dummy_ptr = (void*)&a; current_id = 0; // the next thread id to be assigned id_lock = semaphore_create(); semaphore_initialize(id_lock,1); runnable_q = multilevel_queue_new(4); blocked_q = queue_new(); blocked_q_lock = semaphore_create(); semaphore_initialize(blocked_q_lock,1); dead_q = queue_new(); dead_q_lock = semaphore_create(); semaphore_initialize(dead_q_lock,1); dead_sem = semaphore_create(); semaphore_initialize(dead_sem,0); runnable_q_lock = semaphore_create(); semaphore_initialize(runnable_q_lock,1); clean_up_thread = minithread_create(clean_up, NULL); multilevel_queue_enqueue(runnable_q, clean_up_thread->priority,clean_up_thread); runnable_count++; minithread_clock_init(TIME_QUANTA, (interrupt_handler_t)clock_handler); init_alarm(); current_thread = minithread_create(mainproc, mainarg); minithread_switch(&dummy_ptr, &(current_thread->stacktop)); return; }
int scheduler() { int next_priority = 0; minithread_t next = NULL; minithread_t temp = NULL; while (1) { while (runnable_count == 0) { set_interrupt_level(ENABLED); }; set_interrupt_level(DISABLED); //dequeue from runnable threads next_priority = choose_priority_level(); if (multilevel_queue_dequeue(runnable_q, next_priority,(void**)(&next)) != -1) { runnable_count--; temp = current_thread; current_thread = next; minithread_switch(&(temp->stacktop),&(next->stacktop)); return 0; } //if dead/runnable queue is empty, do nothing (idle thread) } return 0; }
/* stops execution of current minithread, and then proceeds to get next RUNNABLE minithread * and begin execution from where that minithread left off. */ void minithread_stop() { set_interrupt_level(DISABLED); minithread_t* curr = schedule_data->running_thread; curr->status = WAITING; schedule_data->running_thread = idle_thread; if (multilevel_queue_total_elements(schedule_data->multi_run_queue) > 0) { minithread_t* next = next_runnable(); next->status = RUNNING; schedule_data->running_thread = next; //switch to new RUNNING mini thread minithread_switch(&curr->stacktop, &next->stacktop); } else { //idle in the kernel level minithread_switch(&curr->stacktop, &idle_thread->stacktop); } }
int minithread_context_switch(int add_to_ready, int switch_to_idle) { minithread_t tcb; stack_pointer_t* old_stack_top; interrupt_level_t prev_level = set_interrupt_level(DISABLED); // Add it to the ready queue if needed if (add_to_ready == 1 && running != idle) { minithread_add_to_ready_queue(running, 0); } // get the new thread tcb = minithread_get_from_ready_queue(0); // why context switch to the running thread, we can just let it continue... if (tcb == running) { set_interrupt_level(prev_level); return -1; } // if there's no thread to switch to and we don't want the idle thread... if (tcb == NULL && switch_to_idle == 0) { set_interrupt_level(prev_level); return -2; } else if (tcb == NULL) { tcb = idle; } // Otherwise, set the running thread variable old_stack_top = running->stack_top; running = tcb; // Update the number of ticks until the next switch ticks_until_switch = minithread_get_priority_quanta(tcb->priority); // Perform the context switch minithread_switch(old_stack_top, tcb->stack_top); // Context switch threads will handle re-enabling interrupts, but still... set_interrupt_level(prev_level); return 1; }
/* * Scheduler */ int minithread_schedule() { minithread_t old = current; if ( alarm_has_ready() && old != idle ) { current = idle; } else if(multilevel_queue_length(ready_queue)>0) { minithread_age(ready_queue); multilevel_queue_dequeue(ready_queue, PRIORITY_SHORT, ¤t); } else { current = idle; } if ( current->priority == PRIORITY_SHORT ) { current_quanta_end = ticks + SHORT_QUANTA; } else { current_quanta_end = ticks + LONG_QUANTA; } minithread_switch(&(old->sp), &(current->sp)); return 0; }
static int final_proc(int *arg) { interrupt_level_t old_level = set_interrupt_level(DISABLED); // switch to the reaper thread after completion of a thread // if the running thread is already reaper, switch to the next runnable thread // reaper thread will free the elements of stopped queue which contains all finished threads. if (running_thread != reaper_thread) { queue_append(stopped_queue, running_thread); } if (queue_length(stopped_queue)) { minithread_t *temp = running_thread; running_thread = reaper_thread; minithread_switch(&temp->top, &reaper_thread->top); } else { stop_running_thread(); } set_interrupt_level(old_level); return 0; }
void minithread_yield() { interrupt_level_t old_level = set_interrupt_level(DISABLED); if (multilevel_queue_total_elements(schedule_data->multi_run_queue) == 0) { set_interrupt_level(old_level); return; } minithread_t* old = schedule_data->running_thread; old->status = RUNNABLE; //adds itself to the end of the run queue multilevel_queue_enqueue(schedule_data->multi_run_queue, old->level, old); //yields to the next minithread in the run_queue minithread_t* next = next_runnable(); next->status = RUNNING; schedule_data->running_thread = next; minithread_switch(&old->stacktop, &next->stacktop); }