void ngx_buffer_cache_release( ngx_buffer_cache_t* cache, u_char* key, uint32_t token) { ngx_buffer_cache_entry_t* entry; ngx_buffer_cache_sh_t *sh = cache->sh; uint32_t hash; hash = ngx_crc32_short(key, BUFFER_CACHE_KEY_SIZE); ngx_shmtx_lock(&cache->shpool->mutex); if (!sh->reset) { entry = ngx_buffer_cache_rbtree_lookup(&sh->rbtree, key, hash); if (entry != NULL && entry->state == CES_READY && (uint32_t)entry->write_time == token) { (void)ngx_atomic_fetch_add(&entry->ref_count, -1); } } ngx_shmtx_unlock(&cache->shpool->mutex); }
ngx_flag_t ngx_buffer_cache_fetch( ngx_buffer_cache_t* cache, u_char* key, ngx_str_t* buffer, uint32_t* token) { ngx_buffer_cache_entry_t* entry; ngx_buffer_cache_sh_t *sh = cache->sh; ngx_flag_t result = 0; uint32_t hash; hash = ngx_crc32_short(key, BUFFER_CACHE_KEY_SIZE); ngx_shmtx_lock(&cache->shpool->mutex); if (!sh->reset) { entry = ngx_buffer_cache_rbtree_lookup(&sh->rbtree, key, hash); if (entry != NULL && entry->state == CES_READY && (cache->expiration == 0 || ngx_time() < (time_t)(entry->write_time + cache->expiration))) { result = 1; // update stats sh->stats.fetch_hit++; sh->stats.fetch_bytes += entry->buffer_size; // copy buffer pointer and size buffer->data = entry->start_offset; buffer->len = entry->buffer_size; *token = entry->write_time; // Note: setting the access time of the entry and cache to prevent it // from being freed while the caller uses the buffer sh->access_time = entry->access_time = ngx_time(); (void)ngx_atomic_fetch_add(&entry->ref_count, 1); } else { // update stats sh->stats.fetch_miss++; } } ngx_shmtx_unlock(&cache->shpool->mutex); return result; }
ngx_flag_t ngx_buffer_cache_store_gather( ngx_buffer_cache_t* cache, u_char* key, ngx_str_t* buffers, size_t buffer_count) { ngx_buffer_cache_entry_t* entry; ngx_buffer_cache_sh_t *sh = cache->sh; ngx_str_t* cur_buffer; ngx_str_t* last_buffer; size_t buffer_size; uint32_t hash; uint32_t evictions; u_char* target_buffer; hash = ngx_crc32_short(key, BUFFER_CACHE_KEY_SIZE); ngx_shmtx_lock(&cache->shpool->mutex); if (sh->reset) { // a previous store operation was killed in progress, need to reset the cache // since the data structures may be corrupt. we can only reset the cache after // the access time expires since other processes may still be reading from / // writing to the cache if (ngx_time() < sh->access_time + CACHE_LOCK_EXPIRATION) { ngx_shmtx_unlock(&cache->shpool->mutex); return 0; } // reset the cache, leave the reset flag enabled ngx_buffer_cache_reset(sh); // update stats sh->stats.reset++; } else { // remove expired entries if (cache->expiration) { for (evictions = MAX_EVICTIONS_PER_STORE; evictions > 0; evictions--) { if (!ngx_buffer_cache_free_oldest_entry(sh, cache->expiration)) { break; } } } // make sure the entry does not already exist entry = ngx_buffer_cache_rbtree_lookup(&sh->rbtree, key, hash); if (entry != NULL) { sh->stats.store_exists++; ngx_shmtx_unlock(&cache->shpool->mutex); return 0; } // enable the reset flag before we start making any changes sh->reset = 1; } // allocate a new entry entry = ngx_buffer_cache_get_free_entry(sh); if (entry == NULL) { goto error; } // calculate the buffer size last_buffer = buffers + buffer_count; buffer_size = 0; for (cur_buffer = buffers; cur_buffer < last_buffer; cur_buffer++) { buffer_size += cur_buffer->len; } // allocate a buffer to hold the data target_buffer = ngx_buffer_cache_get_free_buffer(sh, buffer_size); if (target_buffer == NULL) { goto error; } // initialize the entry entry->state = CES_ALLOCATED; entry->node.key = hash; memcpy(entry->key, key, BUFFER_CACHE_KEY_SIZE); entry->start_offset = target_buffer; entry->buffer_size = buffer_size; // update the write position sh->buffers_write = target_buffer; // move from free_queue to used_queue ngx_queue_remove(&entry->queue_node); ngx_queue_insert_tail(&sh->used_queue, &entry->queue_node); // insert to rbtree ngx_rbtree_insert(&sh->rbtree, &entry->node); // update stats sh->stats.store_ok++; sh->stats.store_bytes += buffer_size; // Note: the memcpy is performed after releasing the lock to avoid holding the lock for a long time // setting the access time of the entry and cache prevents it from being freed sh->access_time = entry->access_time = ngx_time(); entry->write_time = ngx_time(); sh->reset = 0; ngx_shmtx_unlock(&cache->shpool->mutex); for (cur_buffer = buffers; cur_buffer < last_buffer; cur_buffer++) { target_buffer = ngx_copy(target_buffer, cur_buffer->data, cur_buffer->len); } // Note: no need to obtain the lock since state is ngx_atomic_t entry->state = CES_READY; return 1; error: sh->stats.store_err++; sh->reset = 0; ngx_shmtx_unlock(&cache->shpool->mutex); return 0; }