/* * The scheduler is called with the context of the current thread, * or NULL when the scheduler is first started. * * The general operation of this function is: * 1. Save the context argument into the current thread. * 2. Either queue up or deallocate the current thread, * based on its state. * 3. Select a new "ready" thread to run, and set the "current" * variable to that thread. * - If no "ready" thread is available, examine the system * state to handle this situation properly. * 4. Return the context of the thread to run, so that a context- * switch will be performed to start running the next thread. * * This function is global because it needs to be called from the assembly. */ ThreadContext *__sthread_scheduler(ThreadContext *context) { /* If this is not the first call to scheduler (= no current thread), * 1. Save the context argument into the current thread. */ if (context != NULL) { current->context = context; /* 2. Queue up or deallocate the current thread. */ if (current->state == ThreadRunning) { current->state = ThreadReady; queue_add(current); } else if (current->state == ThreadBlocked) { queue_add(current); } else if (current->state == ThreadFinished) { __sthread_delete(current); } } /* 3. Select a new 'ready' thread to run. */ current = queue_take(&ready_queue); /* No Ready threads. */ if (current == NULL && queue_take(&blocked_queue) == NULL) { fprintf(stderr, "All threads finished successfully.\n"); exit(0); } else if (current == NULL) { fprintf(stderr, "Program deadlocked!\n"); exit(1); } current->state = ThreadRunning; /* 4. Return the next thread to resume executing. */ return current->context; }
/* * The scheduler is called with the context of the current thread, * or NULL when the scheduler is first started. * * The general operation of this function is: * 1. Save the context argument into the current thread. * 2. Either queue up or deallocate the current thread, * based on its state. * 3. Select a new "ready" thread to run, and set the "current" * variable to that thread. * - If no "ready" thread is available, examine the system * state to handle this situation properly. * 4. Return the context of the thread to run, so that a context- * switch will be performed to start running the next thread. * * This function is global because it needs to be called from the assembly. */ ThreadContext *__sthread_scheduler(ThreadContext *context) { // Check if we actually have a context to work with if (context != NULL) { // 1. Save the context argument into the current thread current->context = context; // 2. Either queue up or deallocate the current thread, based on its state switch (current->state) { case ThreadFinished: // Delete the thread __sthread_delete(current); break; case ThreadRunning: // Change state of thread, and queue it current->state = ThreadReady; queue_add(current); break; case ThreadBlocked: // Queue thread queue_add(current); break; case ThreadReady: // Should not be switching from a ready thread printf("ERROR: Switching from a ready thread\n"); assert(0); break; } } // 3. Select a new "ready" thread to run, and set the "current" variable // to that thread. If there are no ready threads and there are no blocked // threads, all threads in the program have successfully completed. However, // if there are one more more blocked threads in the blocked queue, the // program has become deadblocked. current = queue_take(&ready_queue); if (current == NULL && queue_empty(&blocked_queue)) { printf("All threads in the program have successfully completed, exiting\n"); exit(0); } else if (current == NULL && !queue_empty(&blocked_queue)) { printf("Program has become deadlocked, exiting\n"); exit(1); } current->state = ThreadRunning; // 4. Return the next thread to resume executing. return current->context; }
/* * The scheduler is called with the context of the current thread, * or NULL when the scheduler is first started. * * The general operation of this function is: * 1. Save the context argument into the current thread. * 2. Either queue up or deallocate the current thread, * based on its state. * 3. Select a new "ready" thread to run, and set the "current" * variable to that thread. * - If no "ready" thread is available, examine the system * state to handle this situation properly. * 4. Return the context of the thread to run, so that a context- * switch will be performed to start running the next thread. * * This function is global because it needs to be called from the assembly. */ ThreadContext *__sthread_scheduler(ThreadContext *context) { if (context != NULL) { /* Save the context argument into the current thread. */ current->context = context; /* Either queue up or deallocate current thread, based on its state. */ if (current->state == ThreadFinished) { /* Deallocatate because finished. */ __sthread_delete(current); } else if (current->state == ThreadBlocked) { /* Queue up because blocked. */ queue_add(current); } else if (current->state == ThreadRunning) { /* Queue up because yielding and make it ready. */ current->state = ThreadReady; queue_add(current); } } if (queue_empty(&ready_queue)) { if (queue_empty(&blocked_queue)) { /* Nothing ready and nothing blocked, so done! */ printf("Done.\n"); exit(0); } else { /* Nothing ready but still blocked threads, so deadlock. */ printf("Deadlock.\n"); /* Exit with error. */ exit(1); } } else { /* Select a new "ready" thread to run, and set the "current" variable to * that thread. */ current = queue_take(&ready_queue); current->state = ThreadRunning; } return current->context; }
/* * The scheduler is called with the context of the current thread, * or NULL when the scheduler is first started. * * The general operation of this function is: * 1. Save the context argument into the current thread. * 2. Either queue up or deallocate the current thread, * based on its state. * 3. Select a new "ready" thread to run, and set the "current" * variable to that thread. * - If no "ready" thread is available, examine the system * state to handle this situation properly. * 4. Return the context of the thread to run, so that a context- * switch will be performed to start running the next thread. * * This function is global because it needs to be called from the assembly. */ ThreadContext *__sthread_scheduler(ThreadContext *context) { /* Add the current thread to the ready queue */ if (context != NULL) { assert(current != NULL); if (current->state == ThreadRunning) current->state = ThreadReady; if (current->state != ThreadFinished) { current->context = context; queue_add(current); } else { __sthread_delete(current); } } /* * Choose a new process from the ready queue. * Abort if the queue is empty. */ current = queue_take(&ready_queue); if (current == NULL) { if (queue_empty(&blocked_queue)) { fprintf(stderr, "All threads completed, exiting.\n"); exit(0); } else { fprintf(stderr, "The system is deadlocked!\n"); exit(1); } } current->state = ThreadRunning; /* Return the next thread to resume executing. */ return current->context; }
/** * Reads a file and appends it to the search list */ int search_queue_add(char* file, struct queue_head* free_iovec_queue, struct queue_head* search_queue) { int bytes_read = 0; int fd = open(file, O_RDONLY); if(fd < 0){ fprintf(stderr,"Couldn't open file [%s] \n", file); return -1; } struct stat file_info; int ret = fstat(fd,&file_info); if(ret){ fprintf(stderr,"Couldnt stat file %s %d \n",file,ret); return -1; } posix_fadvise(fd,0,0,POSIX_FADV_SEQUENTIAL|POSIX_FADV_NOREUSE); int total_bytes = file_info.st_size; if(total_bytes == 0) { close(fd); return total_bytes; } int iovecs_to_read = (int)ceil((double)total_bytes/(1.0*cfg.iovec_block_size)); struct queue* assignable_nodes = queue_take(free_iovec_queue, iovecs_to_read); struct search_queue_node* sqn[iovecs_to_read]; struct queue* cur = assignable_nodes; long long i = 0; while(cur!=NULL){ sqn[i] = assignable_nodes->data; strncpy(sqn[i]->file_name,file,MAX_FILE_NAME); sqn[i]->file_name_len = strlen(file); sqn[i]->iovec_num = i; cur = cur->next; i++; } if(sqn == NULL){ return 0; } struct iovec buffers[iovecs_to_read]; // Assign base address from nodes gotten from free list for(i = 0;i < iovecs_to_read; i++){ // also populate list buffers[i].iov_base = sqn[i]->vec->iov_base; buffers[i].iov_len = sqn[i]->vec->iov_len; } // gather all files blocks that can be read into buffers bytes_read = readv(fd, buffers , iovecs_to_read); queue_prepend_all_list(search_queue, assignable_nodes); fprintf(stderr,"Read file [%s] \n%d bytes num iovecs %d \n",file,bytes_read,iovecs_to_read); close(fd); return bytes_read; }