mod_mem_cache.c revision aa202cda00837ed5381d5f67254e08c565a5c3a8
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000-2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
*
* Portions of this software are based upon public domain software
* originally written at the National Center for Supercomputing Applications,
* University of Illinois, Urbana-Champaign.
*/
#define CORE_PRIVATE
/* CACHE_FD will eventually be exposed as a configuration directive */
#define CACHE_FD 0
#include "mod_cache.h"
#include "ap_mpm.h"
#include "apr_thread_mutex.h"
/* USE_ATOMICS should be replaced with the appropriate APR feature macro */
#define USE_ATOMICS
#ifdef USE_ATOMICS
#include "apr_atomic.h"
#endif
#if !APR_HAS_THREADS
#endif
/*
* XXX
* This cache uses apr_hash functions which leak storage when something is removed
* from the cache. This can be fixed in the apr_hash functions by making them use
*/
typedef enum {
CACHE_TYPE_FILE = 1,
} cache_type_e;
typedef struct {
char* hdr;
char* val;
typedef struct mem_cache_object {
void *m;
typedef struct {
/* Fields set by config directives */
static mem_cache_conf *sconf;
#define DEFAULT_MIN_CACHE_OBJECT_SIZE 0
#define DEFAULT_MAX_CACHE_OBJECT_SIZE 10000
#define DEFAULT_MAX_OBJECT_CNT 1000
#define CACHEFILE_LEN 20
/* Forward declarations */
static int remove_entity(cache_handle_t *h);
{
/* TODO:
* We desperately need a more efficient way of allocating objects. We're
* making way too many malloc calls to create a fully populated
* cache object...
*/
/* Cleanup the cache_object_t */
}
}
}
}
}
/* Cleanup the mem_cache_object_t */
if (mobj) {
}
#ifdef WIN32
#else
#endif
}
if (mobj->header_out) {
}
if (mobj->subprocess_env) {
}
}
}
return;
}
{
#ifdef USE_ATOMICS
}
}
#else
}
/* If the object is marked for cleanup and the refcount
* has dropped to zero, cleanup the object
*/
}
}
#endif
return APR_SUCCESS;
}
{
if (!co) {
return APR_SUCCESS;
}
/* Iterate over the cache and clean up each entry */
}
if (obj) {
#ifdef USE_ATOMICS
#else
#endif
}
}
}
}
return APR_SUCCESS;
}
/*
* TODO: enable directives to be overridden in various containers
*/
{
int threaded_mpm;
if (threaded_mpm) {
}
/* Number of objects in the cache */
sconf->object_cnt = 0;
/* Size of the cache in KB */
sconf->cache_size = 0;
return sconf;
}
const char *type,
const char *key,
{
return DECLINED;
}
return DECLINED;
}
/*
* TODO: Get smarter about managing the cache size.
* If the cache is full, we need to do garbage collection
*/
return DECLINED;
}
return DECLINED;
}
/* Allocate and initialize cache_object_t */
if (!obj) {
return DECLINED;
}
return DECLINED;
}
/* Allocate and init mem_cache_object_t */
if (!mobj) {
return DECLINED;
}
/* Reference mem_cache_object_t out of cache_object_t */
/* Place the cache_object_t into the hash table.
* Note: Perhaps we should wait to put the object in the
* hash table when the object is complete? I add the object here to
* avoid multiple threads attempting to cache the same content only
* to discover at the very end that only one of them will suceed.
* Furthermore, adding the cache object to the table at the end could
* open up a subtle but easy to exploit DoS hole: someone could request
* a very large file with multiple requests. Better to detect this here
* rather than after the cache object has been completely built and
* initialized...
* XXX Need a way to insert into the cache w/o such coarse grained locking
*/
}
key,
if (!tmp_obj) {
sconf->object_cnt++;
}
}
if (tmp_obj) {
/* This thread collided with another thread loading the same object
* into the cache at the same time. Defer to the other thread which
* is further along.
*/
return DECLINED;
}
/* Set the cleanup flag and register the cleanup to cleanup
* the cache_object_t if the cache load is aborted.
*/
/* Populate the cache handle */
h->read_headers = &read_headers;
h->write_body = &write_body;
h->write_headers = &write_headers;
h->remove_entity = &remove_entity;
return OK;
}
{
/* Look up entity keyed to 'url' */
return DECLINED;
}
}
if (obj) {
}
else {
}
}
}
if (!obj) {
return DECLINED;
}
/* Initialize the cache_handle */
h->read_headers = &read_headers;
h->write_body = &write_body;
h->write_headers = &write_headers;
h->remove_entity = &remove_entity;
return OK;
}
static int remove_entity(cache_handle_t *h)
{
/* Remove the cache object from the cache */
}
if (obj) {
sconf->object_cnt--;
}
else {
/* If the object was removed from the cache prior to this function being
* called, then obj == NULL. Reinit obj with the object being cleaned up.
* Note: This code assumes that there is at most only one object in the
* cache per key.
*/
}
/* Cleanup the cache object
* Set obj->cleanup to ensure decrement_refcount cleans up the obj if it
* is still being referenced by another thread
*/
#ifndef USE_ATOMICS
}
#endif
}
#ifdef USE_ATOMICS
}
#endif
return OK;
}
{
apr_ssize_t i;
apr_size_t len = 0;
apr_size_t idx = 0;
char *buf;
if (*nelts == 0 ) {
return APR_SUCCESS;
}
return APR_ENOMEM;
}
}
/* Transfer the headers into a contiguous memory block */
if (!buf) {
return APR_ENOMEM;
}
for (i = 0; i < *nelts; ++i) {
}
return APR_SUCCESS;
}
int num_headers,
apr_table_t *t )
{
int i;
for (i = 0; i < num_headers; ++i) {
}
return APR_SUCCESS;
}
/* Define request processing hook handlers */
{
return DECLINED;
}
/* WIBNIF
* apr_hash_set(..,..,..,NULL) returned pointer to the object just removed.
* That way, we could free the object w/o doing another call to an
* apr_hash function.
*/
/* First, find the object in the cache */
}
if (obj) {
sconf->object_cnt--;
/* Set cleanup to ensure decrement_refcount cleans up the obj if it
* is still being referenced by another thread
*/
#ifdef USE_ATOMICS
/* Refcount increment MUST be made under protection of the lock */
#else
}
#endif
}
}
#ifdef USE_ATOMICS
if (obj) {
}
}
#endif
return OK;
}
{
int rc;
r->headers_out);
r->subprocess_env);
r->notes);
return rc;
}
{
apr_bucket *b;
/* CACHE_TYPE_FILE */
}
else {
/* CACHE_TYPE_HEAP */
}
b = apr_bucket_eos_create();
return APR_SUCCESS;
}
{
int rc;
/* Precompute how much storage we need to hold the headers */
r->headers_out);
if (rc != APR_SUCCESS) {
return rc;
}
r->subprocess_env );
if (rc != APR_SUCCESS) {
return rc;
}
if (rc != APR_SUCCESS) {
return rc;
}
/* Init the info struct */
}
}
}
if (info->content_type) {
return APR_ENOMEM;
}
}
return APR_ENOMEM;
}
}
return APR_SUCCESS;
}
{
apr_bucket *e;
char *cur;
if (CACHE_FD) {
int fd = 0;
int other = 0;
/* We can cache an open file descriptor if:
* - the brigade contains one and only one file_bucket &&
* - the brigade is complete &&
* - the file_bucket is the last data bucket in the brigade
*/
APR_BRIGADE_FOREACH(e, b) {
if (APR_BUCKET_IS_EOS(e)) {
}
else if (APR_BUCKET_IS_FILE(e)) {
apr_bucket_file *a = e->data;
fd++;
}
else {
other++;
}
}
/* Open a new XTHREAD handle to the file */
APR_OS_DEFAULT, r->pool);
if (rv != APR_SUCCESS) {
return rv;
}
/* Open for business */
return APR_SUCCESS;
}
}
/*
* FD cacheing is not enabled or the content was not
* suitable for fd caching.
*/
return APR_ENOMEM;
}
}
/* Iterate accross the brigade and populate the cache storage */
APR_BRIGADE_FOREACH(e, b) {
const char *s;
if (APR_BUCKET_IS_EOS(e)) {
/* Open for business */
break;
}
if (rv != APR_SUCCESS) {
return rv;
}
/* XXX Check for overflow */
if (len ) {
}
/* This should not happen, but if it does, we are in BIG trouble
* cause we just stomped all over the heap.
*/
}
return APR_SUCCESS;
}
static const char
{
int val;
return "CacheSize value must be an integer (kBytes)";
}
return NULL;
}
static const char
{
return "CacheMinObjectSize value must be an integer (bytes)";
}
return NULL;
}
static const char
{
return "CacheMaxObjectSize value must be an integer (KB)";
}
return NULL;
}
static const char
{
return "CacheMaxObjectCount value must be an integer";
}
return NULL;
}
static const command_rec cache_cmds[] =
{
"The maximum space used by the cache in KB"),
"The maximum number of objects allowed to be placed in the cache"),
"The minimum size (in bytes) of an object to be placed in the cache"),
"The maximum size (in KB) of an object to be placed in the cache"),
{NULL}
};
static void register_hooks(apr_pool_t *p)
{
/* cache initializer */
/* cache_hook_cache_init(cache_init, NULL, NULL, AP_HOOK_FIRST); */
}
{
NULL, /* create per-directory config structure */
NULL, /* merge per-directory config structures */
create_cache_config, /* create per-server config structure */
NULL, /* merge per-server config structures */
cache_cmds, /* command apr_table_t */
};