Hi,

I found a problem with non-blocking write to pipe.

Current code (2.0.43) is as following.

------------------------------------------------------------
httpd-2.0.43/srclib/apr/file_io/unix/readwrite.c:apr_file_write()
------------------------------------------------------------
        do {
            rv = write(thefile->filedes, buf, *nbytes);
        } while (rv == (apr_size_t)-1 && errno == EINTR);
#ifdef USE_WAIT_FOR_IO
        if (rv == (apr_size_t)-1 &&
            (errno == EAGAIN || errno == EWOULDBLOCK) &&
            thefile->timeout != 0) {
            apr_status_t arv = apr_wait_for_io_or_timeout(thefile, NULL, 0);
            if (arv != APR_SUCCESS) {
                *nbytes = 0;
                return arv;
            }
            else {
                do {
                    rv = write(thefile->filedes, buf, *nbytes);
                } while (rv == (apr_size_t)-1 && errno == EINTR);
            }
        }
#endif
------------------------------------------------------------

It seems assuming write request never fail when
apr_wait_for_io_or_timeout() succeeded, but it is not true
for pipe.

"The Single UNIXR Specification, Version 2" is saying :

------------------------------------------------------------
Write requests to a pipe or FIFO will be handled the same as
a regular file with the following exceptions: 

[snip]

If the O_NONBLOCK flag is set, write() requests will be
handled differently, in the following ways: 

  - The write() function will not block the thread. 

  - A write request for {PIPE_BUF} or fewer bytes will have
    the following effect: If there is sufficient space
    available in the pipe, write() will transfer all the
    data and return the number of bytes requested. Otherwise,
    write() will transfer no data and return -1 with errno
    set to [EAGAIN]. 
------------------------------------------------------------

It means that if room on the pipe is smaller than *nbyes and
*nbytes is less than or equal to {PIPE_BUF}, write() can
fail with errno=EAGAIN and return -1, and apr_file_write()
just fails.

I found this problem on HP-UX11.0 whose PIPE_BUF is 8192 with
CGI that receive more than 8kbytes POST request.

This problem can be fixed with the following code, however I
do not know if there is better solution other than looping.

------------------------------------------------------------
        do {
            rv = write(thefile->filedes, buf, *nbytes);
        } while (rv == (apr_size_t)-1 && errno == EINTR);
#ifdef USE_WAIT_FOR_IO
        if (rv == (apr_size_t)-1 &&
            (errno == EAGAIN || errno == EWOULDBLOCK) &&
            thefile->timeout != 0) {
            apr_status_t arv = apr_wait_for_io_or_timeout(thefile, NULL, 0);
            if (arv != APR_SUCCESS) {
                *nbytes = 0;
                return arv;
            }
            else {
                do {
                    rv = write(thefile->filedes, buf, *nbytes);

                    /* write request of {PIPE_BUF} bytes or less may fail */
                    /* because it is atomic when writing to pipe or FIFO  */
                    while (rv == (apr_size_t)-1 &&
                           *nbytes < PIPE_BUF && errno == EAGAIN)
                    {
                        apr_sleep(10000);       /* sleep ~10ms */
                        rv = write(thefile->filedes, buf, *nbytes);
                    }
                } while (rv == (apr_size_t)-1 && errno == EINTR);
            }
        }
#endif
------------------------------------------------------------


--
[EMAIL PROTECTED]

Reply via email to