I believe that I have made some progress on this, but I'm not confident that my solution is the correct one. There were several factors playing into this that pulled my attention away from the real issue.
I originally reported that this problem was CGI specific; it turns out that this is not the case. The way I was reproducing what appeared to be a major leak was by sending a number of parallel requests to CGIs that generate a lot of output. The result of this was that Apache needed a lot of temporary buffers at the same time. After digging around for a while, I discovered that Apache on Windows never calls apr_allocator_max_free_set(), and the default behaviour is to hold on to all memory ever allocated, and allocate memory future requests out of that block. When I spawned the parallel requests, Apache grabbed a whole bunch of memory, and then kept reusing it. After setting a limit here, I was able to move on to the bigger problem. After making the change above, I realized that every request was leaking a little bit, not just the CGI requests. To confirm this, I made a simple configuration file that redirects all requests to a static HTML file. Sure enough, it was leaking. The problem, as best as I can tell, is that mpm_winnt calls apr_bucket_alloc_create() once for each thread, and registers it in the pchild pool. This bucket allocator is then passed through to core_create_conn(), and used for all apr_bucket_XXX() routines during the request. The pchild pool is not cleared until the server shuts down, so the memory used here grows and grows. To solve this problem I changed mpm_winnt so that it creates the bucket allocator using the ptrans pool, which gets cleared after every connection is finished. After making this change, the system behaved much better. Just to check, I then undid my first change related to the maximum memory to hold on to, and the system continued to function corrrectly. So in the end, I only needed the one fix. The following patch shows the changes I made. My question now for the experts is whether this will break anything. Thanks, Alex. Index: server/mpm/winnt/child.c =================================================================== RCS file: /home/cvspublic/httpd-2.0/server/mpm/winnt/child.c,v retrieving revision 1.9 diff -u -u -r1.9 child.c --- server/mpm/winnt/child.c 14 Oct 2002 14:54:45 -0000 1.9 +++ server/mpm/winnt/child.c 20 Dec 2002 01:02:38 -0000 @@ -122,6 +122,7 @@ if (context) { apr_pool_clear(context->ptrans); context->next = NULL; + context->ba = apr_bucket_alloc_create(context->ptrans); ResetEvent(context->Overlapped.hEvent); apr_thread_mutex_lock(qlock); if (qtail) @@ -187,7 +188,7 @@ apr_pool_tag(context->ptrans, "ptrans"); context->accept_socket = INVALID_SOCKET; - context->ba = apr_bucket_alloc_create(pchild); + context->ba = apr_bucket_alloc_create(context->ptrans); apr_atomic_inc(&num_completion_contexts); } @@ -416,7 +417,7 @@ context = apr_pcalloc(pchild, sizeof(COMP_CONTEXT)); apr_pool_create(&context->ptrans, pchild); apr_pool_tag(context->ptrans, "ptrans"); - context->ba = apr_bucket_alloc_create(pchild); + context->ba = apr_bucket_alloc_create(context->ptrans); } while (1) {