void alarm_handler(int sig){ gtthread_blk_t *cur, *next; DEBUG_MSG("alarm_handler\n"); cur = (gtthread_blk_t *)steque_front(&thread_queue); if(cur->state == RUNNING){ // avoid update state when it's already done cur->state = READY; steque_cycle(&thread_queue); } /*if((cur->state == TERMINATED) && (cur->tID == 1)){ while(!steque_isempty(&thread_queue)){ steque_pop(&thread_queue); } return; } */ while((next = (gtthread_blk_t *)steque_front(&thread_queue))){ if(next->state == READY){ next->state = RUNNING; DEBUG_MSG("Schedule #%ld thread\n", next->tID); break; } else if(next->state == JOINED){ steque_pop(&thread_queue); DEBUG_MSG("POP #%ld thread\n", next->tID); } else if(next->state == TERMINATED) steque_cycle(&thread_queue); else{ steque_pop(&thread_queue); DEBUG_MSG("ERROR! RUNNING thread?\n"); } } DEBUG_MSG("swap: cur thread: # %ld, next thread: # %ld\n", cur->tID, next->tID); reset_timer(); if (swapcontext(&cur->uctx, &next->uctx) != 0) DEBUG_MSG("ERROR! Swap failed?\n"); DEBUG_MSG("GOOD! Swap SUCCESS!\n"); list_thread(); }
/** * Cycles through the join_steque to update the waiting status of any threads that may * be waiting on a thread to join. */ static void joininator(gtthread_t *joinee) { int i; for(i=0; i<steque_size(&g_join_steque); i++) { gtthread_t *curr = steque_front(&g_join_steque); if(curr->wait_tid == joinee->id) { curr->is_joined = 1; curr->joinee = joinee; } steque_cycle(&g_join_steque); } }
void alarm_handler(int sig) { void* current = (node_t*)malloc(sizeof(node_t)); void* next = (node_t*)malloc(sizeof(node_t)); block_signal(); current = steque_front(q); steque_cycle(q); while(1) { next = steque_front(q); if((((node_t*)next)->canceled) == 0){ break; } steque_cycle(q); } set_alarm(); swapcontext (((node_t*)current)->thread_ctx, ((node_t*)next)->thread_ctx); unblock_signal(); }
/* The gtthread_exit() function is analogous to pthread_exit. */ void gtthread_exit(void* retval) { sigprocmask(SIG_BLOCK, &vtalrm, NULL); gtthread_t *thread = steque_front(&g_threads_steque); thread->is_finished = 1; if(retval != NULL) thread->retval = retval; /* If this was the last thread in the queue, clean up and exit */ if(steque_size(&g_threads_steque) == 1) { gtthread_t *dead_thread; int num = steque_size(&g_dead_threads_steque); while(num > 0) { dead_thread = steque_front(&g_dead_threads_steque); if(dead_thread->context && dead_thread->context->uc_stack.ss_sp) free(dead_thread->context->uc_stack.ss_sp); if(dead_thread->context) free(dead_thread->context); if(dead_thread->id == 0) free(dead_thread); if(steque_size(&g_dead_threads_steque) > 1) steque_cycle(&g_dead_threads_steque); num--; } if(thread->context) free(thread->context); // Main thread was the only one that was dynamically allocated. if(thread->id == 0) free(thread); steque_destroy(&g_threads_steque); steque_destroy(&g_dead_threads_steque); steque_destroy(&g_cancelatorium); steque_destroy(&g_join_steque); /* So apparently we can't free the stack of the thread that is running. */ // if(thread->context && thread->context->uc_stack.ss_sp) // free(thread->context->uc_stack.ss_sp); exit(0); } sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); alarm_safe_yield(); }
/* will swap out currently running thread for next thread in the queue*/ void alrm_handler(int sig) { gtthread *currentThread; /* block signals */ sigset_t oldset; sigprocmask(SIG_BLOCK, &alrm, &oldset); currentThread = (gtthread *) gtthread_self(); /* currently running thread has reached the end of its time slice */ /* push this thread to the back of the queue */ steque_cycle(¤tly_running); scheduleNextAndSwap(currentThread); sigprocmask(SIG_UNBLOCK, &alrm, NULL); }
/* to find thread with threadId in the globalQ */ gtthread* getThread(gtthread_t threadId) { int size = steque_size(&globalQ); int i = 0; gtthread *temp, *returnThread; returnThread = NULL; /* Get front of queue in temp, compare id Do this till you find a match, save it in returnThread and continue loop to restore original order */ while(i < size) { temp = (gtthread *) steque_front(&globalQ); if(temp -> id == threadId) { returnThread = temp; } steque_cycle(&globalQ); i++; } return returnThread; }
/** * Helper function for yield. */ static void yield_helper(int is_alarm_safe) { if(is_alarm_safe) sigprocmask(SIG_BLOCK, &vtalrm, NULL); // Don't need to do anything if there's just one thread in the queue. if(steque_size(&g_threads_steque) == 1) return; gtthread_t *old_thread = steque_pop(&g_threads_steque); gtthread_t *new_thread = NULL; /* Find an eligible new thread - i.e., a thread that isn't queued for cancelation. */ if(!is_alarm_safe) sigprocmask(SIG_BLOCK, &vtalrm, NULL); while(steque_size(&g_threads_steque) > 0) { new_thread = steque_front(&g_threads_steque); /* Cancels threads when it's their turn to run */ int i; int canceled=0; for(i=0; i < steque_size(&g_cancelatorium); i++) { if((long) steque_front(&g_cancelatorium) == new_thread->id) { new_thread->is_finished = 1; new_thread->retval = (void *) -1; steque_pop(&g_cancelatorium); steque_pop(&g_threads_steque); steque_enqueue(&g_dead_threads_steque, new_thread); canceled=1; joininator(new_thread); // Attempt to join the thread you just canceled. break; } if(steque_size(&g_cancelatorium) > 0) steque_cycle(&g_cancelatorium); } if(!canceled) break; } /* If the thread that yielded finsihed executing, put it in the finished steque. */ if(old_thread->is_finished) { steque_enqueue(&g_dead_threads_steque, old_thread); joininator(old_thread); } else { steque_enqueue(&g_threads_steque, old_thread); } if(!is_alarm_safe) sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); // All threads have finished running. Is this necessary? if(steque_size(&g_threads_steque) == 0) exit(0); // Don't context switch if the original thread is the only one left in the queue. if(gtthread_equal(*((gtthread_t *) steque_front(&g_threads_steque)), *old_thread)) return; if(is_alarm_safe) { T.it_value.tv_usec = global_period; // Reset timer so that the next period can start immediately. sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); } swapcontext(old_thread->context, new_thread->context); }
/* The gtthread_join() function is analogous to pthread_join. All gtthreads are joinable. */ int gtthread_join(gtthread_t thread, void **status) { sigprocmask(SIG_BLOCK, &vtalrm, NULL); gtthread_t *self = steque_front(&g_threads_steque); sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); /* The range [0, g_thread_id) indicates the range of thread ids that have ever belonged to valid threads. Also can't join with self. */ if(thread.id >= g_thread_id || thread.id == self->id) return 1; sigprocmask(SIG_BLOCK, &vtalrm, NULL); self->is_joined = 0; self->wait_tid = thread.id; sigprocmask(SIG_BLOCK, &vtalrm, NULL); int found_among_dead = 0; /* First look for joinee among threads that have already terminated. */ int i; for(i=0; i<steque_size(&g_dead_threads_steque); i++) { sigprocmask(SIG_BLOCK, &vtalrm, NULL); gtthread_t *curr = steque_front(&g_dead_threads_steque); sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); if(curr->id == self->wait_tid) { found_among_dead = 1; sigprocmask(SIG_BLOCK, &vtalrm, NULL); self->joinee = curr; self->is_joined = 1; sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); break; } sigprocmask(SIG_BLOCK, &vtalrm, NULL); steque_cycle(&g_dead_threads_steque); sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); } /* If we haven't found it, wait until it terminates. */ if(!found_among_dead) { // First check to see that the thread I am waiting on is not already waiting on me. for(i=0; i<steque_size(&g_join_steque); i++) { sigprocmask(SIG_BLOCK, &vtalrm, NULL); gtthread_t *curr = steque_front(&g_join_steque); sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); if(curr->wait_tid == self->id && curr->id == self->wait_tid) { sigprocmask(SIG_BLOCK, &vtalrm, NULL); self->is_joined = -1; self->wait_tid = -1L; sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); return 1; } sigprocmask(SIG_BLOCK, &vtalrm, NULL); steque_cycle(&g_join_steque); sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); } // If joinee isn't already waiting on me, enqueue myself in the join queue... sigprocmask(SIG_BLOCK, &vtalrm, NULL); steque_enqueue(&g_join_steque, self); sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); // ... and wait until joinee terminates. while(!self->is_joined) alarm_safe_yield(); } if(status) *status = self->joinee->retval; sigprocmask(SIG_BLOCK, &vtalrm, NULL); self->joinee = NULL; self->wait_tid = -1L; self->is_joined = -1; sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); /* Remove yourself from the join steque, if you put yourself there. */ if(!found_among_dead) { for(i=0; i<steque_size(&g_join_steque); i++) { sigprocmask(SIG_BLOCK, &vtalrm, NULL); gtthread_t *curr = steque_front(&g_join_steque); sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); if(gtthread_equal(*self, *curr)) { steque_pop(&g_join_steque); break; } if(steque_size(&g_join_steque) > 0) { sigprocmask(SIG_BLOCK, &vtalrm, NULL); steque_cycle(&g_join_steque); sigprocmask(SIG_UNBLOCK, &vtalrm, NULL); } } } return 0; }