Making System.in interruptible, how?

A

Arne Vajhøj

Found the following nice article, highlighting how
it would be possible to make System.in interrupible:

http://www.javaspecialists.eu/archive/Issue153.html

Is this the preferred way to do? i.e. to use some
polling, or are there other ways around?

I am not too comfortable with that code.

It seems to work on Windows with 64 bit SUN Java 1.6.

May guess is that it will work in most other contexts.

But is it required to work?

The core logic in the code is:

while (!br.ready()) {
Thread.sleep(200);
}

The docs says:

<doc>

BufferedReader ready

Tells whether this stream is ready to be read. A buffered character
stream is ready if the buffer is not empty, or if the underlying
character stream is ready.

....

True if the next read() is guaranteed not to block for input, false
otherwise. Note that returning false does not guarantee that the next
read will block.

InputStreamReader ready

Tells whether this stream is ready to be read. An InputStreamReader is
ready if its input buffer is not empty, or if bytes are available to be
read from the underlying byte stream.

....

True if the next read() is guaranteed not to block for input, false
otherwise. Note that returning false does not guarantee that the next
read will block.

InputStream available

Returns an estimate of the number of bytes that can be read (or skipped
over) from this input stream without blocking by the next invocation of
a method for this input stream. The next invocation might be the same
thread or another thread. A single read or skip of this many bytes will
not block, but may read or skip fewer bytes.

Note that while some implementations of InputStream will return the
total number of bytes in the stream, many will not. It is never correct
to use the return value of this method to allocate a buffer intended to
hold all data in this stream.

....

an estimate of the number of bytes that can be read (or skipped over)
from this input stream without blocking or 0 when it reaches the end of
the input stream.

</doc>

It is not clear to me that:
1) System.in.available will in fact return a reliable number
("estimate" sounds a bit flexible)
2) BufferedReader ready will only return true if a full line
is available and not eat any characters if that is not the case

I would seriously consider writing some JNI to do what is necessary on
the relevant platforms.

It is not portable, but at least it is obvious how it works.

Arne
 
J

Jan Burse

Arne said:
2) BufferedReader ready will only return true if a full line
is available and not eat any characters if that is not the case

I guess that is more a freature of the underlying console,
if echo is off and the console is in line editing mode.

For example I can do the following:

abc<backspace>def

And I will get from BufferedReader abdef and not
abc<backspace>def. But there is no logic in BufferedReader
for line editing. There is only some skip LF logic
in the buffered reader.

Also the buffered reader will delegate the ready()
to the underlying stream.
The core logic in the code is:

while (!br.ready()) {
Thread.sleep(200);
}

Yep, but I have a bad feeling concerning the above
logic. What if the input of the console is redirected,
and some file should be processed. Then the sleep
will throttle the input considerable.

Best would be if the stream underlying BufferedReader
would be interruptible. That is if System.in or
a substitute would be interruptible.
I would seriously consider writing some JNI to do
what is necessary on the relevant platforms.

It is not portable, but at least it is obvious how it works.

Or maybe some sun.misc.* stuff. Which is a little bit
more widespread, and can be used via reflection, so
that when sun.misc.* is present interruptibility could
be provided, and otherwise the stream would not be
interruptible as before.

Unfortunately the following does also not yield an
interruptible console stream:

Console console = System.console();
Reader reader = console.reader();

The reader there is created as follows:

reader = new LineReader(StreamDecoder.forInputStreamReader(
new FileInputStream(FileDescriptor.in),
readLock,
cs));

So maybe something can be done with FileDescriptor.in?

There is a hack documented here:

Here is a way to get a NIO FileChannel from System.in
http://stackoverflow.com/a/808795/502187


/* unravels all layers of FilterInputStream wrappers to get to the
* core InputStream
*/
public static InputStream extract(InputStream in)
throws NoSuchFieldException, IllegalAccessException {

Field f = FilterInputStream.class.getDeclaredField("in");
f.setAccessible(true);

while( in instanceof FilterInputStream )
in = (InputStream)f.get((FilterInputStream)in);

return in;
}


Maybe I can get the channel without reflection from
the file descriptor? Actually this is possible:

FileInputStream fi = new FileInputStream(FileDescriptor.in);
final Thread thread = Thread.currentThread();
new Thread() {
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException x) {
throw new RuntimeException(x);
}
thread.interrupt();
}
}.start();
System.out.print("test: ");
System.out.flush();
ByteBuffer buf = ByteBuffer.allocate(1024);
try {
fi.getChannel().read(buf);
} catch (IOException x) {
throw new RuntimeException(x);
}

If run it I get:

test:
....
Caused by: java.nio.channels.ClosedByInterruptException
at
java.nio.channels.spi.AbstractInterruptibleChannel.end(AbstractInterruptibleChannel.java:202)
at sun.nio.ch.FileChannelImpl.read(FileChannelImpl.java:148)
... 5 more

Bye
 
J

Jan Burse

Dear All,

Just checking it on Mac OS. The exception is different
between version 1.6 and 1.7 of the JDK:

java -jar interpreter.jar
test1: abc
test2: Exception in thread "main" java.nio.channels.ClosedChannelException
at sun.nio.ch.FileChannelImpl.ensureOpen(FileChannelImpl.java:88)
at sun.nio.ch.FileChannelImpl.read(FileChannelImpl.java:132)

/Library/Java/JavaVirtualMachines/1.7.0u.jdk/Contents/Home/bin/java
test1:
Exception in thread "main" java.nio.channels.ClosedByInterruptException
at
java.nio.channels.spi.AbstractInterruptibleChannel.end(AbstractInterruptibleChannel.java:202)
at sun.nio.ch.FileChannelImpl.read(FileChannelImpl.java:148)

And there is a glitch, the interrupt is only recognized
after pressing return.
 
J

Jan Burse

Jan said:
Dear All,

Found the following nice article, highlighting how
it would be possible to make System.in interrupible:

http://www.javaspecialists.eu/archive/Issue153.html

Is this the preferred way to do? i.e. to use some
polling, or are there other ways around?

Best Regards

I have finally adopted the ready() solution. I guess that
if the input stream is redirected to a file, the ready()
will mostly return true, so that performance is no issue.

An issue I had was that I wanted also the possibility that
the input stream is hitchiked by an INT handler. The following
worked for me:

try {
for (;;) {
notPauseScreen();
if (!super.ready()) {
Thread.sleep(SLEEP);
} else {
return super.read(cbuf, offset, length);
}
}
} catch (InterruptedException x) {
throw new InterruptedIOException();
}

The INT handler will pause the screen. So when the screen is
paused neither ready nor read are called. And the INT handler
can call read on System.in, so as to query the end user for
some action.

When the INT handler has determined the action, it can
resume the screen. And the normal application will continue
getting input.

Bye
 
A

Arne Vajhøj

I guess that is more a freature of the underlying console,
if echo is off and the console is in line editing mode.

For example I can do the following:

abc<backspace>def

And I will get from BufferedReader abdef and not
abc<backspace>def. But there is no logic in BufferedReader
for line editing. There is only some skip LF logic
in the buffered reader.

Also the buffered reader will delegate the ready()
to the underlying stream.

Yes.

That was stated in some of what you chose not to quote.

It is not sufficient to guarantee success.
Yep, but I have a bad feeling concerning the above
logic. What if the input of the console is redirected,
and some file should be processed. Then the sleep
will throttle the input considerable.

No.

A file will b ready.
Or maybe some sun.misc.* stuff. Which is a little bit
more widespread, and can be used via reflection, so
that when sun.misc.* is present interruptibility could
be provided, and otherwise the stream would not be
interruptible as before.

I would still prefer JNI.

If someone get the app on a new platform they can just
implement the C code and then it works.

If using sun.* code, then on a new platform not based
on SUN/Oracle code there is no easy fix.

Arne
 
A

Arne Vajhøj

I have finally adopted the ready() solution. I guess that
if the input stream is redirected to a file, the ready()
will mostly return true, so that performance is no issue.

File is not the problem.

The problem is that it dos not seem to be guaranteed to
work.

Arne
 
J

Jan Burse

Arne said:
File is not the problem.

The problem is that it dos not seem to be guaranteed to
work.

Arne

Problem with Echo
-----------------

Yep, doesn't work on Windows 7 / JDK 1.7. Somehow
inside cmd.exe the ready does not echo. So I enter
a line, done see anything during editing the line,
and then when I hit return the line gets read.

On Mac OS / JDK 1.7 in the standard terminal
and in xterm it works fine. During a ready call,
everything I type is echoed, a line can fully be
edited, and then when hitting return the line
gets read.

Damned

Now checking linux, and other JDKs.

Bye
 
J

Jan Burse

Hi,

Dirty Dirty, the StreamDecoder suppresses IOExceptions:

private boolean inReady() {
try {
return (((in != null) && (in.available() > 0))
|| (ch instanceof FileChannel));
// ## RBC.available()?
} catch (IOException x) {
return false;
}
}

What if an InterruptedIOException happens in.available()?
The IOExceptions is suppressed, but the interrupt flag
is not set again.

Bye
 
A

Arne Vajhøj

Yep, doesn't work on Windows 7 / JDK 1.7. Somehow
inside cmd.exe the ready does not echo. So I enter
a line, done see anything during editing the line,
and then when I hit return the line gets read.

On Mac OS / JDK 1.7 in the standard terminal
and in xterm it works fine. During a ready call,
everything I type is echoed, a line can fully be
edited, and then when hitting return the line
gets read.

My advice is still: either do it true cross platform
in pure Java and stay away from anything that is not
clearly documented to work, or use JNI to call some
platform specific C code that does exactly what you want.

In the first case, then it just works everywhere. The
cost is that the UI may be a bit "dumb".

In the second case it is very well defined what has to
be done to get the code working on a new platform.

Using not well defined behavior will eventually create
very painfully experiences when it stop working due
to: new platform, OS upgrade, Java upgrade etc..

Arne
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,008
Latest member
HaroldDark

Latest Threads

Top