Ryan, I've CC'd you on this just to let you see the patch. If you don't want me to involve you in this, please accept my apologies and let me know and I won't CC you in any further patches.
I spent some time debugging the perchild MPM since it wasn't working for me, nor anyone else it seems. I've found a few problems: Problem 1: In worker_thread, there is a variable called csd that is used to get the new socket from lr->accept_func(). If that variable is NULL, then the memory for the new socket is allocated in the per-transaction pool. Unfortunately, the code neglected to reset the variable to NULL after servicing a request. The result is that the first request for each thread worked fine, but subsequent request may have the memory overwritten and resulting in an invalid FD. Solution: Set it to NULL before calling lr->accept_func(). Implemented in the patch below. Problem 2: pass_request fills out an iovec with the headers and the body of the request it wants to pass to another process. It unfortunately uses the wrong variable for the length causing it to always be 0. Solution: Use the correct variable name "l". len is never set to anything so I removed that and used 0 in the one reference to it. Implemented in the patch below. Problem 3: receive_from_other_child assumes that the iovec is the same on read as it is on write. This isn't true and readmsg() follows readv() semantics. iovec is a scatter/gather list and as a result, the 2 send buffers are merged into one received buffer with the second always being untouched. It also trusted the lengths in iov.iov_len which will be the size of the original buffer, not the size of the data actually received. Solution: Merge the 2 buffer's into 1 and find the null terminators for the 2 strings. Implemented in the patch below. Problem 4: Occasionally, when a request needs to be passed to another process, it will hang until another connection is accepted. perchild uses the same process accept mutex that the other MPM's seem to need. This results in serialization of all accept() calls as is the intent of the mutex. The problem is that another process might have acquired the mutex from the process that actually needs the mutex to hit accept() and see the passed request. This isn't normally a problem because all processes accept connections on all listening ports. The problem in this case is the fact not all processes accept connections on the per process unix domain socket to pass connections around, for obvious reasons. Solution: I don't have one :) I'm not sure what the best way of solving this is. I don't fully understand the semantics of the process accept mutex yet. Any suggestions? Anyway, this patch has been tested lightly and makes things work MUCH better for the perchild MPM now, atleast for me :) If this patch is acceptable by the Apache development team, can it be applied? JE Index: perchild.c =================================================================== RCS file: /home/cvspublic/httpd-2.0/server/mpm/experimental/perchild/perchild.c,v retrieving revision 1.135 diff -u -r1.135 perchild.c --- perchild.c 11 Oct 2002 15:41:52 -0000 1.135 +++ perchild.c 18 Oct 2002 01:31:22 -0000 @@ -679,9 +679,9 @@ { struct msghdr msg; struct cmsghdr *cmsg; - char headers[HUGE_STRING_LEN]; - char request_body[HUGE_STRING_LEN]; - struct iovec iov[2]; + char buffer[HUGE_STRING_LEN * 2], *headers, *body; + int headerslen, bodylen; + struct iovec iov; int ret, dp; apr_os_sock_t sd; apr_bucket_alloc_t *alloc = apr_bucket_alloc_create(ptrans); @@ -690,15 +690,13 @@ apr_os_sock_get(&sd, lr->sd); - iov[0].iov_base = headers; - iov[0].iov_len = HUGE_STRING_LEN; - iov[1].iov_base = request_body; - iov[1].iov_len = HUGE_STRING_LEN; + iov.iov_base = buffer; + iov.iov_len = sizeof(buffer); msg.msg_name = NULL; msg.msg_namelen = 0; - msg.msg_iov = iov; - msg.msg_iovlen = 2; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; cmsg = apr_palloc(ptrans, sizeof(*cmsg) + sizeof(sd)); cmsg->cmsg_len = sizeof(*cmsg) + sizeof(sd); @@ -715,14 +713,25 @@ APR_BRIGADE_INSERT_HEAD(bb, bucket); bucket = apr_bucket_socket_create(*csd, alloc); APR_BRIGADE_INSERT_HEAD(bb, bucket); - bucket = apr_bucket_heap_create(iov[1].iov_base, - iov[1].iov_len, NULL, alloc); + + body = strchr(iov.iov_base, 0); + if (!body) { + return 1; + } + + body++; + bodylen = strlen(body); + + headers = iov.iov_base; + headerslen = body - headers; + + bucket = apr_bucket_heap_create(body, bodylen, NULL, alloc); APR_BRIGADE_INSERT_HEAD(bb, bucket); - bucket = apr_bucket_heap_create(iov[0].iov_base, - iov[0].iov_len, NULL, alloc); + bucket = apr_bucket_heap_create(headers, headerslen, NULL, alloc); APR_BRIGADE_INSERT_HEAD(bb, bucket); apr_pool_userdata_set(bb, "PERCHILD_SOCKETS", NULL, ptrans); + return 0; } @@ -730,7 +739,7 @@ static void *worker_thread(apr_thread_t *thd, void *arg) { - void *csd = NULL; + void *csd; apr_pool_t *tpool; /* Pool for this thread */ apr_pool_t *ptrans; /* Pool for per-transaction stuff */ volatile int thread_just_started = 1; @@ -832,6 +841,7 @@ } got_fd: if (!workers_may_exit) { + csd = NULL; rv = lr->accept_func(&csd, lr, ptrans); if (rv == APR_EGENERAL) { /* E[NM]FILE, ENOMEM, etc */ @@ -1635,7 +1645,6 @@ apr_bucket_brigade *bb = apr_brigade_create(r->pool, c->bucket_alloc); apr_bucket_brigade *sockbb; char request_body[HUGE_STRING_LEN] = "\0"; - apr_off_t len = 0; apr_size_t l = 0; perchild_header h; apr_bucket *sockbuck; @@ -1647,7 +1656,7 @@ "passing request to another child. Vhost: %s, child %d %d", apr_table_get(r->headers_in, "Host"), child_num, sconf->output); ap_get_brigade(r->connection->input_filters, bb, AP_MODE_EXHAUSTIVE, APR_NONBLOCK_READ, - len); + 0); for (sockbuck = APR_BRIGADE_FIRST(bb); sockbuck != APR_BRIGADE_SENTINEL(bb); sockbuck = APR_BUCKET_NEXT(sockbuck)) { @@ -1678,7 +1687,7 @@ iov[0].iov_base = h.headers; iov[0].iov_len = strlen(h.headers) + 1; iov[1].iov_base = request_body; - iov[1].iov_len = len + 1; + iov[1].iov_len = l + 1; msg.msg_name = NULL; msg.msg_namelen = 0;