fwrite() returning ULONG_MAX
I had a strange problem recently... occasionally, one of my FastCGI programs would go into a
near-infinite loop (actually, just looping approximately 18 x 1018 or 18 billion billion times!!). It
was easy to trace the problem back to an fwrite()
call returning ULONG_MAX
(18446744073709551615 on this particular
64-bit server).
Now, the thing about that is this: fwrite()
should always return an integer between 0 and the supplied nmemb
parameter (always much, much less than ULONG_MAX
in this case). So, why is fwrite()
returning any number greater
than nmemb
, let alone one that is so much bigger!!?
Well, any experienced programmer would immediately recognise that ULONG_MAX
is exactly the same as (usigned int)-1
(ie casting -1
to an unsigned integer). So, it would seem likely that fwrite()
is returning -1
at some point...
yet, the man page does not allow for fwrite()
to return -1
, so what's going on?
Well, it turns out that fwrite()
is actually being overridden by the FCGI_fwrite()
function via the
FastCGI library. So, a quick dig through the FCGI_fwrite()
function's source reveals three
different scenarios in which fwrite()
may now return -1
(cast to an unsigned integer, of course).
So, here's some pseudo-code that shows how to correctly handle this:
size_t bytes=fwrite(ptr,size,nmemb,fp);
if ((bytes==(size_t)EOF)||(bytes==(size_t)-1)) {
LogErr("fwrite returned an error.");
if ((!fp->stdio_stream)&&(!fp->fcgx_stream))
LogInfo("Invalid stream.");
else if (fp->fcgx_stream) {
if (fp->fcgx_stream->isClosed)
LogInfo("stream is closed.");
if (fp->fcgx_stream->isReader)
LogInfo("stream is read only.");
}
return false;
}
Here you can see that the return value of fwrite()
is checked against both (size_t)EOF
and (size_t)-1
- both of
which should evaluate to ULONG_MAX
. The reason I test both, even though they should should both be the one and
the same number, is that the FCGI_fwrite()
function returns either (size_t)EOF
or (size_t)-1
depending on where
the error occurred, so it is safer to evaluate both, just in case they do differ on some strange platform (perhaps EOF
is not -1
on some systems?). Anyway, don't let the redundancy bother you, because when the two values are the same
(always?) then any decent compiler will simply optimise out the redundant check anyway :)
As you can see, the above pseudo-code can report three different error cases (the LogInfo()
calls). The first
("invalid stream") is a possible condition in which FCGI_fwrite()
may return (size_t)EOF
. While the other two cases
("stream is closed" and "stream is read only") may be returned by the FCGX_PutStr()
function which may be called
internally by the FCGI_fwrite()
function.
For further reading, you can browse the definitions of the FCGI_fwrite()
and FCGX_PutStr()
functions at:
FCGI_fwrite()
-fcgi_stdio.c
FCGX_PutStr()
-fcgiapp.c
Well that's it, problem sorted ;)
Oh, and in case you were wondering, the reason I was getting these -1
errors in the first place, was due to a client
hastily dropping the network connection before downloads completed... thus I was seeing (and not handling gracefully)
the "stream is closed" scenario :)