Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include "request.h"
- #include "server_thread.h"
- #include "common.h"
- #include <pthread.h>
- #include "semaphore.h"
- /* REFACTORING IDEAS
- *
- *
- * - The evictQueue would be a super easy this to completely remake, all it has to do is keep
- * track of what are the most recently used files, if you look up LRU methods on google
- * there are a bunch of different implementations, the most common being to have a count
- * attached to each file, and every time a file is used, you reset that files count, but increment all other files counts,
- * and then to find the least recently used, you find the one with the highest count
- *
- * - Should definitely reorganize the logic and the order of how things are called in
- * do_server_request() this will probably be super different between every project
- *
- * - removeFileFromCache() and updateEvictQueue() both have a bunch of logic that essentially relates to navigating through
- * the linked list, this can probably be put into its own general function
- *
- * - Not storing hash in the file struct, might be suspicious. Recalculate new each time
- *
- * - Generally, alot of the ways that I navigated and searched through the lists were
- * not the best way, so I think there are some good changes to make there.
- */
- struct server {
- int nr_threads;
- int max_requests;
- int max_cache_size;
- int exiting;
- int size;
- /* add any other parameters you need */
- };
- struct file {
- struct file_data * fileData;
- unsigned long hash; // This would probably be pretty bait to keep in,
- // it is probably better to just recalculate the
- // hash each time you want to use it, as long as
- // it doest affect speed
- struct file * nextFileInCache;
- struct file * prevFileInCache;
- struct file * nextFileInQueue;
- struct file * prevFileInQueue;
- sem_t * sem;
- pthread_mutex_t * lock;
- int users;
- };
- struct fileCache{
- int size;
- int maxSize;
- struct file **files;
- };
- struct evictQueue{
- struct file * leastRecent;
- struct file * mostRecent; // I don't think I used this, you can get rid of it, or implement it
- };
- pthread_t* threadPool;
- pthread_mutex_t* lock;
- pthread_cond_t* cvEmpty;
- pthread_cond_t* cvFull;
- int in;
- int out;
- int* buffer;
- struct fileCache * fileCache;
- struct evictQueue * evictQueue;
- pthread_mutex_t * fileLock;
- unsigned long hash(char *str);
- struct file * cacheLookup(char * fileName);
- struct file * cacheInsert(struct file_data * fileData);
- void cacheEvict(int amountToEvict);
- void updateEvictQueue(struct file * file);
- void readFileFromDisk(struct request * rq, struct file_data * data);
- void removeFileFromCache(struct file * file);
- /* static functions */
- /* initialize file data */
- static struct file_data *
- file_data_init(void)
- {
- struct file_data *data;
- data = Malloc(sizeof(struct file_data));
- data->file_name = NULL;
- data->file_buf = NULL;
- data->file_size = 0;
- return data;
- }
- /* free all file data */
- static void
- file_data_free(struct file_data *data)
- {
- free(data->file_name);
- free(data->file_buf);
- free(data);
- }
- static void
- do_server_request(struct server *sv, int connfd)
- {
- int ret;
- struct request *rq;
- struct file_data *data;
- data = file_data_init();
- /* fill data->file_name with name of the file being requested */
- rq = request_init(connfd, data);
- if (!rq) {
- file_data_free(data);
- return;
- }
- // If the there is no cache, just read that shit normally
- if(sv->max_cache_size <= 0){
- ret = request_readfile(rq);
- if (ret != 0) request_sendfile(rq);
- file_data_free(data);
- request_destroy(rq);
- }else{
- // Lock the datastructures
- pthread_mutex_lock(fileLock);
- // Check if the file is in the cache, if it isn't 'file *' will be null
- struct file * file = cacheLookup(data->file_name);
- if(file != NULL){
- // Update the file so that it is "in use"
- pthread_mutex_lock(file->lock);
- if(file->users == 0) sem_wait(file->sem);
- file->users++;
- pthread_mutex_unlock(file->lock);
- // Fill the request with the stored data from the file stored in the cache
- request_set_data(rq, file->fileData);
- // Update the evict queue to reflect that this file was recently used, ie put it at the back of the queue
- updateEvictQueue(file);
- // Unlock the data structures for before sending the request
- pthread_mutex_unlock(fileLock);
- // Send the request and destroy it
- request_sendfile(rq);
- request_destroy(rq);
- // Decrement the number of users
- pthread_mutex_lock(file->lock);
- file->users--;
- if(file->users == 0) sem_post(file->sem);
- pthread_mutex_unlock(file->lock);
- }else{
- // Lock the data structues
- pthread_mutex_unlock(fileLock);
- // Read the file from disk, send the request, then destroy it
- ret = request_readfile(rq);
- if (ret != 0) request_sendfile(rq);
- request_destroy(rq);
- // Cache the file, evict some space if nessecary
- pthread_mutex_lock(fileLock);
- if(cacheLookup(data->file_name) == NULL){
- if(data->file_size < fileCache->maxSize){
- if((data->file_size + fileCache->size) > fileCache->maxSize){
- cacheEvict(data->file_size - fileCache->maxSize + fileCache->size + 1);
- }
- file = cacheInsert(data);
- updateEvictQueue(file);
- }
- }
- pthread_mutex_unlock(fileLock);
- }
- }
- }
- /* entry point functions */
- // Searches the cache for a file, returns a pointer to it if it exists, NULL otherwise
- struct file * cacheLookup(char * fileName){
- if(strlen(fileName) == 0) {
- return NULL;
- }
- long index = hash(fileName);
- if( fileCache->files[index] == NULL){
- return NULL;
- }else{
- struct file * file = fileCache->files[index];
- while(file != NULL && strcmp(file->fileData->file_name, fileName) != 0){
- file = file->nextFileInCache;
- }
- return file;
- }
- }
- struct file * cacheInsert(struct file_data * fileData){
- struct file * newFile = malloc(sizeof(struct file));
- // Create the file
- char * fileName = fileData->file_name;
- newFile->fileData = fileData;
- newFile->sem = malloc(sizeof(sem_t));
- newFile->lock = malloc(sizeof(pthread_mutex_t));
- sem_init(newFile->sem, 0, 1);
- pthread_mutex_init(newFile->lock, NULL);
- newFile->users = 0;
- newFile->nextFileInQueue = NULL;
- newFile->prevFileInQueue = NULL;
- newFile->nextFileInCache = NULL;
- newFile->prevFileInCache = NULL;
- if(strlen(fileName) == 0) return NULL;
- long index = hash(fileName);
- newFile->hash = index;
- // If they cell is empty, place it at the top
- if(fileCache->files[index] == NULL){
- fileCache->files[index] = newFile;
- }else {
- // Otherwise, navigate down the linked list until you've found the end
- struct file * file = fileCache->files[index];
- while(file->nextFileInCache != NULL){
- file = file->nextFileInCache;
- }
- file->nextFileInCache = newFile;
- newFile->prevFileInCache = file;
- newFile->nextFileInCache = NULL;
- }
- fileCache->size += newFile->fileData->file_size;
- return newFile;
- }
- void removeFileFromCache(struct file * file){
- // First and only
- if(file->prevFileInCache == NULL && file->nextFileInCache == NULL){
- fileCache->files[file->hash] = NULL;
- }
- // First
- else if(file->prevFileInCache == NULL && file->nextFileInCache != NULL){
- fileCache->files[file->hash] = file->nextFileInCache;
- fileCache->files[file->hash]->prevFileInCache = NULL;
- file->nextFileInCache = NULL;
- }
- // Last
- else if(file->prevFileInCache != NULL && file->nextFileInCache == NULL){
- file->prevFileInCache->nextFileInCache = NULL;
- file->prevFileInCache = NULL;
- }
- // Middle
- else {
- file->nextFileInCache->prevFileInCache = file->prevFileInCache;
- file->prevFileInCache->nextFileInCache = file->nextFileInCache;
- file->prevFileInCache = NULL;
- file->nextFileInCache = NULL;
- }
- }
- void cacheEvict(int amountToEvict){
- struct file * fileToEvict;
- int amountEvicted = 0;
- while(amountEvicted < amountToEvict && evictQueue->leastRecent != NULL){
- fileToEvict = evictQueue->leastRecent;
- evictQueue->leastRecent = fileToEvict->nextFileInQueue;
- if(fileToEvict->nextFileInQueue != NULL)
- fileToEvict->nextFileInQueue->prevFileInQueue = NULL;
- removeFileFromCache(fileToEvict);
- fileCache->size -= fileToEvict->fileData->file_size;
- amountEvicted += fileToEvict->fileData->file_size;
- // This semaphore is only released in do_server_request when the last request is done reading the file
- sem_wait(fileToEvict->sem);
- fileToEvict->nextFileInQueue = NULL;
- fileToEvict->prevFileInQueue = NULL;
- free(fileToEvict->sem);
- free(fileToEvict->lock);
- file_data_free(fileToEvict->fileData);
- free(fileToEvict);
- }
- }
- void updateEvictQueue(struct file * file){
- // Last and not only
- if(file->prevFileInQueue != NULL && file->nextFileInQueue == NULL)
- return;
- // First and not only
- if(file->prevFileInQueue == NULL && file->nextFileInQueue != NULL){
- evictQueue->leastRecent = file->nextFileInQueue;
- file->nextFileInQueue->prevFileInQueue = NULL;
- }
- // Middle
- else if(file->prevFileInQueue != NULL && file->nextFileInQueue != NULL){
- file->nextFileInQueue->prevFileInQueue = file->prevFileInQueue;
- file->prevFileInQueue->nextFileInQueue = file->nextFileInQueue;
- }
- file->nextFileInQueue = NULL;
- file->prevFileInQueue = NULL;
- if(evictQueue->leastRecent == NULL){
- evictQueue->leastRecent = file;
- file->nextFileInQueue = NULL;
- file->prevFileInQueue = NULL;
- }else{
- struct file * fileIter = evictQueue->leastRecent;
- while(fileIter->nextFileInQueue != NULL){
- fileIter = fileIter->nextFileInQueue;
- }
- fileIter->nextFileInQueue = file;
- file->prevFileInQueue = fileIter;
- }
- }
- // Read file
- void* handler(void* sv){
- struct server* s = (struct server*)sv;
- while(1){
- pthread_mutex_lock(lock);
- while(in == out){
- pthread_cond_wait(cvEmpty, lock);
- if(s->exiting == 1){
- pthread_mutex_unlock(lock);
- pthread_exit(0);
- }
- }
- int connfd = buffer[out];
- if ((in - out + s->max_requests + 1) % (s->max_requests + 1) == s->max_requests)
- pthread_cond_broadcast(cvFull);
- out = (out + 1)%(s->max_requests + 1);
- pthread_mutex_unlock(lock);
- do_server_request(s, connfd);
- }
- return NULL;
- }
- struct server *
- server_init(int nr_threads, int max_requests, int max_cache_size)
- {
- struct server *sv;
- sv = Malloc(sizeof(struct server));
- sv->nr_threads = nr_threads;
- sv->max_requests = max_requests;
- sv->max_cache_size = max_cache_size;
- sv->exiting = 0;
- if (nr_threads > 0 || max_requests > 0 || max_cache_size > 0) {
- threadPool = malloc(sizeof(pthread_t)* sv->nr_threads);
- in = 0;
- out = 0;
- lock = malloc(sizeof(pthread_mutex_t));
- pthread_mutex_init(lock, NULL);
- fileLock = malloc(sizeof(pthread_mutex_t));
- pthread_mutex_init(fileLock, NULL);
- cvFull = malloc(sizeof(pthread_cond_t));
- pthread_cond_init(cvFull, NULL);
- cvEmpty = malloc(sizeof(pthread_cond_t));
- pthread_cond_init(cvEmpty, NULL);
- if(max_requests > 0){
- buffer = Malloc((max_requests + 1) * sizeof(int));
- for(int i = 0; i < max_requests + 1; i++)
- buffer[i] = -1;
- }
- if(nr_threads > 0){
- for(int i = 0; i < nr_threads; i++)
- pthread_create(&threadPool[i], NULL, handler, (void*)sv);
- }
- if(max_cache_size > 0){
- fileCache = malloc(sizeof(struct fileCache));
- fileCache->size = 0;
- fileCache->maxSize = max_cache_size;
- fileCache->files = malloc(sizeof(struct file *) * ((fileCache->maxSize / 12228) * 2 + 5));
- evictQueue = malloc(sizeof(struct evictQueue));
- evictQueue->leastRecent = NULL;
- evictQueue->mostRecent = NULL;
- }
- }
- /* Lab 4: create queue of max_request size when max_requests > 0 */
- /* Lab 5: init server cache and limit its size to max_cache_size */
- /* Lab 4: create worker threads when nr_threads > 0 */
- return sv;
- }
- // Write file
- void server_request(struct server *sv, int connfd)
- {
- if (sv->nr_threads == 0) { /* no worker threads */
- do_server_request(sv, connfd);
- } else {
- /* Save the relevant info in a buffer and have one of the
- * worker threads do the work. */
- pthread_mutex_lock(lock);
- while((in - out + sv->max_requests + 1)%(sv->max_requests + 1) == sv->max_requests){
- pthread_cond_wait(cvFull, lock);
- }
- buffer[in] = connfd;
- if(in == out){
- pthread_cond_broadcast(cvEmpty);
- }
- in = (in + 1)%(sv->max_requests + 1);
- pthread_mutex_unlock(lock);
- }
- }
- void
- server_exit(struct server *sv)
- {
- /* when using one or more worker threads, use sv->exiting to indicate to
- * these threads that the server is exiting. make sure to call
- * pthread_join in this function so that the main server thread waits
- * for all the worker threads to exit before exiting. */
- sv->exiting = 1;
- pthread_cond_broadcast(cvEmpty);
- for(int i = 0; i< sv->nr_threads; i++)
- pthread_join(threadPool[i], NULL);
- if(sv->max_requests > 0) free(buffer);
- if(sv->nr_threads > 0)free(threadPool);
- if(sv->max_cache_size > 0){
- pthread_mutex_lock(fileLock);
- cacheEvict(sv->max_cache_size);
- pthread_mutex_unlock(fileLock);
- free(fileCache);
- free(evictQueue);
- }
- free(cvEmpty);
- free(cvFull);
- free(lock);
- /* make sure to free any allocated resources */
- free(sv);
- }
- unsigned long hash(char *str){
- int size = (fileCache->maxSize / 12228) * 2 + 5;
- unsigned long hash = 5381;
- int c;
- while((c = *str++))
- hash = ((hash << 5) + hash) + c;
- return hash % (size);
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement