void runthreads() { sigset_t x; sigemptyset(&x); sigaddset(&x, SIGALRM); sigprocmask(SIG_BLOCK, &x, NULL); //If we have no thread quit if(threadCounter == 0) { return; } //Setup the quantum timer struct itimerval timer; timer.it_interval.tv_sec = 0; timer.it_interval.tv_usec = quantum_size; timer.it_value.tv_sec = 0; timer.it_value.tv_usec = quantum_size; setitimer(ITIMER_REAL, &timer, 0); sigset(SIGALRM, &switch_thread); //Pop the first thread to run current_thread = list_shift_int(runQueue); //Set its state to RUNNING thread_table[current_thread].state = RUNNING; //Set the start_running time thread_table[current_thread].start_time = getCurrentNanoTime(); sigprocmask(SIG_UNBLOCK, &x, NULL); if(swapcontext(&uctx_main, &thread_table[current_thread].context) == -1) { return; } while(!list_empty(runQueue)) { current_thread = list_shift_int(runQueue); thread_table[current_thread].state = RUNNING; thread_table[current_thread].start_time = getCurrentNanoTime(); if(swapcontext(&uctx_main, &thread_table[current_thread].context) == -1) { return; } } sigprocmask(SIG_BLOCK, &x, NULL); int i; for(i = 0; i < threadCounter; i++) { thread_table[i].state = EXIT; } }
int sfs_write(int fileID, char *buf, int length){ if(fdt[fileID][open] == false) return -1; if(fileID > MAX_NUM_FILES || fileID < 0) return -1; int fatEntry = files[fileID].fatIndex; //if this is the first time writing to this file, give it a fat index if(fat[fatEntry][block_index] == unused){ fat[fatEntry][block_index] = list_shift_int(free_blocks); fat[fatEntry][next] = eof; } //we must find the block where the file ends int numWrittenBlocks = files[fileID].size / BLOCK_SIZE; int i; for(i=0; i < numWrittenBlocks; i++) fatEntry = fat[fatEntry][next]; //need to append to the rest of this block char block[BLOCK_SIZE]; read_blocks(fat[fatEntry][block_index], 1, block); int numBytes; if (length < (BLOCK_SIZE - fdt[fileID][write_ptr] % BLOCK_SIZE)) numBytes = length; else numBytes = BLOCK_SIZE - fdt[fileID][write_ptr] % BLOCK_SIZE; memcpy(block + (fdt[fileID][write_ptr] % BLOCK_SIZE), buf, numBytes); write_blocks(fat[fatEntry][block_index], 1, block); fdt[fileID][write_ptr] += numBytes; int numBytesWritten = numBytes; //now we can write entire blocks until no more bytes need to be //written while(numBytesWritten < length){ fat[fatEntry][next] = firstFreeFat(); fatEntry = fat[fatEntry][next]; fat[fatEntry][block_index] = list_shift_int(free_blocks); fat[fatEntry][next] = eof; write_blocks(fat[fatEntry][block_index], 1, buf+numBytesWritten); if((length - numBytesWritten) >= BLOCK_SIZE){ numBytesWritten +=BLOCK_SIZE; fdt[fileID][write_ptr] +=BLOCK_SIZE; } else{ numBytesWritten += length-numBytesWritten; fdt[fileID][write_ptr] += length-numBytesWritten; } } //update sizes and pointers and write file system blocks to disk files[fileID].size += numBytesWritten; write_fs_blocks(); return numBytesWritten; }
/* 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); } } }
/* Starts running one of the threads in the runqueue. When this method exits, all of the created threads should have completed running. */ void runthreads(){ //Block Signals sigset_t blockSet; sigemptyset(&blockSet); sigaddset(&blockSet, SIGALRM); sigprocmask(SIG_BLOCK, &blockSet, NULL); //Create a timer to know when the scheduler should switch contexts. struct itimerval timer; timer.it_interval.tv_sec = 0; timer.it_interval.tv_usec = quantum_size; timer.it_value.tv_sec = 0; timer.it_value.tv_usec = quantum_size; setitimer(ITIMER_REAL, &timer, 0); sigset(SIGALRM, &scheduler); //Get the first thread that should run. runningThreadId = list_shift_int(runqueue); tcbTable[runningThreadId].state = RUNNING; clock_gettime(CLOCK_REALTIME,&tcbTable[runningThreadId].start); //Unblock signal sigprocmask(SIG_UNBLOCK, &blockSet, NULL); //Go to the context for the thread (i.e. handler() function). Also, save the //current context in uctx_main. if(swapcontext(&uctx_main, &tcbTable[runningThreadId].context) == -1) { perror("swapcontext error"); exit(1); } //To ensure that there are still no remaining threads left. while(totalThreadsExited<totalThreadsCreated); }
/** * 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); }
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(); }
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 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(); }
/** * Removes the given semaphore. * * Fails and gives error message if any threads are still waiting on the given semaphore. * * Prints warning message if the current value of the semaphore is not the same as the initial value. */ void destroy_semaphore(int semaphore) { #if DEBUG == 1 printf("destorying semaphore %d\n", semaphore); #endif // check if any threads are still on the wait queue if (list_shift_int(semaphores[semaphore]->thread_queue) != 0) { perror("semaphore wait queue is not empty!\n"); exit(-1); } else { // check value of semaphore if (semaphores[semaphore]->value != semaphores[semaphore]->value) { printf( "warning: current value of semaphore (%d) does not equal initial value (%d)", semaphores[semaphore]->value, semaphores[semaphore]->init_value); } // else decrement semaphore counter and detroy the queue next_semaphore_index--; list_destroy(&(semaphores[semaphore]->thread_queue)); } }
/** * 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); } }