--- mod_disk_cache.c.ls-rwc-fixups 2006-10-08 19:17:31.000000000 +0200 +++ mod_disk_cache.c 2006-10-08 19:11:40.000000000 +0200 @@ -22,6 +22,8 @@ #include "util_filter.h" #include "util_script.h" #include "util_charset.h" +#include "ap_mpm.h" + /* * mod_disk_cache: Disk Based HTTP 1.1 Cache. @@ -1677,6 +1679,272 @@ static apr_status_t copy_body(apr_pool_t } +/* Provide srcfile and srcinfo containing + APR_FINFO_INODE|APR_FINFO_MTIME to make sure we have opened the right file + (someone might have just replaced it which messes up things). +*/ +static apr_status_t copy_body_nofd(apr_pool_t *p, const char *srcfile, + apr_off_t srcoff, apr_finfo_t *srcinfo, + const char *destfile, apr_off_t destoff, + apr_off_t len) +{ + apr_status_t rc; + apr_file_t *srcfd, *destfd; + apr_finfo_t finfo; + + rc = apr_file_open(&srcfd, srcfile, APR_READ | APR_BINARY, 0, p); + if(rc != APR_SUCCESS) { + return rc; + } + rc = apr_file_info_get(&finfo, APR_FINFO_INODE|APR_FINFO_MTIME, srcfd); + if(rc != APR_SUCCESS) { + return rc; + } + if(srcinfo->inode != finfo.inode || srcinfo->mtime < finfo.mtime) { + return APR_EGENERAL; + } + + rc = apr_file_open(&destfd, destfile, APR_WRITE | APR_BINARY, 0, p); + if(rc != APR_SUCCESS) { + return rc; + } + + rc = copy_body(p, srcfd, srcoff, destfd, destoff, len); + apr_file_close(srcfd); + if(rc != APR_SUCCESS) { + apr_file_close(destfd); + return rc; + } + + return apr_file_close(destfd); +} + + +#if APR_HAS_THREADS +static apr_status_t bgcopy_thread_cleanup(void *data) +{ + copyinfo *ci = data; + apr_status_t rc, ret; + apr_pool_t *p; + + /* FIXME: Debug */ + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ci->s, + "disk_cache: bgcopy_thread_cleanup: %s -> %s", + ci->srcfile, ci->destfile); + + rc = apr_thread_join(&ret, ci->t); + if(rc != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, rc, ci->s, + "disk_cache: bgcopy_thread_cleanup: apr_thread_join " + "failed %s -> %s", ci->srcfile, ci->destfile); + return rc; + } + if(ret != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, ret, ci->s, + "disk_cache: Background caching body %s -> %s failed", + ci->srcfile, ci->destfile); + } + + /* FIXME: Debug */ + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ci->s, + "disk_cache: bgcopy_thread_cleanup: SUCCESS %s -> %s", + ci->srcfile, ci->destfile); + + /* Destroy our private pool */ + p = ci->pool; + apr_pool_destroy(p); + + return APR_SUCCESS; +} + + +static void *bgcopy_thread(apr_thread_t *t, void *data) +{ + copyinfo *ci = data; + apr_pool_t *p; + apr_status_t rc; + + p = apr_thread_pool_get(t); + + /* FIXME: Debug */ + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ci->s, + "disk_cache: bgcopy_thread: start %s -> %s", + ci->srcfile, ci->destfile); + + rc = copy_body_nofd(p, ci->srcfile, ci->srcoff, &(ci->srcinfo), + ci->destfile, ci->destoff, ci->len); + + if(rc != APR_SUCCESS) { + apr_file_remove(ci->destfile, p); + } + + /* FIXME: Debug */ + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ci->s, + "disk_cache: bgcopy_thread: done %s -> %s", + ci->srcfile, ci->destfile); + + apr_thread_exit(t, rc); + return NULL; +} +#endif /* APR_HAS_THREADS */ + + +#if APR_HAS_FORK +static apr_status_t bgcopy_child_cleanup(void *data) { + copyinfo *ci = data; + int status; + apr_exit_why_e why; + apr_pool_t *p; + + apr_proc_wait(ci->proc, &status, &why, APR_WAIT); + if(why == APR_PROC_EXIT) { + if(status != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, status, ci->s, + "disk_cache: Background caching body %s -> %s failed", + ci->srcfile, ci->destfile); + return APR_SUCCESS; + } + } + else if(status & (APR_PROC_SIGNAL | APR_PROC_SIGNAL_CORE) ) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, ci->s, + "disk_cache: Background caching body %s -> %s failed, " + "caught signal %d", ci->srcfile, ci->destfile, status); + return APR_SUCCESS; + } + + /* Destroy our private pool */ + p = ci->pool; + apr_pool_destroy(p); + + return APR_SUCCESS; +} +#endif /* APR_HAS_FORK */ + + +static apr_status_t do_bgcopy(apr_file_t *srcfd, apr_off_t srcoff, + apr_file_t *destfd, apr_off_t destoff, + apr_off_t len, conn_rec *c) +{ + copyinfo *ci; + apr_status_t rv; + apr_pool_t *newpool; + const char *srcfile, *destfile; + int mpm_query_info; + + /* It seems pool gets destroyed (ie. fd's closed) before our cleanup + function is called when an error occurs (a dropped connection, for + example), so we need a pool of our own. + */ + rv = apr_pool_create(&newpool, NULL); + if (rv != APR_SUCCESS) { + return rv; + } + + ci = apr_palloc(newpool, sizeof(*ci)); + if(ci == NULL) { + apr_pool_destroy(newpool); + return APR_ENOMEM; + } + + rv = apr_file_name_get(&srcfile, srcfd); + if(rv != APR_SUCCESS) { + return rv; + } + rv = apr_file_info_get(&(ci->srcinfo), APR_FINFO_INODE|APR_FINFO_MTIME, + srcfd); + if(rv != APR_SUCCESS) { + return rv; + } + + rv = apr_file_name_get(&destfile, destfd); + if(rv != APR_SUCCESS) { + return rv; + } + + ci->pool = newpool; + ci->srcfile = apr_pstrdup(newpool, srcfile); + ci->srcoff = srcoff; + ci->destfile = apr_pstrdup(newpool, destfile); + ci->destoff = destoff; + ci->len = len; + ci->s = c->base_server; + +#if APR_HAS_THREADS + if(ap_mpm_query(AP_MPMQ_IS_THREADED, &mpm_query_info) == APR_SUCCESS) { + apr_threadattr_t *ta; + apr_thread_t *t; + rv = apr_threadattr_create(&ta, newpool); + if(rv != APR_SUCCESS) { + apr_pool_destroy(newpool); + return rv; + } + + apr_threadattr_detach_set(ta, FALSE); + + /* FIXME: This makes module unloadable on AIX */ +#if 0 +#ifdef AP_MPM_WANT_SET_STACKSIZE + if (ap_thread_stacksize != 0) { + apr_threadattr_stacksize_set(ta, ap_thread_stacksize); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, c->base_server, + "disk_cache: BG thread stacksize set to %" + APR_SIZE_T_FMT, ap_thread_stacksize); + } +#endif /* AP_MPM_WANT_SET_STACKSIZE */ +#endif /* 0 */ + + rv = apr_thread_create (&t, ta, bgcopy_thread, ci, newpool); + if (rv != APR_SUCCESS) { + apr_pool_destroy(newpool); + return rv; + } + ci->t = t; + + apr_pool_cleanup_register(c->pool, ci, + bgcopy_thread_cleanup, apr_pool_cleanup_null); + } + else +#endif /* APR_HAS_THREADS */ +#if APR_HAS_FORK + if(ap_mpm_query(AP_MPMQ_IS_FORKED, &mpm_query_info) == APR_SUCCESS) { + ci->proc = apr_palloc(newpool, sizeof(apr_proc_t)); + if(ci->proc == NULL) { + apr_pool_destroy(newpool); + return APR_ENOMEM; + } + rv = apr_proc_fork(ci->proc, newpool); + if(rv == APR_INCHILD) { + /* Child */ + rv = copy_body_nofd(ci->pool, ci->srcfile, ci->srcoff, + &(ci->srcinfo), ci->destfile, ci->destoff, + ci->len); + if(rv != APR_SUCCESS) { + apr_file_remove(ci->destfile, ci->pool); + } + exit(rv); + } + else if(rv == APR_INPARENT) { + apr_pool_cleanup_register(c->pool, ci, + bgcopy_child_cleanup, + apr_pool_cleanup_null); + } + else { + return rv; + } + } + else +#endif /* APR_HAS_FORK */ + if(1) + { + rv = copy_body(newpool, srcfd, ci->srcoff, destfd, ci->destoff, + ci->len); + apr_pool_destroy(newpool); + } + + return rv; +} + + static apr_status_t replace_brigade_with_cache(cache_handle_t *h, request_rec *r, apr_bucket_brigade *bb) @@ -1828,8 +2096,14 @@ static apr_status_t store_body(cache_han e = APR_BRIGADE_FIRST(bb); a = e->data; - rv = copy_body(r->pool, a->fd, e->start, dobj->fd, 0, - dobj->file_size); + if(dobj->file_size > conf->minbgsize) { + rv = do_bgcopy(a->fd, e->start, dobj->fd, 0, dobj->file_size, + r->connection); + } + else { + rv = copy_body(r->pool, a->fd, e->start, dobj->fd, 0, + dobj->file_size); + } if(rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server, "disk_cache: Copying body failed, " @@ -1963,6 +2237,7 @@ static void *create_config(apr_pool_t *p conf->maxfs = DEFAULT_MAX_FILE_SIZE; conf->minfs = DEFAULT_MIN_FILE_SIZE; conf->updtimeout = DEFAULT_UPDATE_TIMEOUT; + conf->minbgsize = DEFAULT_MIN_BACKGROUND_SIZE; conf->cache_root = NULL; conf->cache_root_len = 0; @@ -2065,6 +2340,22 @@ static const char } +static const char +*set_cache_minbgsize(cmd_parms *parms, void *in_struct_ptr, const char *arg) +{ + disk_cache_conf *conf = ap_get_module_config(parms->server->module_config, + &disk_cache_module); + + if (apr_strtoff(&conf->minbgsize, arg, NULL, 0) != APR_SUCCESS || + conf->minbgsize < 0) + { + return "CacheMinBGSize argument must be a non-negative integer representing the min size in bytes for a file to be eligable for background caching"; + } + + return NULL; +} + + static const command_rec disk_cache_cmds[] = { AP_INIT_TAKE1("CacheRoot", set_cache_root, NULL, RSRC_CONF, @@ -2079,6 +2370,8 @@ static const command_rec disk_cache_cmds "The maximum file size to cache a document"), AP_INIT_TAKE1("CacheUpdateTimeout", set_cache_updtimeout, NULL, RSRC_CONF, "Timeout in ms for cache updates"), + AP_INIT_TAKE1("CacheMinBGSize", set_cache_minbgsize, NULL, RSRC_CONF, + "The minimum file size for background caching"), {NULL} }; --- mod_disk_cache.h.rwc 2006-10-06 14:22:32.000000000 +0200 +++ mod_disk_cache.h 2006-10-08 19:11:31.000000000 +0200 @@ -99,6 +99,8 @@ typedef struct disk_cache_object { #define DEFAULT_MIN_FILE_SIZE 1 #define DEFAULT_MAX_FILE_SIZE 1000000 #define DEFAULT_UPDATE_TIMEOUT apr_time_from_sec(10) +/* Background caching disabled by default */ +#define DEFAULT_MIN_BACKGROUND_SIZE DEFAULT_MAX_FILE_SIZE typedef struct { const char* cache_root; @@ -108,6 +110,7 @@ typedef struct { apr_off_t minfs; /* minimum file size for cached files */ apr_off_t maxfs; /* maximum file size for cached files */ apr_interval_time_t updtimeout; /* Cache update timeout */ + apr_off_t minbgsize; /* minimum file size to do bg caching */ } disk_cache_conf; #define CACHE_ENODATA (APR_OS_START_USERERR+1) @@ -128,5 +131,31 @@ struct diskcache_bucket_data { }; +/* Stuff needed by the background copy thread */ +typedef struct copyinfo copyinfo; +struct copyinfo { + apr_off_t len; + /* Source info */ + const char *srcfile; + apr_finfo_t srcinfo; + apr_off_t srcoff; + /* Destination info */ + const char *destfile; + apr_off_t destoff; + + /* Our private pool */ + apr_pool_t *pool; + +#if APR_HAS_THREADS + /* Background process info */ + apr_thread_t *t; +#endif /* APR_HAS_THREADS */ +#if APR_HAS_FORK + apr_proc_t *proc; +#endif /* APR_HAS_FORK */ + + /* For logging */ + const server_rec *s; +}; #endif /*MOD_DISK_CACHE_H*/