void mksfs(int fresh){ int i; if (fresh){ init_fresh_disk(disk_name, BLOCK_SIZE, NUM_BLOCKS); free_blocks = list_create(NULL); for (i=2; i<NUM_BLOCKS-1; i++){ list_append_int(free_blocks, i); } for (i=0; i<NUM_BLOCKS; i++){ fat[i][block_index] = unused; fat[i][next] = eof; } for (i=0; i<MAX_NUM_FILES; i++){ files[i].created = false; files[i].fatIndex = eof; } } else{ init_disk(disk_name, BLOCK_SIZE, NUM_BLOCKS); read_blocks(0,1, &files); read_blocks(1,1, &fat); read_blocks(NUM_BLOCKS-1, 1, &free_blocks); } for(i=0; i<MAX_NUM_FILES; i++){ fdt[i][open] = false; fdt[i][read_ptr] = false; fdt[i][write_ptr] = files[i].size; } }
/* The scheduler is called each time an ALRM signal is fired. It is responsible for preempting the currently running thread and giving a chance to another thread that is eligible to run in the runqueue. */ void scheduler(){ noSwitches++; if (!list_empty(runqueue) ) { //Get the next thread to run and save the current one in a temp variable. int threadToPreempt = runningThreadId; runningThreadId = list_shift_int(runqueue); //Update the times to print the total time run on the CPU at the end of //the program. clock_gettime(CLOCK_REALTIME, &tcbTable[threadToPreempt].end); tcbTable[threadToPreempt].run_time += tcbTable[threadToPreempt].end.tv_nsec - tcbTable[threadToPreempt].start.tv_nsec; clock_gettime(CLOCK_REALTIME, &tcbTable[runningThreadId].start); if (tcbTable[threadToPreempt].state == RUNNABLE || tcbTable[threadToPreempt].state == RUNNING) { //If the thread we are about to preempt is still RUNNABLE or RUNNING, we //add it to the runqueue so that it can be scheduler at a later time. runqueue = list_append_int(runqueue, threadToPreempt); } //Perform the context switch. if (swapcontext(&tcbTable[threadToPreempt].context, &tcbTable[runningThreadId].context) == -1) { printf("swapcontext error\n"); fflush(stdout); } } }
/** * Decrements the value of the given semaphore. * If the value goes below 0, the thread is put into a WAIT state. */ void semaphore_wait(int semaphore) { // disable alarm while working with semaphore sighold(SIGALRM); #if DEBUG == 1 char print[100]; sprintf(print, "wait called on semaphore %d with value %d\n", semaphore, semaphores[semaphore]->value); perror(print); #endif semaphores[semaphore]->value -= 1; if (semaphores[semaphore]->value < 0) { #if DEBUG == 1 sprintf(print, "thread %d put on waitqueue\n", current_thread); perror(print); #endif // block thread threads[current_thread]->state = WAIT; // put it on the wait queue list_append_int(semaphores[semaphore]->thread_queue, current_thread); //unblock alarm and wait for scheduler to take over sigrelse(SIGALRM); while (threads[current_thread]->state == WAIT) ; // when semaphore is signaled thread will be RUNNABLE again and return from this function } else { //don't block thread, unblock alarm and go back to the thread sigrelse(SIGALRM); } }
/** * Increments the value of the given semaphore. * If the value was 0, then the thread at the top of the wait queue is put on the runqueue. */ void semaphore_signal(int semaphore) { int next_thread; // disable alarm while working with semaphore sighold(SIGALRM); #if DEBUG == 1 char print[100]; sprintf(print, "signaling semaphore %d with value %d\n", semaphore, semaphores[semaphore]->value); perror(print); #endif if (semaphores[semaphore]->value < 0) { // make first thread on the wait queue RUNNABLE next_thread = list_shift_int(semaphores[semaphore]->thread_queue); if (next_thread == 0) { perror("no threads on waitqueue\n"); exit(-1); } #if DEBUG == 1 sprintf(print, "signaling thread %d\n", next_thread); perror(print); #endif threads[next_thread]->state = RUNNABLE; list_append_int(runqueue, next_thread); } semaphores[semaphore]->value += 1; sigrelse(SIGALRM); }
/** * Switches control from the main thread to one of the threads in the runqueue. * Activates the thread switcher. */ void runthreads() { int i; current_thread = 1; setup_signals(); //queue all the threads for (i = 2; i < next_thread_index; i++) { list_append_int(runqueue, i); } /* setup our timer */ it.it_interval.tv_sec = 0; it.it_interval.tv_usec = quantum; it.it_value = it.it_interval; if (setitimer(ITIMER_REAL, &it, NULL)) perror("setitiimer"); /* force a swap to the first context */ #if DEBUG == 1 printf("starting the first thread\n"); #endif swapcontext(&main_context, threads[current_thread]->context); #if DEBUG == 1 printf("returned to main context; destorying runqueue\n"); #endif list_destroy(&runqueue); }
int mythread_create(char *threadname, void (*threadfunc)(), int stacksize) { int pointer = threadCounter; if(threadCounter >= THREAD_MAX_SIZE) { return -1; } getcontext(&thread_table[pointer].context); thread_table[pointer].context.uc_link = &uctx_main; thread_table[pointer].context.uc_stack.ss_sp = malloc(stacksize); thread_table[pointer].context.uc_stack.ss_size = stacksize; thread_table[pointer].context.uc_stack.ss_flags = 0; sigemptyset(&thread_table[pointer].context.uc_sigmask); thread_table[pointer].thread_name = threadname; thread_table[pointer].thread_id = pointer; thread_table[pointer].state = RUNNABLE; //Time only count when thread is running thread_table[pointer].start_time = 0; makecontext(&thread_table[pointer].context, threadfunc, 0); //Finally add the thread to the running queue runQueue = list_append_int(runQueue, pointer); threadCounter ++; return pointer ; }
void semaphore_signal(int semaphore) { //Init signal sigset_t x; sigemptyset(&x); sigaddset(&x, SIGALRM); //Block signal sigprocmask(SIG_BLOCK, &x, NULL); semaphore_table[semaphore].value++; if(semaphore_table[semaphore].value <= 0) { int thread; thread = list_shift_int(semaphore_table[semaphore].queue); thread_table[thread].state = RUNNABLE; //Put back the thread in the running queue runQueue = list_append_int(runQueue, thread); } sigprocmask(SIG_UNBLOCK, &x, NULL); switch_thread(); }
list_t *digits(int i) { list_t *lst = NULL; while (i > 0) { int digit = i % 10; i = i / 10; lst = list_append_int(lst, digit); } return lst; }
void switch_thread() { switch_ctr++; //If the running queue is empty and current thread is exit if(list_empty(runQueue) && thread_table[current_thread].state == EXIT) { //Calculate the running time long end_time = getCurrentNanoTime(); thread_table[current_thread].running_time += end_time - thread_table[current_thread].start_time; end = 1; setcontext(&uctx_main); } //If we have a another thread in the running queue we switch if(!list_empty(runQueue)) { //Switch thread with the first in the queue int old_thread = current_thread; current_thread = list_shift_int(runQueue); //Update the running time of the old thread long end_time = getCurrentNanoTime(); thread_table[old_thread].running_time += end_time - thread_table[old_thread].start_time; //Reset the start time of the current_thread thread_table[current_thread].start_time = getCurrentNanoTime(); //If we still need to run the old thread we put it back in the running quue if(thread_table[old_thread].state == RUNNABLE || thread_table[old_thread].state == RUNNING) { runQueue = list_append_int(runQueue, old_thread); } //Update the context if(swapcontext(&thread_table[old_thread].context, &thread_table[current_thread].context) == -1) { return; } } else //No other thread in the queue { if(swapcontext(&thread_table[current_thread].context, &uctx_main) == -1) { return; } } }
/* When a thread calls this function, the value of the sempahore is decremented. If the value goes below 0, the thread is put into a BLOCKED state. The calling thread is no longer in the runqueue and will be inserted into the waitqueue for the given sempahore. */ void semaphore_wait(int semaphore){ sigset_t blockSet; sigemptyset(&blockSet); sigaddset(&blockSet, SIGALRM); sigprocmask(SIG_BLOCK, &blockSet, NULL); int tmpNoSwitches = noSwitches; //Decrement the semaphore value. (semTable[semaphore]).value--; if((semTable[semaphore]).value<0) { //This thread has to wait so we set it's state to block and put it in the //wait queue for the given semaphore. (tcbTable[runningThreadId]).state = BLOCKED; (semTable[semaphore]).thread_queue = list_append_int((semTable[semaphore]).thread_queue,runningThreadId); } sigprocmask(SIG_UNBLOCK,&blockSet,NULL); while(tmpNoSwitches==noSwitches); }
void semaphore_wait(int semaphore) { sigset_t set; sigemptyset(&set); sigaddset(&set,SIGALRM); sigprocmask(SIG_BLOCK,&set,NULL); long long int old_switch_ctr = switch_ctr; semaphore_table[semaphore].value --; if(semaphore_table[semaphore].value < 0) { thread_table[current_thread].state = BLOCKED; list_append_int(semaphore_table[semaphore].queue, current_thread); } sigprocmask(SIG_UNBLOCK,&set,NULL); while(old_switch_ctr==switch_ctr); }
/* This function creates a new thread and returns an integer that points to the thread control block that is allocated to the newly created thread in the thread control block table. It returns -1 if it is unable to create a new thread. Further, the function creates and initializes the user context for the thread being created. */ int mythread_create(char *threadName, void(*threadfunc)(), int stacksize){ //Set basic information about the thread. strcpy(tcbTable[threadId].thread_name, threadName); tcbTable[threadId].thread_id = threadId; tcbTable[threadId].state = RUNNABLE; //Set the context information about the thread. getcontext(&tcbTable[threadId].context); //Save the current context. tcbTable[threadId].context.uc_link = &uctx_main; //The context that will be resumed when the current context exits. tcbTable[threadId].context.uc_stack.ss_sp = malloc(stacksize); //Allocate the stack and make it point to the beginning of the stack. tcbTable[threadId].context.uc_stack.ss_size = stacksize; //Set the signal stack size. makecontext(&tcbTable[threadId].context,threadfunc,0); //Creates the context with the information set above. //Add thread to the runqueue. runqueue = list_append_int(runqueue,threadId); totalThreadsCreated++; //If an error occured, return -1. Otherwise, return the thread id. if(!tcbTable[threadId].context.uc_stack.ss_sp){ //We failed to allocate memory for the stack #if DEBUG == 1 printf("Failed to allocate memory for the stack\n"); fflush(stdout); #endif return -1; }else{ #if DEBUG == 1 printf("state of thread %s is %d\n",tcbTable[threadId].thread_name,tcbTable[threadId].state); #endif int tmp = threadId; threadId++; return tmp; } }
/* When a thread calls this function, the value of the semaphore is incremented. If the value is less than one, then we should have at least one thread waiting on it. If this is the case then we take the thread waiting on the current semaphore out of the semaphore wait queue and insert it into the runqueue. */ void semaphore_signal(int semaphore){ // Block Signals sigset_t blockSet; sigemptyset (&blockSet); sigaddset(&blockSet, SIGALRM); sigprocmask(SIG_BLOCK, &blockSet, NULL); /* Increase Semaphore value */ semTable[semaphore].value = semTable[semaphore].value + 1; if (semTable[semaphore].value < 1) { int waitingThread; //Get the thread that has been granted the semphore. waitingThread = list_shift_int(semTable[semaphore].thread_queue); //Set the state of the thread granted the semaphore to runnable. tcbTable[waitingThread].state = RUNNABLE; //Add this thread to the back of the runqueue. runqueue = list_append_int(runqueue, waitingThread); } // Unblock Signals sigprocmask(SIG_UNBLOCK, &blockSet, NULL); scheduler(); }
void list_append_range_with_step(list *l, int start, int end, int step){ register int i; for (i=start; i<end; i+=step){ list_append_int(l, i); } }
void list_append_range(list *l, int start, int end){ register int i; for (i=start; i<end; i++){ list_append_int(l, i); } }
/** * Modified version of the swap.c example provided on http://cgi.cs.mcgill.ca/~xcai9/2012_comp_310.html * * The scheduling algorithm; selects the next context to run, then starts it. */ void scheduler() { sighold(SIGALRM); #if DEBUG == 1 char print[100]; #endif // increment current thread run time threads[current_thread]->total_time += quantum; // check if thread has completed if (threads[current_thread]->state == EXIT) { #if DEBUG == 1 sprintf(print, "thread %d completed, run time: %d\n", current_thread, threads[current_thread]->total_time); perror(print); #endif } else if (threads[current_thread]->state == WAIT) { #if DEBUG == 1 sprintf(print, "thread %d blocked\n", current_thread); perror(print); #endif } else { // else append it to the end of the runqueue threads[current_thread]->state = RUNNABLE; list_append_int(runqueue, current_thread); #if DEBUG == 1 sprintf(print, "thread %d put on runqueue, run time: %d\n", current_thread, threads[current_thread]->total_time); perror(print); #endif } // get the next thread waiting to run from the run queue current_thread = list_shift_int(runqueue); // check if there is a next item // current_thread is 0 if there is not if (current_thread == 0) { // return to main context #if DEBUG == 1 perror("all threads completed\n"); #endif //turn off timer sighold(SIGALRM); it.it_interval.tv_usec = 0; it.it_value.tv_usec = 0; //returns to main context return; } else { // else run next thread on queue threads[current_thread]->state = RUNNING; #if DEBUG == 1 sprintf(print, "scheduling thread %d\n", current_thread); perror(print); #endif sigrelse(SIGALRM); setcontext(threads[current_thread]->context); } }