Non-blocking console input C++
Asked Answered
S

13

48

I'm looking for a (multiplatform) way to do non-blocking console input for my C++ program, so I can handle user commands while the program continually runs. The program will also be outputting information at the same time.

What's the best/easiest way to do this? I have no problem using external libraries like boost, as long as they use a permissive license.

Sunday answered 29/5, 2011 at 23:34 Comment(5)
Could an ordinary thread library work for you?Caducous
@Steinbitglis: What is an "ordinary" thread library, and how does it differ from any others?Skipper
@Tomalak I think he meant a threading library, a not non-blocking IO library.Ricker
possible duplicate of Non-blocking stdioScandalmonger
Look here. I find this solution is the simplest and works: https://mcmap.net/q/371873/-c-input-and-output-to-the-console-window-at-the-same-timeParrish
R
13

I would do this by creating separate a thread which calls normal blocking IO functions and pass it a callback function which it would call when it got input. Are you sure you need to do what you said you want to do?

As for outputting information at the same time, what would happen if the user was in the middle of typing some input and you printed something?

Ricker answered 29/5, 2011 at 23:42 Comment(5)
What would happen if I had one thread waiting for say.. cin to handle input and then another thread used cout to output the the console? Would that end badly?Sunday
@Sunday You shouldn't really be outputting and inputting to/from the console from different threads (unless you want to have some synchronization object to keep them all in line, which would probably make the reason you were using threading in the first place go down the drain). I haven't tried it before, but I imagine that if you print something from one thread while another thread is waiting for input and the user hits enter, the one waiting for input will get what the other one outputted as part of the input. So things can get messy. Like I said, I haven't tried it though.Ricker
@Doug: That would be fine. Just make sure both threads to not try to use the same stream concurrently.Lurlene
I ended up using threading and input and output queues with a pair of mutexes. The thread handles all console i/o.Sunday
This is sufficient as long as the input thread is always terminated as a consequence of input. If the program must be able to terminate independent of console input then this would necessarily result in either a deadlock or a thread termination.Gaudy
F
22

Example using C++11:

#include <iostream>
#include <future>
#include <thread>
#include <chrono>

static std::string getAnswer()
{    
    std::string answer;
    std::cin >> answer;
    return answer;
}

int main()
{

    std::chrono::seconds timeout(5);
    std::cout << "Do you even lift?" << std::endl << std::flush;
    std::string answer = "maybe"; //default to maybe
    std::future<std::string> future = std::async(getAnswer);
    if (future.wait_for(timeout) == std::future_status::ready)
        answer = future.get();

    std::cout << "the answer was: " << answer << std::endl;
    exit(0);
}

online compiler: https://rextester.com/GLAZ31262

Foochow answered 12/12, 2017 at 14:27 Comment(10)
Didn't know about 'future'. This is the way to do non-blocking I/OUrial
This solution is actually misleading. std::cin in getAnswer is still blocking but you don't notice this because you call it from main and then call exit. Exit cleans up threads and flushes/closes streams. If you were to move almost the entire body from main into another function that gets called by main - as in this example: rextester.com/DIWL84093 - you will see that the function that uses a future never returns. Because std::cin still blocks and prevents that future from returning.Polonium
To clarify Ryan Jarvis' comment (because I was a bit confused): if the future.wait_for() call times out, the call to std::cin is not cancelled - it's still pending. To genuinely execute the main thread to completion without calling exit(), something must be returned from the std::cin call, or the thread will block at the point when the future object is destroyed. Putting braces around the future line and its corresponding if-statement demonstrates this.Standin
This solution makes me wondering if it wouldn't be doable to spawn a new thread with a function to execute in which a blocking std::cin exists. It handles the input in another thread so that the main thread can run freely like e.g. in a while(running){} loop and "running" could be std::atomic. The threaded function can set running to false (when the user types "quit" e.g.) to tell itself and main to exit (and join properly). Would this be feasible? Any pitfalls?Unorthodox
To provide a solution to what Ryan is saying: use std::string input; std::readLine(std::cin, input);Nealey
@Nealey Unfortunately, it dit not work. See here rextester.com/SVVORC19799Empiric
I know why the code in rextester.com/SVVORC19799 does not work. Because the future object will wait until it is ready when future object deconstruct. So It will block in function AsyncGetLine.Empiric
@Empiric you're correct, you cannot block the thread that reads cin. Make sure cin is the only thing happening on that thread (preferably main thread -- think same as making a GUI) and use std::this_thread::sleep_for(std::chrono::milliseconds(500)); to give control of the thread back. Do other tasks on other threads. Please see my answer on this post for an example.Nealey
It does not matter whether the read is wrapped with std::getline, std::cin blocks until it has input. Terminating a blocking thread is not non-blocking, it's corrupting.Gaudy
This answer causes a dead-lock on exit() starting with glibc 2.38 (Ubuntu 23.10). The only solution is to poll for input using poll() with a low timeout. (don't use select() if you plan to open more than 1024 files). See: sourceware.org/bugzilla/show_bug.cgi?id=15142Polymerize
R
13

I would do this by creating separate a thread which calls normal blocking IO functions and pass it a callback function which it would call when it got input. Are you sure you need to do what you said you want to do?

As for outputting information at the same time, what would happen if the user was in the middle of typing some input and you printed something?

Ricker answered 29/5, 2011 at 23:42 Comment(5)
What would happen if I had one thread waiting for say.. cin to handle input and then another thread used cout to output the the console? Would that end badly?Sunday
@Sunday You shouldn't really be outputting and inputting to/from the console from different threads (unless you want to have some synchronization object to keep them all in line, which would probably make the reason you were using threading in the first place go down the drain). I haven't tried it before, but I imagine that if you print something from one thread while another thread is waiting for input and the user hits enter, the one waiting for input will get what the other one outputted as part of the input. So things can get messy. Like I said, I haven't tried it though.Ricker
@Doug: That would be fine. Just make sure both threads to not try to use the same stream concurrently.Lurlene
I ended up using threading and input and output queues with a pair of mutexes. The thread handles all console i/o.Sunday
This is sufficient as long as the input thread is always terminated as a consequence of input. If the program must be able to terminate independent of console input then this would necessarily result in either a deadlock or a thread termination.Gaudy
F
7

I've done this on QNX4.5 that doesn't support threads or Boost by using select. You basically pass select STDIN as the file descriptor to use and select will return when a new line is entered. I've added a simplified example loop below. It's platform independent, at least for Unix like systems. Not sure about Windows though.

while (!g_quit)
{
   //we want to receive data from stdin so add these file
   //descriptors to the file descriptor set. These also have to be reset
   //within the loop since select modifies the sets.
   FD_ZERO(&read_fds);
   FD_SET(STDIN_FILENO, &read_fds);

   result = select(sfd + 1, &read_fds, NULL, NULL, NULL);
   if (result == -1 && errno != EINTR)
   {
      cerr << "Error in select: " << strerror(errno) << "\n";
      break;
   }
   else if (result == -1 && errno == EINTR)
   {
      //we've received and interrupt - handle this
      ....
   }
   else
   {
      if (FD_ISSET(STDIN_FILENO, &read_fds))
      {
         process_cmd(sfd);
      }
   }
}
Fatling answered 29/5, 2011 at 23:55 Comment(3)
Select is my favorite. Could run on cygwin or minsys lib on windows. It must work, I think. I'll give it a try and post the result.Gussiegussman
@NadavB it is of type fd_set.Shorttempered
On Windows you can use !_isatty(_fileno(__acrt_iob_func(0))); to do this. For non-production code you could also use _kbhit & _getch, although those are heavily deprecated.Implant
L
5

There is one easy way:

char buffer[512];
int point = 0;
...
while (_kbhit()) {
    char cur = _getch();
    if (point > 511) point = 511;
    std::cout << cur;
    if (cur != 13) buffer[point++] = cur;
    else{
        buffer[point] = '\0';
        point = 0;
        //Run(buffer);
    }
}

No block, all in 1 thread. As for me, this works.

Longo answered 4/5, 2015 at 8:52 Comment(1)
problem: thats just a hack, _kbhit() returns only true if you use your hardware keyboard. If the input to your program comes from another process then _kbhit() blocks.Bacon
S
4

Non-blocking console input C++ ?

Ans: do console IO on a background thread and provide a means of communicating between threads.

Here's a complete (but simplistic) test program that implements async io by deferring the io to a background thread.

the program will wait for you to enter strings (terminate with newline) on the console and then perform a 10-second operation with that string.

you can enter another string while the operation is in progress.

enter 'quit' to get the program to stop on the next cycle.

#include <iostream>
#include <memory>
#include <string>
#include <future>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <deque>

int main()
{
    std::mutex m;
    std::condition_variable cv;
    std::string new_string;
    bool error = false;

    auto io_thread = std::thread([&]{
        std::string s;
        while(!error && std::getline(std::cin, s, '\n'))
        {
            auto lock = std::unique_lock<std::mutex>(m);
            new_string = std::move(s);
            if (new_string == "quit") {
                error = true;
            }
            lock.unlock();
            cv.notify_all();
        }
        auto lock = std::unique_lock<std::mutex>(m);
        error = true;
        lock.unlock();
        cv.notify_all();
    });

    auto current_string = std::string();
    for ( ;; )
    {
        auto lock = std::unique_lock<std::mutex>(m);
        cv.wait(lock, [&] { return error || (current_string != new_string); });
        if (error)
        {
            break;
        }
        current_string = new_string;
        lock.unlock();

        // now use the string that arrived from our non-blocking stream
        std::cout << "new string: " << current_string;
        std::cout.flush();
        for (int i = 0 ; i < 10 ; ++i) {
            std::this_thread::sleep_for(std::chrono::seconds(1));
            std::cout << " " << i;
            std::cout.flush();
        }
        std::cout << ". done. next?\n";
        std::cout.flush();
    }
    io_thread.join();
    return 0;
}

sample test run:

$ ./async.cpp
first
new string: first 0 1las 2t 3
 4 5 6 7 8 9. done. next?
new string: last 0 1 2 3 4 5 6 7 8quit 9. done. next?
Shift answered 5/4, 2016 at 13:19 Comment(2)
how would you signal the io_thread to quit without "quit"? getline is blockingCarricarriage
@Carricarriage If my requirements were more complex than this, i'd probably use some platform-specific code for the I/O.Shift
S
2

ncurses can be a good candidate.

Samul answered 29/5, 2011 at 23:51 Comment(1)
provide full answerMongrel
G
0

The StdinDataIO class of the BSD-licensed MUSCLE networking library supports non-blocking reads from stdin under Windows, MacOS/X, and Linux/Unix ... you could use that (or just examine the code as an example of how it can be done) if you want.

Groos answered 30/5, 2011 at 20:57 Comment(0)
D
0

You can use the tinycon library to do this. Just spawn a tinycon object in a new thread, and you are pretty much done. You can define the trigger method to fire off whatever you'd like when enter is pressed.

You can find it here: https://sourceforge.net/projects/tinycon/

Also, the license is BSD, so it will be the most permissive for your needs.

Deel answered 1/4, 2013 at 19:41 Comment(0)
C
0

libuv is a cross-platform C library for asynchronous I/O. It uses an event loop to do things like read from standard input without blocking the thread. libuv is what powers Node.JS and others.

Carolyn answered 27/12, 2015 at 4:28 Comment(0)
S
0

In a sense, this answer is incomplete. But yet, I think it can be useful even for people who have different platforms or circumstances, giving the idea, what to look for in their platform.

As I just wrote some scripting engine integration into an SDL2 main event loop (which is supposed to read lines from stdin if there are lines to be read), here is how I did it (on linux (debian bullseye 64 bit)). See below.

But even if you are not on linux, but on some other posix system, you can use the equivalent platform APIs of your platform. For example, you can use kqueue on FreeBSD. Or you can consider using libevent for a bit more portable approach (still will not really work on Windows).

This approach might also work on Windows if you do some special fiddling with the rather new-ish ConPTY. In traditional windows console applications, the problem is, that stdin is not a real file handle and as such, passing it to libevent or using IOCP (IO completion ports) on it will not work as expected.

But, this approach should also work on posix systems, if there is redirection at play. As long as there is a file handle available.

So how does it work?

  1. Use epoll_wait() to detect if there is data available on stdin. While consoles can be configured in all sorts of ways, typically, they operate on a line by line basis (should also apply for ssh etc.).
  2. Use your favorite getline() function to read the line from stdin. Which will work, because you know, there is data and it will not block (unless your console is not defaulting to line by line handling).
  3. Rince and repeat.
#include <unistd.h>
#include <sys/epoll.h>
#include <iostream>
#include <string>
#include <array>

using EpollEvent_t = struct epoll_event;

int main(int argc, const char* argv[]) {
  //
  // create epoll instance
  //
  int epollfd = epoll_create1(0);
  if (epollfd < 0) {
    std::cout << "epoll_create1(0) failed!" << std::endl;
    return -1;
  }

  //
  // associate stdin with epoll
  //
  EpollEvent_t ev;
  ev.data.ptr = nullptr;
  ev.data.fd = STDIN_FILENO; // from unistd.h
  ev.data.u32 = UINT32_C(0);
  ev.data.u64 = UINT64_C(0);
  ev.events = EPOLLIN;
  if (epoll_ctl(epollfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev) < 0) {
    std::cout
      << "epoll_ctl(epollfd, EPOLL_CTL_ADD, fdin, &ev) failed."
      << std::endl;
    return -1;
  }

  //
  // do non-blocking line processing in your free running
  // main loop
  //
  std::array<EpollEvent_t,1> events;
  bool running = true;
  while (running) {
    int waitret = epoll_wait(epollfd,
                 events.data(),
                 events.size(),
                 0); // 0 is the "timeout" we want
    if (waitret < 0) {
      std::cout << "epoll_wait() failed." << std::endl;
      running = false;
    }
    if (0 < waitret) { // there is data on stdin!
      std::string line;
      std::getline(std::cin, line);
      std::cout
    << "line read: [" << line << "]" << std::endl;
      if (line == "quit")
    running = false;
    }

      // ... Do what you usually do in your main loop ...
  }

  //
  // cleanup of epoll etc.
  //
  close(epollfd);
    
    
  return 0;
}

Scharf answered 18/10, 2021 at 3:16 Comment(0)
N
0

You could do:

#include <thread>
#include <chrono>
#include <string>
#include <iostream>



int main() {

    std::cout << "Type exit to quit." << std::endl;

    // initialize other std::thread handlers here 

    std::string input;
    
    while (input != "exit") {
        std::getline(std::cin, input);
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
    }

    std::cout << "Cleaning up and quitting" << std::endl;

    return 0;
};

Nealey answered 1/12, 2021 at 1:3 Comment(0)
G
0

A simple answer with thread/future and reading a single char at a time (you can replace getchar with cin as required)

Timeout is set to zero and a new future is created every time the previous call is completed. Like cin, getchar requires that the user hits the RETURN key to end the function call.

#include <chrono>
#include <cstdio>
#include <future>
#include <iostream>
#include <thread>

static char get_usr_in()
{
  return std::getchar();
}

int main()
{
  std::chrono::seconds timeout(0);
  std::future<char> future = std::async(std::launch::async, get_usr_in);
  char ch = '!';
  while(ch!='q') {
    if(future.wait_for(timeout) == std::future_status::ready) {
      ch = future.get();
      if(ch!='q') {
        future = std::async(std::launch::async, get_usr_in);
      }
      if(ch >= '!' && ch <'~')
        std::cout << "ch:" << ch << std::endl;
    }
    std::cout << "." << std::endl;
  }
  exit(0);
}
Gunstock answered 12/9, 2022 at 17:13 Comment(0)
F
0

Why not use promises?

#include <iostream>
#include <istream>
#include <thread>
#include <future>
#include <chrono>

void UIThread(std::chrono::duration<int> timeout) {
    std::promise<bool> p;

    std::thread uiWorker([&p]() {
        bool running = true;
        while(running) {
            std::string input;
            std::cin >> input;
            if(input == "quit") {
                p.set_value(true);
                running = false;
            }
        }
    });

    auto future = p.get_future();
    if (future.wait_for(timeout) != std::future_status::ready) {
        std::cout << "UI thread timed out" << std::endl;
        uiWorker.detach();
        return;
    }

    uiWorker.join();
}

int main()
{
    std::thread uiThread(UIThread, std::chrono::seconds(3));

    std::cout << "Waiting for UI thread to complete" << std::endl;
    uiThread.join();
}

online complier

Funch answered 21/10, 2022 at 5:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.