static ngx_int_t ngx_buffer_cache_init(ngx_shm_zone_t *shm_zone, void *data) { ngx_buffer_cache_sh_t *sh; ngx_buffer_cache_t *ocache = data; ngx_buffer_cache_t *cache; u_char* p; cache = shm_zone->data; if (ocache) { cache->sh = ocache->sh; cache->shpool = ocache->shpool; return NGX_OK; } cache->shpool = (ngx_slab_pool_t *)shm_zone->shm.addr; if (shm_zone->shm.exists) { cache->sh = cache->shpool->data; return NGX_OK; } // start following the ngx_slab_pool_t that was allocated at the beginning of the chunk p = shm_zone->shm.addr + sizeof(ngx_slab_pool_t); // initialize the log context cache->shpool->log_ctx = p; p = ngx_sprintf(cache->shpool->log_ctx, " in buffer cache \"%V\"%Z", &shm_zone->shm.name); // allocate the shared cache state p = ngx_align_ptr(p, sizeof(void *)); sh = (ngx_buffer_cache_sh_t*)p; p += sizeof(*sh); cache->sh = sh; cache->shpool->data = sh; // initialize fixed cache fields p = ngx_align_ptr(p, sizeof(void *)); sh->entries_start = (ngx_buffer_cache_entry_t*)p; sh->buffers_end = shm_zone->shm.addr + shm_zone->shm.size; sh->access_time = 0; // reset the stats ngx_memzero(&sh->stats, sizeof(sh->stats)); // reset the cache status ngx_buffer_cache_reset(sh); sh->reset = 0; return NGX_OK; }
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; }