Based on Cliff's suggestion, this patch introduces an apr_mmap_dup() function that's used to setaside mmap buckets without having to memcpy the content.
I gave up trying to do full reference counting semantics for duplicates of apr_mmap_t, because I couldn't find a suitable pool to own the locks that would be required in a threaded implementation. Instead, apr_mmap_dup() lets the caller specify which of the two apr_mmap_t structs--the original one or the new one--is the owner of the mmap (and is responsible for destroying it). The patch includes a change to mod_file_cache to create a non-owner copy of the mmap for the bucket brigade to use, so that the mmap can't get unmapped by any code other than mod_file_cache itself --Brian
Index: srclib/apr/include/apr_mmap.h =================================================================== RCS file: /home/cvspublic/apr/include/apr_mmap.h,v retrieving revision 1.25 diff -u -r1.25 apr_mmap.h --- srclib/apr/include/apr_mmap.h 2001/08/18 15:15:46 1.25 +++ srclib/apr/include/apr_mmap.h 2001/11/15 05:45:58 @@ -111,6 +111,8 @@ void *mm; /** The amount of data in the mmap */ apr_size_t size; + /** Whether this object is reponsible for doing the munmap */ + int is_owner; }; #if APR_HAS_MMAP || defined(DOXYGEN) @@ -134,6 +136,19 @@ apr_file_t *file, apr_off_t offset, apr_size_t size, apr_int32_t flag, apr_pool_t *cntxt); + +/** + * Duplicate the specified MMAP. + * @param new_mmap The structure to duplicate into. + * @param old_mmap The file to duplicate. + * @param p The pool to use for the new file. + * @param transfer_ownership Whether responsibility for destroying + * the memory-mapped segment is transferred from old_mmap to new_mmap + */ +APR_DECLARE(apr_status_t) apr_mmap_dup(apr_mmap_t **new_mmap, + apr_mmap_t *old_mmap, + apr_pool_t *p, + int transfer_ownership); /** * Remove a mmap'ed. Index: srclib/apr/mmap/unix/mmap.c =================================================================== RCS file: /home/cvspublic/apr/mmap/unix/mmap.c,v retrieving revision 1.36 diff -u -r1.36 mmap.c --- srclib/apr/mmap/unix/mmap.c 2001/08/02 04:26:53 1.36 +++ srclib/apr/mmap/unix/mmap.c 2001/11/15 05:45:58 @@ -83,6 +83,11 @@ { apr_mmap_t *mm = themmap; int rv; + + if (!mm->is_owner) { + return APR_SUCCESS; + } + #ifdef BEOS rv = delete_area(mm->area); @@ -159,10 +164,38 @@ (*new)->mm = mm; (*new)->size = size; (*new)->cntxt = cont; + (*new)->is_owner = 1; /* register the cleanup... */ apr_pool_cleanup_register((*new)->cntxt, (void*)(*new), mmap_cleanup, apr_pool_cleanup_null); + return APR_SUCCESS; +} + +APR_DECLARE(apr_status_t) apr_mmap_dup(apr_mmap_t **new_mmap, + apr_mmap_t *old_mmap, + apr_pool_t *p, + int transfer_ownership) +{ + *new_mmap = (apr_mmap_t *)apr_palloc(p, sizeof(apr_mmap_t)); + memcpy(*new_mmap, old_mmap, sizeof(apr_mmap_t)); + (*new_mmap)->cntxt = p; + + /* The old_mmap can transfer ownership only if the old_mmap itself + * is an owner of the mmap'ed segment. + */ + if (old_mmap->is_owner) { + if (transfer_ownership) { + (*new_mmap)->is_owner = 1; + old_mmap->is_owner = 0; + apr_pool_cleanup_kill(old_mmap->cntxt, old_mmap, mmap_cleanup); + } + else { + (*new_mmap)->is_owner = 0; + } + apr_pool_cleanup_register(p, *new_mmap, mmap_cleanup, + apr_pool_cleanup_null); + } return APR_SUCCESS; } Index: srclib/apr/mmap/win32/mmap.c =================================================================== RCS file: /home/cvspublic/apr/mmap/win32/mmap.c,v retrieving revision 1.6 diff -u -r1.6 mmap.c --- srclib/apr/mmap/win32/mmap.c 2001/06/27 22:07:24 1.6 +++ srclib/apr/mmap/win32/mmap.c 2001/11/15 05:45:58 @@ -62,10 +62,15 @@ #if APR_HAS_MMAP -static apr_status_t mmap_cleanup(void *themmap) +APR_DECLARE(apr_status_t) apr_mmap_cleanup(void *themmap) { apr_mmap_t *mm = themmap; apr_status_t rv = 0; + + if (!mm->is_owner) { + return APR_SUCCESS; + } + if (mm->mv) { if (!UnmapViewOfFile(mm->mv)) { @@ -155,19 +160,47 @@ (*new)->mm = (char*)((*new)->mv) + offset; (*new)->size = size; (*new)->cntxt = cont; + (*new)->is_owner = 1; /* register the cleanup... */ - apr_pool_cleanup_register((*new)->cntxt, (void*)(*new), mmap_cleanup, + apr_pool_cleanup_register((*new)->cntxt, (void*)(*new), apr_mmap_cleanup, apr_pool_cleanup_null); return APR_SUCCESS; } +APR_DECLARE(apr_status_t) apr_mmap_dup(apr_mmap_t **new_mmap, + apr_mmap_t *old_mmap, + apr_pool_t *p, + int transfer_ownership) +{ + *new_mmap = (apr_mmap_t *)apr_palloc(p, sizeof(apr_mmap_t)); + memcpy(*new_mmap, old_mmap, sizeof(apr_mmap_t)); + (*new_mmap)->cntxt = p; + + /* The old_mmap can transfer ownership only if the old_mmap itself + * is an owner of the mmap'ed segment. + */ + if (old_mmap->is_owner) { + if (transfer_ownership) { + (*new_mmap)->is_owner = 1; + old_mmap->is_owner = 0; + apr_pool_cleanup_kill(old_mmap->cntxt, old_mmap, mmap_cleanup); + } + else { + (*new_mmap)->is_owner = 0; + } + apr_pool_cleanup_register(p, *new_mmap, mmap_cleanup, + apr_pool_cleanup_null); + } + return APR_SUCCESS; +} + APR_DECLARE(apr_status_t) apr_mmap_delete(apr_mmap_t *mmap) { apr_status_t rv; - if ((rv = mmap_cleanup(mmap)) == APR_SUCCESS) { - apr_pool_cleanup_kill(mmap->cntxt, mmap, mmap_cleanup); + if ((rv = apr_mmap_cleanup(mmap)) == APR_SUCCESS) { + apr_pool_cleanup_kill(mmap->cntxt, mmap, apr_mmap_cleanup); return APR_SUCCESS; } return rv; Index: srclib/apr-util/buckets/apr_buckets_mmap.c =================================================================== RCS file: /home/cvspublic/apr-util/buckets/apr_buckets_mmap.c,v retrieving revision 1.43 diff -u -r1.43 apr_buckets_mmap.c --- srclib/apr-util/buckets/apr_buckets_mmap.c 2001/09/22 22:36:07 1.43 +++ srclib/apr-util/buckets/apr_buckets_mmap.c 2001/11/15 05:45:58 @@ -121,24 +121,18 @@ { apr_bucket_mmap *m = data->data; apr_mmap_t *mm = m->mmap; - char *base; - void *addr; + apr_mmap_t *new_mm; apr_status_t ok; if (apr_pool_is_ancestor(mm->cntxt, p)) { return APR_SUCCESS; } - ok = apr_mmap_offset(&addr, m->mmap, data->start); + ok = apr_mmap_dup(&new_mm, mm, p, 1); if (ok != APR_SUCCESS) { return ok; } - - base = apr_palloc(p, data->length); - memcpy(base, addr, data->length); - data = apr_bucket_pool_make(data, base, data->length, p); - mmap_destroy(m); - + m->mmap = new_mm; return APR_SUCCESS; } Index: modules/cache/mod_file_cache.c =================================================================== RCS file: /home/cvspublic/httpd-2.0/modules/cache/mod_file_cache.c,v retrieving revision 1.62 diff -u -r1.62 mod_file_cache.c --- modules/cache/mod_file_cache.c 2001/08/11 04:04:12 1.62 +++ modules/cache/mod_file_cache.c 2001/11/15 05:45:58 @@ -333,8 +333,20 @@ #if APR_HAS_MMAP apr_bucket *b; apr_bucket_brigade *bb = apr_brigade_create(r->pool); + apr_mmap_t *mm; - b = apr_bucket_mmap_create(file->mm, 0, (apr_size_t)file->finfo.size); + /* Create a copy of the apr_mmap_t that's marked as + * a "non-owner." The reason for this is that the + * mmap bucket setaside function might later try to + * transfer ownership of the mmap from a request + * pool to a parent pool. For this particular mmap, + * though, we want the cache to retain ownership so + * that it never gets munmapped. Thus we give the + * bucket code a non-owner copy to work with. + */ + apr_mmap_dup(&mm, file->mm, r->pool, 0); + + b = apr_bucket_mmap_create(mm, 0, (apr_size_t)file->finfo.size); APR_BRIGADE_INSERT_TAIL(bb, b); b = apr_bucket_eos_create(); APR_BRIGADE_INSERT_TAIL(bb, b);