httpd-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From dgau...@hotwired.com (Dean Gaudet)
Subject Re: apache patch (fwd)
Date Wed, 17 Apr 1996 23:27:35 GMT
In article <199604172231.SAA21959@volterra.ai.mit.edu> you write:
>The problem with LINGER is that some Unix kernels don't honor the timeout,
>in which case, closes on LINGERing sockets can literally take forever.  If
>you think zombie processes are bad, you've never had to contend with
>processes which are blocked *in exit()* trying to close a file descriptor
>without having first given up their memory image.  A few hundred of those
>can get to be a real inconvenience.

I deal with this in my MUD code by forking a program (vfork/exec or
fork/exec whatever your poison is) which closes the socket AFTER the
server has closed the socket.  In apache each child could delay all socket
closes until necessary (i.e. out of descriptors, client end closes,
or child wants to exit) and then fork a small program which cleans up.
The only trick is synchronising things so that the child can close its
sockets before the grandchild does.  I cheat and synchronize by abusing
pipe() (hey! it's portable).

Here's my code:

    /* the object is to close( d->descriptor ) */
    {
	/* close the socket in a fork()d child so that we don't have
	    to wait for the close to succeed */
	static int vfork_errors; /* these are to prevent log spammage */
	static int exec_errors;
	int fds[2];		/* to synchronize through pipe() */
	char arg_block[ 20 ];
	char arg_sock[ 20 ];
	const char *argv[4];

	if( pipe( fds ) != 0 ) {
	    syslogf( "close_socket: pipe: %s", strerror( errno ) );
	    exit( 52 );
	}
	switch( vfork() ) {
	case -1: /* vfork error */
	    ++vfork_errors;
	    if( vfork_errors < 10 ) {
		syslogf( "close_socket: #%d vfork(): %s",
		    vfork_errors, strerror( errno ) );
	    }
	    close( fds[0] );
	    close( fds[1] );
	    if( close( d->descriptor ) ) {
		syslogf( "close_socket: error closing socket: %s",
		    strerror( errno ) );
		exit( 52 );
	    }
	    break;
	case 0: /* child */
	    /* close the server's socket, since we have no reason to
		hang onto it */
	    close( game_socket );
	    close( fds[1] );
	    /* all sockets are set to be closed across exec() by default,
		so we have to mark this one single socket to stay open
		so that the child will have a copy of it */
	    if( fcntl( d->descriptor, F_SETFD, 0 ) == -1 ) {
		syslogf( "close_socket: fcntl( F_SETFD ): %s",
		    strerror( errno ) );
		_exit( 1 );
	    }
	    /* now spawn a program which will do the equivalent of this:

		read( fds[0], buf, sizeof(buf) );
		close( d->descriptor );
		close( fds[0] );

		The read() will block until the write() occurs in the
		default part of this switch(fork()).
	    */
	    sprintf( arg_block, "%d", fds[0] );
	    sprintf( arg_sock, "%d", d->descriptor );
	    argv[0] = "close_socket";
	    argv[1] = arg_block;
	    argv[2] = arg_sock;
	    argv[3] = NULL;
	    execvp( "close_socket", (void*)argv );
	    if( ++exec_errors < 10 ) {
		syslogf( "close_socket: execvp: %s", strerror( errno ) );
	    }
	    _exit( 1 );
	    break;
	default: /* parent */
	    /* first we close the descriptor */
	    close( d->descriptor );
	    /* now we close up the pipe so that the child will unblock,
		and be able to close the descriptor */
	    close( fds[0] );
	    write( fds[1], "blah", 4 );
	    close( fds[1] );
	    break;
	}

>(That said, use of LINGER on systems that support it *correctly* is a good
>idea.  The question is how to tell which is which).

Among those that don't support it correctly is Dynix/PTX.  Which is
the precise reason I wrote the above code.  I turn off linger on all
the mud's sockets (I'm not concerned about delivering all the data).
But when we ran on a sequent we'd freeze up on close() when there was
flakiness somewhere on the net (i.e. all the time).

The nice thing about this grandchild is that it has no critical sockets
open, just sockets that you want to close.  (You have to be careful
to make sure that's true though, careful use of F_SETFD is recommended.)
It's also a really small program.  It can be set up to close a list
of descriptors too.  (Or just pass it fds[0] and let it loop from 3
through the descriptor rlimit closing everything along the way ;)

Dean

Mime
View raw message