Export to GitHub

lanterna - issue #44

Blocking readInput


Posted on Oct 5, 2012 by Grumpy Rabbit

I noticed the readInput function is non-blocking. So, in a loop the CPU usage reaches 50% while the program waits for user presses a key. Does a way exist to wait for a press key action by a sort of blocking readInput function?

Comment #1

Posted on Oct 7, 2012 by Grumpy Ox

The way the input system is built, it's not very easy to add a 'truly' blocking method that is using the underlying input stream for blocking I/O. I'd rather not add a method that simulates blocking input, since it's quite easy to do this yourself:

Key key = terminal.readInput(); while(key == null) { Thread.sleep(1); key = terminal.readInput(); }

This should do the trick and shouldn't consume any CPU. If sleeping for 1 ms is too much, you can probably try Thread.yield() instead.

Comment #2

Posted on Oct 7, 2012 by Grumpy Rabbit

I tough use the sleep method to solve this issue. I was afraid a lack of responsiveness with this solution. I'll try the yield method. Is this solution you've chosen in the GUI layer?

Thanks for your answer and help.

Chris

Le 7 oct. 2012 à 05:19, lanterna@googlecode.com a écrit :

Comment #3

Posted on Oct 8, 2012 by Grumpy Ox

No, I'm doing sleep(1):

if(!repainted) { try { Thread.sleep(1); } catch(InterruptedException e) {} }

I did a quick test and it seems both Thread.yield() and Thread.sleep(0) will cause one CPU core to max load. Thread.sleep(0, 1) (and of course Thread.sleep(1) ) seemed to work though.

public class YieldTest { public static void main(String[] args) throws InterruptedException { while(true) { Thread.sleep(0, 1); } } }

Comment #4

Posted on Oct 8, 2012 by Grumpy Rabbit

Fine, thanks you :)

Christian

Le 8 oct. 2012 à 03:32, lanterna@googlecode.com a écrit :

Comment #5

Posted on Jun 19, 2013 by Massive Panda

Do I understand correctly? It is your intention never to add a blocking version of readInput?

This seems like a fundamental design flaw to me, since every individual application will now need to pick a sleep value, and make a decision to trade off responsiveness for CPU usage. Particularly in virtualized environments, where machine resources are over-committed, and one machine can be running dozens of OSes, each with dozens of idle applications, it is not appropriate to have every application on the system wake up 1000 times per second just to poll for input and discover that it has nothing to do.

One easy possibility would be to add a blocking readInput call that is initially implemented using the sleep technique you descrive. That way, Lanterna at least appears to offer a blocking call, and you leave the door open in the future if you change your mind and want to offer a better implementation.

Comment #6

Posted on Jun 24, 2013 by Grumpy Ox

True, but if you use blocking I/O then the screen won't be able to redraw/refresh the screen until there is input available. I chose this polling approach mostly because I didn't want to multi-thread it (and also, for my purposes of writing this library, sleep(1) wasn't a problem).

However, with the next major revision of Lanterna, I'm trying to make the whole stack thread-safe and could probably put both input and output on separate threads; truly enabling blocking input. But then again, there would be another thread doing polling of the input queue to find out if there's anything new...

Comment #7

Posted on Sep 2, 2014 by Grumpy Kangaroo

I believe that this is a very, very, very important feature for any server application using lanterna. I am evaluating using it myself (kudos btw, nice lib so far!) and the only two stumbling blocks so far have been the privateness of the util functions to en/disable local echo, linemode etc. in TelnetTerminal -- and the lack of a blocking readInput.

I'd suggest using the current read(buf,off,len) implementation of the InputStream derived guy in TelnetTerminal, adding a nonblocking parameter to it, and change the fillBuffer() stuff from:

if(inputStream.available() > 0) { fillBuffer(); } if(bytesInBuffer == 0) { return -1; }

to:

if (inputStream.availabe() > 0 || !nonblocking) { fillBuffer(); } while (bytesInBuffer == 0) { if (nonblocking) return -1; else fillBuffer(); }

with appropriate

then, implement read(buf, off, len) by either read(buf, off, len, true) or read(buf, off, len, false), and make the four-argument version public. Also, implement read() by read(_, _, _, false).

Rationale: Yes, this blocks the thread. But usually there's two sides to a socket connection you are interested in. There's the reader side and the writer side. There's potentially four kinds of interactions that I see (use-case wise): server-push: write, client-push: read, client-request-reply: read-and-then-write, server-client-reply: write-and-then-read.

In server-push, obviously, I know that I want to push. In client-push, I'd like to be notified, and then react. In client-request-reply, I'd like to be notified, and then react (and reply). In server-client-reply, I'd like to write, and then be notified of an answer.

This also goes along with an architecture that gives each connection one thread to play on. The Terminal (and Screen) instances there just live on that one thread. My computational loop(s) live on (an)other thread(s). Input from the threads are transported to the other threads in a thread-safe manner already anyways. I'd like to be able to just sleep on the client, waiting on input. If the computational loop decides it wants to push something, it would just interrupt the thread (the read from a socket's input stream should throw an InterruptedIOException as suggested by the (closed) bugreport at http://bugs.java.com/view_bug.do?bug_id=4905626 ), I'd handle the InterruptedIOException in my client, knowing that I'd be pushing something next; push; and return to my read-block-sleep.

With this approach, don't you think this is suitable for implementation? (I'll be trying, but thought I'd ask if you, being familiar with the code obviously, see a glaring problem with it). It should be giving both blocking and non-blocking I/O depending on your needs and design (1:1 thread:telnetTerminal or 1:n thread:telnetTerminal).

Comment #8

Posted on Sep 21, 2014 by Grumpy Ox

Sounds reasonable. Given that you are mentioning TelnetTerminal, are you using Lanterna 3 by any chance?

Comment #9

Posted on Jul 12, 2015 by Grumpy Ox

This issue has been closed because Google Code is shutting down shortly and all issues have been migrated, along with the rest of the project, to GitHub. However, notifications will probably not work when updates are made to this issue on GitHub so for future updates, please visit https://github.com/mabe02/lanterna/issues and subscribe to further updates there.

Thank you, Martin

Status: Duplicate

Labels:
Type-Defect Priority-Medium