/* search_cell searches the cache and returns the cached * cell if found and returns NULL if the request cell is * not cached. request_line is treated as the key. The * strategy is to lock the cache for reading. reading from * the cache is permitted but writing to the cache is not. * In the hit case, the cache is not unlocked until data is * writen to client. */ struct cache_cell *search_cell(char *request_line) { struct cache_cell *ptr; /* locks the cache for reading */ Pthread_rwlock_rdlock(&cache_lock); for (ptr = head; ptr != NULL; ptr = ptr->next) { if (!strcmp(ptr->request_line, request_line)) { /* locks the mutex in the cell so that * other threads cannot access this cell. */ Pthread_rwlock_wrlock(&(ptr->lock)); Pthread_mutex_lock(&time_mutex); ptr->last_use = cache_time; cache_time++; Pthread_mutex_unlock(&time_mutex); Pthread_rwlock_unlock(&(ptr->lock)); return ptr; } } /* unlocks the cache immediately in miss case */ Pthread_rwlock_unlock(&cache_lock); return NULL; }
/* search_cell_variant searches the cache but does not update*/ int search_cell_variant(char *request_line) { struct cache_cell *ptr; Pthread_rwlock_rdlock(&cache_lock); for (ptr = head; ptr != NULL; ptr = ptr->next) if (!strcmp(ptr->request_line, request_line)) { Pthread_rwlock_unlock(&cache_lock); return 1; } Pthread_rwlock_unlock(&cache_lock); return 0; }
/* Add a new cache item into the cache */ void add_cache_item(Cache_List *cache_list, char *uri, char *content, unsigned int size) { if (DEBUG_MODE) printf(" add_cache_item():\n"); Cache_Item *cache_item = build_cache_item(uri, content, size); /* Abort caching if build_cache_item failed */ if (cache_item == NULL) { if (DEBUG_MODE) printf(" add_cache_item() failed.\n"); return; } Pthread_rwlock_wrlock(&cache_rwlock); /* Lock for exlusive writing */ /* Writing block */ while (cache_list->unused_size < size) { evict_cache_item(cache_list); } insert_item_to_listhead(cache_list, cache_item); printf("\tResponse content (%u bytes) for URI: %s has been cached.\n", size, uri); print_cache_status(cache_list); /* End of writing block */ Pthread_rwlock_unlock(&cache_rwlock); /* Unlock writing */ if (DEBUG_MODE) printf(" add_cache_item() finish.\n"); }
/* hit_handler handles hit case. It retrieves data from * cache and writes data to client. */ void hit_handler(int clientfd, struct cache_cell *cell, struct client_request *request) { if (rio_writen(clientfd, cell->content, cell->size) < 0) close_connection(request, clientfd, -1); Pthread_rwlock_unlock(&cache_lock); close_connection(request, clientfd, -1); }
/* Search the cache, if hit, copy content to the user buffer */ int search_and_get(Cache_List *cache_list, char *for_uri, void *usrbuf, unsigned int *size) { Cache_Item *cache_item = NULL; Pthread_rwlock_rdlock(&cache_rwlock); /* Lock for concurrent reading */ cache_item = search_cache_item(cache_list, for_uri); /* Reading */ Pthread_rwlock_unlock(&cache_rwlock); /* Unlocked reading */ /* During this small transition period after releasing the reading lock * but before acquiring the writing lock, it is possible that the cache * item just found would be evicted by another writter who has been * waiting in the que. So it needs to be double checked before using. If * it is evicted, just treat it as a cache-miss. */ if (cache_item != NULL) { Pthread_rwlock_wrlock(&cache_rwlock); /* Lock for exlusive writing */ /* Check the item again in case it has been evicted instantaneously */ if (strcmp(cache_item->uri, for_uri) == 0) { /* If it is still there, treat it as a cache-hit and use it */ use_cache_item(cache_list, cache_item, usrbuf, size); print_cache_status(cache_list); Pthread_rwlock_unlock(&cache_rwlock); /* Unlock writing */ return 0; } else { /* If it is lost, treated it as a cache-miss */ Pthread_rwlock_unlock(&cache_rwlock); /* Unlock writing */ return -1; } } else { /* Cache-miss */ return -1; } }
/* add_to_list takes a cell and adds that cell the * the cache list. It locks the cache for writing so * that other threads cannot modify it. */ void add_to_list(struct cache_cell *cell) { /* locks the cache for writing */ Pthread_rwlock_wrlock(&cache_lock); /* if there is enough space in the cache, * no eviction is needed. */ if (cache_size + cell->size <= MAX_CACHE_SIZE) { cell->next = head; if (head != NULL) head->previous = cell; head = cell; cache_size += cell->size; cell->last_use = cache_time; Pthread_mutex_lock(&time_mutex); cache_time++; Pthread_mutex_unlock(&time_mutex); } /* if there is not enough space in the cache, * eviction is needed. */ else { struct cache_cell *tmp_cell, *ptr; int tmp_last_use; /* remove elements from cache so that there is enough * space in the cache. */ while (!(cache_size + cell->size <= MAX_CACHE_SIZE)) { tmp_last_use = cache_time + 1; for (ptr = head; ptr != NULL; ptr = ptr->next) if (ptr->last_use < tmp_last_use) { tmp_last_use = ptr->last_use; tmp_cell = ptr; } remove_from_list(tmp_cell); } /* add cell to cache */ cell->next = head; if (head != NULL) head->previous = cell; head = cell; cache_size += cell->size; cell->last_use = cache_time; Pthread_mutex_lock(&time_mutex); cache_time++; Pthread_mutex_unlock(&time_mutex); } Pthread_rwlock_unlock(&cache_lock); return; }
/* include incr */ void * incr(void *arg) { int i; for (i = 0; i < nloop; i++) { Pthread_rwlock_wrlock(&shared.rwlock); shared.counter++; Pthread_rwlock_unlock(&shared.rwlock); } return(NULL); }
void *thread1(void *arg) { sleep(1); printf("%s: first child tries to obtain write lock\n", gf_time()); Pthread_rwlock_wrlock(&rwlock); /* this should block */ printf("%s: first child obtains write lock\n", gf_time()); sleep(2); Pthread_rwlock_unlock(&rwlock); printf("%s: first child releases write lock\n", gf_time()); return NULL; }
void *thread2(void *arg) { /* 4second child */ sleep(3); printf("%s: second child tries to obtain read lock\n", gf_time()); Pthread_rwlock_rdlock(&rwlock); printf("%s: second child obtains read lock\n", gf_time()); sleep(4); Pthread_rwlock_unlock(&rwlock); printf("%s: second child releases read lock\n", gf_time()); return NULL; }
int main() { pthread_t tid1, tid2; Pthread_rwlock_wrlock(&rwlock); /* parent read locks entire file */ printf("%s: parent has write lock\n", gf_time()); Pthread_create(&tid1, NULL, thread1, NULL); Pthread_create(&tid2, NULL, thread2, NULL); /* 4parent */ sleep(5); Pthread_rwlock_unlock(&rwlock); printf("%s: parent releases write lock\n", gf_time()); Pthread_join(tid1, NULL); Pthread_join(tid2, NULL); exit(0); }
int main(int argc, char **argv) { int i, nthreads; pthread_t tid[MAXNTHREADS]; if (argc != 3) { fprintf(stderr, "usage: incr_rwlock1 <#loops> <#threads>"); exit(EXIT_FAILURE); } nloop = atoi(argv[1]); nthreads = min(atoi(argv[2]), MAXNTHREADS); /* obtain write lock */ Pthread_rwlock_wrlock(&shared.rwlock); /* create all the threads */ //Set_concurrency(nthreads); for (i = 0; i < nthreads; i++) { pthread_create(&tid[i], NULL, incr, NULL); } /* start the timer and release the write lock */ Start_time(); // 写锁释放之后,线程才可以运行 Pthread_rwlock_unlock(&shared.rwlock); /* wait for all the threads */ for (i = 0; i < nthreads; i++) { pthread_join(tid[i], NULL); } printf("microseconds: %.0f usec\n", Stop_time()); if (shared.counter != nloop * nthreads) printf("error: counter = %ld\n", shared.counter); else printf("success: counter = %ld\n", shared.counter); exit(0); }