📄 mod_mem_cache.c
字号:
/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *//* * Rules for managing obj->refcount: * refcount should be incremented when an object is placed in the cache. Insertion * of an object into the cache and the refcount increment should happen under * protection of the sconf->lock. * * refcount should be decremented when the object is removed from the cache. * Object should be removed from the cache and the refcount decremented while * under protection of the sconf->lock. * * refcount should be incremented when an object is retrieved from the cache * by a worker thread. The retrieval/find operation and refcount increment * should occur under protection of the sconf->lock * * refcount can be atomically decremented w/o protection of the sconf->lock * by worker threads. * * Any object whose refcount drops to 0 should be freed/cleaned up. A refcount * of 0 means the object is not in the cache and no worker threads are accessing * it. */#define CORE_PRIVATE#include "mod_cache.h"#include "cache_pqueue.h"#include "cache_cache.h"#include "ap_provider.h"#include "ap_mpm.h"#include "apr_thread_mutex.h"#if APR_HAVE_UNISTD_H#include <unistd.h>#endif#if !APR_HAS_THREADS#error This module does not currently compile unless you have a thread-capable APR. Sorry!#endifmodule AP_MODULE_DECLARE_DATA mem_cache_module;typedef enum { CACHE_TYPE_FILE = 1, CACHE_TYPE_HEAP, CACHE_TYPE_MMAP} cache_type_e;typedef struct mem_cache_object { apr_pool_t *pool; cache_type_e type; apr_table_t *header_out; apr_table_t *req_hdrs; /* for Vary negotiation */ apr_size_t m_len; void *m; apr_os_file_t fd; apr_int32_t flags; /* File open flags */ long priority; /**< the priority of this entry */ long total_refs; /**< total number of references this entry has had */ apr_uint32_t pos; /**< the position of this entry in the cache */} mem_cache_object_t;typedef struct { apr_thread_mutex_t *lock; cache_cache_t *cache_cache; /* Fields set by config directives */ apr_size_t min_cache_object_size; /* in bytes */ apr_size_t max_cache_object_size; /* in bytes */ apr_size_t max_cache_size; /* in bytes */ apr_size_t max_object_cnt; cache_pqueue_set_priority cache_remove_algorithm; /* maximum amount of data to buffer on a streamed response where * we haven't yet seen EOS */ apr_off_t max_streaming_buffer_size;} mem_cache_conf;static mem_cache_conf *sconf;#define DEFAULT_MAX_CACHE_SIZE 100*1024#define DEFAULT_MIN_CACHE_OBJECT_SIZE 1#define DEFAULT_MAX_CACHE_OBJECT_SIZE 10000#define DEFAULT_MAX_OBJECT_CNT 1009#define DEFAULT_MAX_STREAMING_BUFFER_SIZE 100000#define CACHEFILE_LEN 20/* Forward declarations */static int remove_entity(cache_handle_t *h);static apr_status_t store_headers(cache_handle_t *h, request_rec *r, cache_info *i);static apr_status_t store_body(cache_handle_t *h, request_rec *r, apr_bucket_brigade *b);static apr_status_t recall_headers(cache_handle_t *h, request_rec *r);static apr_status_t recall_body(cache_handle_t *h, apr_pool_t *p, apr_bucket_brigade *bb);static void cleanup_cache_object(cache_object_t *obj);static long memcache_get_priority(void*a){ cache_object_t *obj = (cache_object_t *)a; mem_cache_object_t *mobj = obj->vobj; return mobj->priority;}static void memcache_inc_frequency(void*a){ cache_object_t *obj = (cache_object_t *)a; mem_cache_object_t *mobj = obj->vobj; mobj->total_refs++; mobj->priority = 0;}static void memcache_set_pos(void *a, apr_ssize_t pos){ cache_object_t *obj = (cache_object_t *)a; mem_cache_object_t *mobj = obj->vobj; apr_atomic_set32(&mobj->pos, pos);}static apr_ssize_t memcache_get_pos(void *a){ cache_object_t *obj = (cache_object_t *)a; mem_cache_object_t *mobj = obj->vobj; return apr_atomic_read32(&mobj->pos);}static apr_size_t memcache_cache_get_size(void*a){ cache_object_t *obj = (cache_object_t *)a; mem_cache_object_t *mobj = obj->vobj; return mobj->m_len;}/** callback to get the key of a item */static const char* memcache_cache_get_key(void*a){ cache_object_t *obj = (cache_object_t *)a; return obj->key;}/** * memcache_cache_free() * memcache_cache_free is a callback that is only invoked by a thread * running in cache_insert(). cache_insert() runs under protection * of sconf->lock. By the time this function has been entered, the cache_object * has been ejected from the cache. decrement the refcount and if the refcount drops * to 0, cleanup the cache object. */static void memcache_cache_free(void*a){ cache_object_t *obj = (cache_object_t *)a; /* Decrement the refcount to account for the object being ejected * from the cache. If the refcount is 0, free the object. */ if (!apr_atomic_dec32(&obj->refcount)) { cleanup_cache_object(obj); }}/* * functions return a 'negative' score since priority queues * dequeue the object with the highest value first */static long memcache_lru_algorithm(long queue_clock, void *a){ cache_object_t *obj = (cache_object_t *)a; mem_cache_object_t *mobj = obj->vobj; if (mobj->priority == 0) mobj->priority = queue_clock - mobj->total_refs; /* * a 'proper' LRU function would just be * mobj->priority = mobj->total_refs; */ return mobj->priority;}static long memcache_gdsf_algorithm(long queue_clock, void *a){ cache_object_t *obj = (cache_object_t *)a; mem_cache_object_t *mobj = obj->vobj; if (mobj->priority == 0) mobj->priority = queue_clock - (long)(mobj->total_refs*1000 / mobj->m_len); return mobj->priority;}static void cleanup_cache_object(cache_object_t *obj){ mem_cache_object_t *mobj = obj->vobj; /* Cleanup the mem_cache_object_t */ if (mobj) { if (mobj->m) { free(mobj->m); } if (mobj->type == CACHE_TYPE_FILE && mobj->fd) {#ifdef WIN32 CloseHandle(mobj->fd);#else close(mobj->fd);#endif } } apr_pool_destroy(mobj->pool);}static apr_status_t decrement_refcount(void *arg){ cache_object_t *obj = (cache_object_t *) arg; /* If obj->complete is not set, the cache update failed and the * object needs to be removed from the cache then cleaned up. * The garbage collector may have ejected the object from the * cache already, so make sure it is really still in the cache * before attempting to remove it. */ if (!obj->complete) { cache_object_t *tobj = NULL; if (sconf->lock) { apr_thread_mutex_lock(sconf->lock); } tobj = cache_find(sconf->cache_cache, obj->key); if (tobj == obj) { cache_remove(sconf->cache_cache, obj); apr_atomic_dec32(&obj->refcount); } if (sconf->lock) { apr_thread_mutex_unlock(sconf->lock); } } /* If the refcount drops to 0, cleanup the cache object */ if (!apr_atomic_dec32(&obj->refcount)) { cleanup_cache_object(obj); } return APR_SUCCESS;}static apr_status_t cleanup_cache_mem(void *sconfv){ cache_object_t *obj; mem_cache_conf *co = (mem_cache_conf*) sconfv; if (!co) { return APR_SUCCESS; } if (!co->cache_cache) { return APR_SUCCESS; } if (sconf->lock) { apr_thread_mutex_lock(sconf->lock); } obj = cache_pop(co->cache_cache); while (obj) { /* Iterate over the cache and clean up each unreferenced entry */ if (!apr_atomic_dec32(&obj->refcount)) { cleanup_cache_object(obj); } obj = cache_pop(co->cache_cache); } /* Cache is empty, free the cache table */ cache_free(co->cache_cache); if (sconf->lock) { apr_thread_mutex_unlock(sconf->lock); } return APR_SUCCESS;}/* * TODO: enable directives to be overridden in various containers */static void *create_cache_config(apr_pool_t *p, server_rec *s){ sconf = apr_pcalloc(p, sizeof(mem_cache_conf)); sconf->min_cache_object_size = DEFAULT_MIN_CACHE_OBJECT_SIZE; sconf->max_cache_object_size = DEFAULT_MAX_CACHE_OBJECT_SIZE; /* Number of objects in the cache */ sconf->max_object_cnt = DEFAULT_MAX_OBJECT_CNT; /* Size of the cache in bytes */ sconf->max_cache_size = DEFAULT_MAX_CACHE_SIZE; sconf->cache_cache = NULL; sconf->cache_remove_algorithm = memcache_gdsf_algorithm; sconf->max_streaming_buffer_size = DEFAULT_MAX_STREAMING_BUFFER_SIZE; return sconf;}static int create_entity(cache_handle_t *h, cache_type_e type_e, request_rec *r, const char *key, apr_off_t len){ apr_status_t rv; apr_pool_t *pool; cache_object_t *obj, *tmp_obj; mem_cache_object_t *mobj; if (len == -1) { /* Caching a streaming response. Assume the response is * less than or equal to max_streaming_buffer_size. We will * correct all the cache size counters in store_body once * we know exactly know how much we are caching. */ len = sconf->max_streaming_buffer_size; } /* Note: cache_insert() will automatically garbage collect * objects from the cache if the max_cache_size threshold is * exceeded. This means mod_mem_cache does not need to implement * max_cache_size checks. */ if (len < sconf->min_cache_object_size || len > sconf->max_cache_object_size) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "mem_cache: URL %s failed the size check and will not be cached.", key); return DECLINED; } if (type_e == CACHE_TYPE_FILE) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -