/* 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; }
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; }
/* 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; }
/* 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; } }