void file_data_free(struct file *file, unsigned char *data) { if (file->begin) { if (data == file->begin) return; if (data >= file->begin && data < file->end) return; } if (file->cache && data) { cache_entry_destroy(file_cache, data); } else g_free(data); }
static void * cache_maintain(void * arg) { /* This code runs as a thread and is responsible for maintaining the * cache. Every 10 seconds it scans 1/6 of the cache, so that in a * minute it will scan the entire cache. It attempts to refresh cache * entries which have expired, but only up to 5 times if they have not * been used in the interim. After that they are removed. */ int entries = HASH_SIZE / 6; int position = 0; for(;;) { int i; time_t now; struct timespec delay; delay.tv_sec = 10; delay.tv_nsec = 0; while(nanosleep(&delay, &delay) < 0 && errno == EINTR) if(debug) printf("Resuming interrupted sleep!\n"); pthread_mutex_lock(&cache_mutex); if(debug) printf("Look over 1/6 of cache...\n"); now = time(NULL); for(i = 0; i < entries; i++) { /* Since we'll potentially be removing entries from the * linked list, we keep a pointer to the previous * element's link to us so that we can update it and use * that to get to the next element if we remove one. */ struct cache_entry ** point = &hash_table[position]; struct cache_entry * scan; while((scan = *point)) { /* GET*ENT entries do not get refreshed here */ if(scan->refreshes == 5 || (now > scan->expire_time && (scan->type == GETPWENT || scan->type == GETGRENT))) /* kill it */ cache_entry_destroy(scan); else if(now > scan->expire_time) { request_header req = {version: NSCD_VERSION, type: scan->type, key_len: scan->key_len}; int r; void * reply; int32_t reply_len; time_t refresh_interval; /* refresh it */ if(debug) printf("Refreshing cache entry for [%s], refreshes %d\n", (char *) scan->key, scan->refreshes); pthread_mutex_unlock(&cache_mutex); r = generate_reply(&req, scan->key, -1, &reply, &reply_len, &refresh_interval); pthread_mutex_lock(&cache_mutex); now = time(NULL); /* while we were refreshing it, it may have * been marked stale and a new copy fetched */ if(scan->refreshes == 5 || r < 0) { /* kill it */ cache_entry_destroy(scan); if(r >= 0) free(reply); } else { free(scan->reply); scan->reply = reply; scan->reply_len = reply_len; scan->expire_time += refresh_interval; scan->refresh_interval = refresh_interval; scan->refreshes++; /* go to next entry */ point = &scan->chain; } } else /* go to next entry */ point = &scan->chain; } if(++position == HASH_SIZE) position = 0; }
int main (int argc, char *argv[]) { struct cache *cache; struct cache_entry *e1, *e2; json_object *o1; json_object *o2; wait_t *w; int count, i; plan (NO_PLAN); cache_destroy (NULL); cache_entry_destroy (NULL); diag ("cache_destroy and cache_entry_destroy accept NULL arg"); ok ((cache = cache_create ()) != NULL, "cache_create works"); ok (cache_count_entries (cache) == 0, "cache contains 0 entries"); cache_destroy (cache); /* Play with one entry. * N.B.: json ref is NOT incremented by create or get_json. */ o1 = Jnew (); Jadd_int (o1, "foo", 42); ok ((e1 = cache_entry_create (o1)) != NULL, "cache_entry_create works"); ok (cache_entry_get_valid (e1) == true, "cache entry initially valid"); ok (cache_entry_get_dirty (e1) == false, "cache entry initially not dirty"); cache_entry_set_dirty (e1, true); ok (cache_entry_get_dirty (e1) == true, "cache entry succcessfully set dirty"); ok ((o2 = cache_entry_get_json (e1)) != NULL, "json retrieved from cache entry"); ok (Jget_int (o2, "foo", &i) == true && i == 42, "expected json object found"); cache_entry_destroy (e1); /* destroys o1 */ /* Test cache entry waiters. * N.B. waiter is destroyed when run. */ count = 0; ok ((w = wait_create (wait_cb, &count)) != NULL, "wait_create works"); ok ((e1 = cache_entry_create (NULL)) != NULL, "cache_entry_create created empty object"); ok (cache_entry_get_valid (e1) == false, "cache entry invalid, adding waiter"); o1 = Jnew (); Jadd_int (o1, "foo", 42); cache_entry_wait_valid (e1, w); cache_entry_set_json (e1, o1); ok (cache_entry_get_valid (e1) == true, "cache entry set valid with one waiter"); ok (count == 1, "waiter callback ran"); count = 0; ok ((w = wait_create (wait_cb, &count)) != NULL, "wait_create works"); cache_entry_set_dirty (e1, true); ok (cache_entry_get_dirty (e1) == true, "cache entry set dirty, adding waiter"); cache_entry_wait_notdirty (e1, w); cache_entry_set_dirty (e1, false); ok (cache_entry_get_dirty (e1) == false, "cache entry set not dirty with one waiter"); ok (count == 1, "waiter callback ran"); cache_entry_destroy (e1); /* destroys o1 */ /* Put entry in cache and test lookup, expire */ ok ((cache = cache_create ()) != NULL, "cache_create works"); ok (cache_count_entries (cache) == 0, "cache contains 0 entries"); o1 = Jnew (); Jadd_int (o1, "foo", 42); ok ((e1 = cache_entry_create (o1)) != NULL, "cache_entry_create works"); cache_insert (cache, "xxx1", e1); ok (cache_count_entries (cache) == 1, "cache contains 1 entry after insert"); ok (cache_lookup (cache, "yyy1", 0) == NULL, "cache_lookup of wrong hash fails"); ok ((e2 = cache_lookup (cache, "xxx1", 42)) != NULL, "cache_lookup of correct hash works (last use=42)"); i = 0; ok ((o2 = cache_entry_get_json (e2)) != NULL && Jget_int (o2, "foo", &i) == true && i == 42, "expected json object found"); ok (cache_count_entries (cache) == 1, "cache contains 1 entry"); ok (cache_expire_entries (cache, 43, 1) == 0, "cache_expire_entries now=43 thresh=1 expired 0"); ok (cache_count_entries (cache) == 1, "cache contains 1 entry"); ok (cache_expire_entries (cache, 44, 1) == 1, "cache_expire_entries now=44 thresh=1 expired 1"); ok (cache_count_entries (cache) == 0, "cache contains 0 entries"); cache_destroy (cache); done_testing (); return (0); }