Calling System.in.available() fails in win32 if the current JVM process was
created by calling Runtime.exec() from a different JVM process. It throws an
IOException (with no detail message).
This can be demonstrated by compiling and running the attached test case.
Executing the WriteToChild class will exec another JVM to run the DumpIn class,
and then it will write some bytes to that process and then shut down. The
DumpIn class calls available on System.in as well as reading the bytes. All
output and errors are redirected to a file names "dump.out" so that it can be
read after the text case is run.
Doing this with the current JDK 1.2 (or any earlier version, AFAIK) for win32:
C:\test\exec> java WriteToChild
will just create a dump.out file that shows the stack trace of the IOException
from calling available (i.e., the bug). "dump.out" should contain the same
output that is correctly generated when running the following:
C:\test\exec> java WriteOut | java DumpIn
which shows the bytes correctly received.
The problem is in the win32 implementation of sysAvailable() etc. in the file
"src/win32/javavm/runtime/sys_api_md.c". It is called with a fd, and if the
fd is not a regular seekable file in the FS, it calls either stdinAvailable (if
fd == 0) or nonSeekAvailable (otherwise) to find out about bytes pending on
special files like pipes, etc.
These two different functions are required because a different Win32 API
must be used depending on what kind of windows file handle corresponds to
the "special" fd. nonSeekAvailable() calls PeekNamedPipe() for handles to
"pipes", and stdinAvailable() calls WaitForSingleObject() for a handle that
corresponds to "console input".
In the usual case, the correct API is used, because the handle for fd==0
is usually the "console input" if it's not a regular file, and not for other
fds. But in the "WriteToChild" test case above, that is apparently not true:
The fd is zero, so stdinAvailable is called. WaitForSingleObject() returns
the constant WAIT_FAILED and an error condition indicating that the handle was
"invalid", although that return value just falls to the "default:" case in the
switch, and FALSE is returned, resulting in the eventual IOException being
thrown. The documentation for WaitForSingleObject() mentions that its use for
files or pipes can work "in some circumstances" but "this purpose is
discouraged".
The right thing to do would be to call PeekNamedPipe() for this type of handle.
But PeekNamedPipe() doesn't seem work for "console input" handles, and I'm not
sure of a better way of determining which API to call ahead of time. So the
arguably crude but working fix I have is to special case the WAIT_FAILED
return from WaitForSingleObject() and given PeekNamedPipe() a shot (by calling
nonSeekAvailable()). See the "suggested fix" for more details.
created by calling Runtime.exec() from a different JVM process. It throws an
IOException (with no detail message).
This can be demonstrated by compiling and running the attached test case.
Executing the WriteToChild class will exec another JVM to run the DumpIn class,
and then it will write some bytes to that process and then shut down. The
DumpIn class calls available on System.in as well as reading the bytes. All
output and errors are redirected to a file names "dump.out" so that it can be
read after the text case is run.
Doing this with the current JDK 1.2 (or any earlier version, AFAIK) for win32:
C:\test\exec> java WriteToChild
will just create a dump.out file that shows the stack trace of the IOException
from calling available (i.e., the bug). "dump.out" should contain the same
output that is correctly generated when running the following:
C:\test\exec> java WriteOut | java DumpIn
which shows the bytes correctly received.
The problem is in the win32 implementation of sysAvailable() etc. in the file
"src/win32/javavm/runtime/sys_api_md.c". It is called with a fd, and if the
fd is not a regular seekable file in the FS, it calls either stdinAvailable (if
fd == 0) or nonSeekAvailable (otherwise) to find out about bytes pending on
special files like pipes, etc.
These two different functions are required because a different Win32 API
must be used depending on what kind of windows file handle corresponds to
the "special" fd. nonSeekAvailable() calls PeekNamedPipe() for handles to
"pipes", and stdinAvailable() calls WaitForSingleObject() for a handle that
corresponds to "console input".
In the usual case, the correct API is used, because the handle for fd==0
is usually the "console input" if it's not a regular file, and not for other
fds. But in the "WriteToChild" test case above, that is apparently not true:
The fd is zero, so stdinAvailable is called. WaitForSingleObject() returns
the constant WAIT_FAILED and an error condition indicating that the handle was
"invalid", although that return value just falls to the "default:" case in the
switch, and FALSE is returned, resulting in the eventual IOException being
thrown. The documentation for WaitForSingleObject() mentions that its use for
files or pipes can work "in some circumstances" but "this purpose is
discouraged".
The right thing to do would be to call PeekNamedPipe() for this type of handle.
But PeekNamedPipe() doesn't seem work for "console input" handles, and I'm not
sure of a better way of determining which API to call ahead of time. So the
arguably crude but working fix I have is to special case the WAIT_FAILED
return from WaitForSingleObject() and given PeekNamedPipe() a shot (by calling
nonSeekAvailable()). See the "suggested fix" for more details.