How do you do nonblocking console IO on Linux/OS X in C?
Sometimes it's convenient to have I/O that doesn't block i.e we don't want a read call to block on one in case of input from the other. Solution for this is the given function: To specify non-blocking option: #include<fcntl. h> int fd; fcntl(fd, F_SETFL, O_NONBLOCK);
For network socket i/o, when it is "ready", it don't block. That's what the O_NONBLOCK and "ready" means. For disk i/o, we have posix aio, linux aio, sendfile and friends. Follow this answer to receive notifications.
Succinctly, both read() and write() can be blocking or non-blocking, depending on circumstances.
By default, read() waits until at least one byte is available to return to the application; this default is called “blocking” mode. Alternatively, individual file descriptors can be switched to “non-blocking” mode, which means that a read() on a slow file will return immediately, even if no bytes are available.
I want to add an example:
#include <unistd.h> #include <fcntl.h> #include <stdio.h> int main(int argc, char const *argv[]) { char buf[20]; fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK); sleep(4); int numRead = read(0, buf, 4); if (numRead > 0) { printf("You said: %s", buf); } }
When you run this program you have 4 seconds to provide input to standard in. If no input found, it will not block and will simply return.
2 sample executions:
Korays-MacBook-Pro:~ koraytugay$ ./a.out fda You said: fda Korays-MacBook-Pro:~ koraytugay$ ./a.out Korays-MacBook-Pro:~ koraytugay$
Like Pete Kirkham, I found cc.byexamples.com, and it worked for me. Go there for a good explanation of the problem, as well as the ncurses version.
My code needed to take an initial command from standard input or a file, then watch for a cancel command while the initial command was processed. My code is C++, but you should be able to use scanf()
and the rest where I use the C++ input function getline()
.
The meat is a function that checks if there is any input available:
#include <unistd.h> #include <stdio.h> #include <sys/select.h> // cc.byexamples.com calls this int kbhit(), to mirror the Windows console // function of the same name. Otherwise, the code is the same. bool inputAvailable() { struct timeval tv; fd_set fds; tv.tv_sec = 0; tv.tv_usec = 0; FD_ZERO(&fds); FD_SET(STDIN_FILENO, &fds); select(STDIN_FILENO+1, &fds, NULL, NULL, &tv); return (FD_ISSET(0, &fds)); }
This has to be called before any stdin input function When I used std::cin
before using this function, it never returned true again. For example, main()
has a loop that looks like this:
int main(int argc, char* argv[]) { std::string initialCommand; if (argc > 1) { // Code to get the initial command from a file } else { while (!inputAvailable()) { std::cout << "Waiting for input (Ctrl-C to cancel)..." << std::endl; sleep(1); } std::getline(std::cin, initialCommand); } // Start a thread class instance 'jobThread' to run the command // Start a thread class instance 'inputThread' to look for further commands return 0; }
In the input thread, new commands were added to a queue, which was periodically processed by the jobThread
. The inputThread
looked a little like this:
THREAD_RETURN inputThread() { while( !cancelled() ) { if (inputAvailable()) { std::string nextCommand; getline(std::cin, nextCommand); commandQueue.lock(); commandQueue.add(nextCommand); commandQueue.unlock(); } else { sleep(1); } } return 0; }
This function probably could have been in main()
, but I'm working with an existing codebase, not against it.
For my system, there was no input available until a newline was sent, which was just what I wanted. If you want to read every character when typed, you need to turn off "canonical mode" on stdin. cc.byexamples.com has some suggestions which I haven't tried, but the rest worked, so it should work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With