interrupted socket write() due to SIGCHLD (black bars on generated GIFs)

We have a cgi-bin program which generates arbitrarily large GIF files.
Once the GIF files exceed the bufferable limit (~52KB), there is a
race condition where the HTTP Daemon is attempting to write the output
of the CGI program down the HTTP socket (to the browser) and the CGI
program is finishing up.

If you're unlucky, the CGI program finishes while the HTTP Daemon is
still writing data.  This causes a SIGCHLD interrupt in the Daemon,
which interrupts the write(2) call in
Library/Implementation/HTWriter.c, causing the write() call to return
-1.  At this point, the old code merely (silently, unless in verbose
mode) gives up writing the rest of the CGI program's output to the
client.

This leads to a syndrome we have affectionately termed ``the black
bar'' problem, since the resulting GIF usually ends up blanked black
on the end-user's screen.

We have noticed this when running the HTTP Daemon on Digital Unix (DEC
OSF/1 2.0 & 3.2c), but not on Solaris 2.4 (probably because the
default behavior for an interrupted empty write() call on Solaris is
to restart the write).

The patch below simply checks errno for EINTR (i.e. the write call was
interrupted), and resets the status to zero (i.e. no bytes were
actually written), which permits the while loops to continue to flush
the data down the socket.

(The NCSA HTTPd had a similar problem -- they did check for EINTR ==
errno, but they failed to set the byte count to zero, causing them to
step back a byte in their read buffer!)

(This diff is against the library that was included with the
 w3c-httpd-3.0A.tar file:)

----------------------------------------------------------------------

*** HTWriter.c  Fri Jul  5 11:01:42 EDT 1996
--- HTWriter.c.ORIG     Fri Jul  5 10:56:44 EDT 1996
***************
*** 8,15 ****
  
  #include "HTUtils.h"
  #include "tcp.h"
- #include <errno.h> /* for extern int errno */
- #include <strings.h> /* for strerror() */
  
  /*            HTML Object
  **            -----------
--- 8,13 ----
***************
*** 46,52 ****
  #endif
      while (read_pointer < write_pointer) {
          int status;
-       errno = 0;
  #ifdef OLD_CODE
        status = NETWRITE(me->soc, me->buffer,  /* Put timeout? @@@ */
                        write_pointer - read_pointer);
--- 44,49 ----
***************
*** 53,68 ****
  #endif /* OLD_CODE */
        status = NETWRITE(me->soc, read_pointer, write_pointer - read_pointer);
        if (status<0) {
!           if (EINTR == errno) {
!               status = 0;             /* assume the write was interrupted, 
!                                          and no bytes were written */
!           }
!           else {
!               if(TRACE) fprintf(stderr,
!               "HTWrite: Error: write() on socket returns %d (errno %d -- ``%s''!!!\n",
!                                 status, errno, strerror(errno));
!               return;
!           }
        }
        read_pointer += status;
      }
--- 50,58 ----
  #endif /* OLD_CODE */
        status = NETWRITE(me->soc, read_pointer, write_pointer - read_pointer);
        if (status<0) {
!           if(TRACE) fprintf(stderr,
!           "HTWrite: Error: write() on socket returns %d !!!\n", status);
!           return;
        }
        read_pointer += status;
      }
***************
*** 120,141 ****
      }
  #endif
      while (read_pointer < write_pointer) {
!         int status;
! 
!       errno = 0;
!       status = NETWRITE(me->soc, read_pointer,
!                         write_pointer - read_pointer);
        if (status<0) {
!           if (EINTR == errno) {
!               status = 0;             /* assume the write was interrupted, 
!                                          and no bytes were written */
!           }
!           else {
!               if(TRACE) fprintf(stderr,
!               "HTWriter_write: Error on socket output stream!!! (errno %d -- ``%s''\n",
!                                 errno, strerror(errno));
!               return;
!           }
        }
        read_pointer = read_pointer + status;
      }
--- 110,121 ----
      }
  #endif
      while (read_pointer < write_pointer) {
!         int status = NETWRITE(me->soc, read_pointer,
!                       write_pointer - read_pointer);
        if (status<0) {
!           if(TRACE) fprintf(stderr,
!           "HTWriter_write: Error on socket output stream!!!\n");
!           return;
        }
        read_pointer = read_pointer + status;
      }


----------------------------------------------------------------------
Permission form:
----------------------------------------------------------------------

Corrections, Modifications, and Patches

Corrections, modifications, patches and the like will not be 
incorporated into the W3C software distributions unless accompanied by 
the following statement: 

MIT is hereby permitted to distribute these contributions as part of its 
W3C software distributions at no cost to MIT or its licensed users. 

I the undersigned represent that I either own the copyright, or have the 
authority to grant these rights: 

Name		Michael J. McInerny

Title		Sr. Systems Designer

Signature	___________________________________
		(Michael J. McInerny)


Date		___________________________________
		(7/5/96)


Until further notice, please print, sign and send by postal mail to the 
following address: 

	World-Wide Web Consortium
	LCS/MIT 545 Technology Square
	Cambridge, MA 02139
	USA

----------------------------------------------------------------------

Michael McInerny
Sr. System Designer
CLARITECH Corporation
Suite 200A
319 S. Craig St.
Pittsburgh, PA  15213-3726

ph. +1.412.621.0570
fax +1.412.621.0569
e-mail mcinerny@clarit.com

Received on Friday, 5 July 1996 11:29:55 UTC