void constant_insert(long ins, long get) { long t; int ret; alignhash_t(myhash) *my = alignhash_init(myhash); if (my == NULL) { fprintf(stderr, "alloc failed\n"); return; } for (t = 0; t < ins; t++) { ah_iter_t itr = alignhash_set(myhash, my, myrand(), &ret); if (alignhash_end(my) != itr) alignhash_value(my, itr) = t; counter++; } printf("insertion done\n"); for (t = 0; t < get; t++) { alignhash_get(myhash, my, myrand()); counter++; } alignhash_destroy(myhash, my); printf("all done\n"); }
nxweb_result nxweb_cache_try(nxweb_http_server_connection* conn, nxweb_http_response* resp, const char* key, time_t if_modified_since, time_t revalidated_mtime) { if (*key==' ' || *key=='*') return NXWEB_MISS; // not implemented yet nxe_time_t loop_time=nxweb_get_loop_time(conn); ah_iter_t ci; //nxweb_log_error("trying cache for %s", fpath); pthread_mutex_lock(&_nxweb_cache_mutex); if ((ci=alignhash_get(nxweb_cache, _nxweb_cache, key))!=alignhash_end(_nxweb_cache)) { nxweb_cache_rec* rec=alignhash_value(_nxweb_cache, ci); if (rec->last_modified==revalidated_mtime) { rec->expires_time=loop_time+NXWEB_DEFAULT_CACHED_TIME; nxweb_log_info("revalidated %s in memcache", key); } if (loop_time <= rec->expires_time) { if (rec!=_nxweb_cache_head) { cache_rec_unlink(rec); cache_rec_link(rec); // relink to head } if (if_modified_since && rec->last_modified<=if_modified_since) { pthread_mutex_unlock(&_nxweb_cache_mutex); resp->status_code=304; resp->status="Not Modified"; return NXWEB_OK; } rec->ref_count++; // this must be within mutex-protected section pthread_mutex_unlock(&_nxweb_cache_mutex); resp->content_length=rec->content_length; resp->content=rec->content; resp->content_type=rec->content_type; resp->content_charset=rec->content_charset; resp->last_modified=rec->last_modified; resp->gzip_encoded=rec->gzip_encoded; conn->hsp.req_data=rec; conn->hsp.req_finalize=cache_rec_unref; return NXWEB_OK; } else if (!revalidated_mtime) { pthread_mutex_unlock(&_nxweb_cache_mutex); return NXWEB_REVALIDATE; } alignhash_del(nxweb_cache, _nxweb_cache, ci); cache_rec_unlink(rec); if (!rec->ref_count) { nx_free(rec); } else { // this is normal; just notification nxweb_log_error("removed %s [%p] from cache while its ref_count=%d", key, rec, rec->ref_count); } } pthread_mutex_unlock(&_nxweb_cache_mutex); return NXWEB_MISS; }
static void cache_finalize() { ah_iter_t ci; for (ci=alignhash_begin(_nxweb_cache); ci!=alignhash_end(_nxweb_cache); ci++) { if (alignhash_exist(_nxweb_cache, ci)) { nxweb_cache_rec* rec=alignhash_value(_nxweb_cache, ci); if (rec->ref_count) nxweb_log_error("file %s still in cache with ref_count=%d", alignhash_key(_nxweb_cache, ci), rec->ref_count); cache_rec_unlink(rec); nx_free(rec); } } alignhash_destroy(nxweb_cache, _nxweb_cache); pthread_mutex_destroy(&_nxweb_cache_mutex); }
static inline void cache_check_size() { while (alignhash_size(_nxweb_cache)>NXWEB_MAX_CACHED_ITEMS) { nxweb_cache_rec* rec=_nxweb_cache_tail; while (rec && rec->ref_count) rec=rec->prev; if (rec) { const char* fpath=rec->content+rec->content_length+1; ah_iter_t ci=alignhash_get(nxweb_cache, _nxweb_cache, fpath); if (ci!=alignhash_end(_nxweb_cache)) { assert(rec==alignhash_value(_nxweb_cache, ci)); cache_rec_unlink(rec); alignhash_del(nxweb_cache, _nxweb_cache, ci); nx_free(rec); } } else { break; } } }
static void cache_rec_unref(nxd_http_server_proto* hsp, void* req_data) { pthread_mutex_lock(&_nxweb_cache_mutex); nxweb_cache_rec* rec=req_data; assert(rec->ref_count>0); if (!--rec->ref_count) { nxe_time_t loop_time=_nxweb_net_thread_data->loop->current_time; if (loop_time > rec->expires_time || !IS_LINKED(rec)) { const char* fpath=rec->content+rec->content_length+1; ah_iter_t ci=alignhash_get(nxweb_cache, _nxweb_cache, fpath); if (ci!=alignhash_end(_nxweb_cache) && rec==alignhash_value(_nxweb_cache, ci)) { alignhash_del(nxweb_cache, _nxweb_cache, ci); cache_rec_unlink(rec); } else { nxweb_log_error("freed previously removed cache entry %s [%p]", fpath, rec); } nx_free(rec); } } cache_check_size(); pthread_mutex_unlock(&_nxweb_cache_mutex); }
nxweb_result nxweb_cache_store_response(nxweb_http_server_connection* conn, nxweb_http_response* resp) { nxe_time_t loop_time=nxweb_get_loop_time(conn); if (!resp->status_code) resp->status_code=200; if (resp->status_code==200 && resp->sendfile_path // only cache content from files && resp->content_length>=0 && resp->content_length<=NXWEB_MAX_CACHED_ITEM_SIZE // must be small && resp->sendfile_offset==0 && resp->sendfile_end==resp->content_length // whole file only && resp->sendfile_end>=resp->sendfile_info.st_size // st_size could be zero if not initialized && alignhash_size(_nxweb_cache)<NXWEB_MAX_CACHED_ITEMS+16) { const char* fpath=resp->sendfile_path; const char* key=resp->cache_key; if (nxweb_cache_try(conn, resp, key, 0, resp->last_modified)!=NXWEB_MISS) return NXWEB_OK; nxweb_cache_rec* rec=nx_calloc(sizeof(nxweb_cache_rec)+resp->content_length+1+strlen(key)+1); rec->expires_time=loop_time+NXWEB_DEFAULT_CACHED_TIME; rec->last_modified=resp->last_modified; rec->content_type=resp->content_type; // assume content_type and content_charset come rec->content_charset=resp->content_charset; // from statically allocated memory, which won't go away rec->content_length=resp->content_length; rec->gzip_encoded=resp->gzip_encoded; char* ptr=((char*)rec)+offsetof(nxweb_cache_rec, content); int fd; if ((fd=open(fpath, O_RDONLY))<0 || read(fd, ptr, resp->content_length)!=resp->content_length) { if (fd>0) close(fd); nx_free(rec); nxweb_log_error("nxweb_cache_file_store_and_send(): [%s] stat() was OK, but open/read() failed", fpath); nxweb_send_http_error(resp, 500, "Internal Server Error"); return NXWEB_ERROR; } close(fd); resp->content=ptr; ptr+=resp->content_length; *ptr++='\0'; strcpy(ptr, key); key=ptr; int ret=0; ah_iter_t ci; pthread_mutex_lock(&_nxweb_cache_mutex); ci=alignhash_set(nxweb_cache, _nxweb_cache, key, &ret); if (ci!=alignhash_end(_nxweb_cache)) { if (ret!=AH_INS_ERR) { alignhash_value(_nxweb_cache, ci)=rec; cache_rec_link(rec); rec->ref_count++; cache_check_size(); pthread_mutex_unlock(&_nxweb_cache_mutex); nxweb_log_info("memcached %s", key); conn->hsp.req_data=rec; assert(!conn->hsp.req_finalize); conn->hsp.req_finalize=cache_rec_unref; //nxweb_start_sending_response(conn, resp); return NXWEB_OK; } else { // AH_INS_ERR => key already exists (added by other thread) nx_free(rec); rec=alignhash_value(_nxweb_cache, ci); resp->content_length=rec->content_length; resp->content=rec->content; resp->content_type=rec->content_type; resp->content_charset=rec->content_charset; resp->last_modified=rec->last_modified; resp->gzip_encoded=rec->gzip_encoded; rec->ref_count++; pthread_mutex_unlock(&_nxweb_cache_mutex); conn->hsp.req_data=rec; assert(!conn->hsp.req_finalize); conn->hsp.req_finalize=cache_rec_unref; //nxweb_start_sending_response(conn, resp); return NXWEB_OK; } } pthread_mutex_unlock(&_nxweb_cache_mutex); nx_free(rec); } return NXWEB_OK; }