Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

making auto timeout with std::cin statement

I wrote the program

#include<iostream>
using namespace std;
int n;
int main(int argc, char *argv[])
{
  std::cout << "Before reading from cin" << std::endl;

   // Below reading from cin should be executed within stipulated time
   bool b=std::cin >> n;
   if (b)
      std::cout << "input is integer for n and it's correct" << std::endl;
   else
      std::cout << "Either n is not integer or no input for n" << std::endl;
  return 0;
}

Here std::cin statement would wait for the console for input and will go for sleep mode until we provide some input and press Enter.

I want std::cin statement to go for timeout after 10 seconds(if user doesn't enter any data between 10 seconds then compiler would start executing the next statement of the program present below std::cin statement.

I am able to solve it using multithreading mechanism. Below is my code:

#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>

#include<iostream>
using namespace std;
void *thread_function(void *arg);
int input_value;

int main(int argc, char *argv[])
{
    int res;
    pthread_t a_thread;
    void *thread_result;
    res=pthread_create(&a_thread,NULL,thread_function,NULL);
    if(res!=0){
            perror("Thread creation error");
            exit(EXIT_FAILURE);
    }
    //sleep(10);
    cout<<"cancelling thread"<<endl;
    res=pthread_cancel(a_thread);

    cout<<"input value="<<input_value<<endl;
    exit(EXIT_SUCCESS);
 }
 void *thread_function(void *arg)
 {
    int res;
    res=pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
    if(res!=0){
            perror("Unable to set pthread to cancel enbable state");
            exit(EXIT_FAILURE);
    }
    cin>>input_value;
    pthread_exit(&input_value);
 }

But here I am facing a problem. Due to the sleep function either user inputs value or not the sleep function by default sleeps for 10 seconds. This is where I am lagging.

How do I solve this problem like using(signals,binary semaphores etc..). Please relate your answer w.r.t my solution(i.e multithreading).

Any information is most welcome...

like image 377
Santosh Sahu Avatar asked Jan 27 '26 23:01

Santosh Sahu


2 Answers

Since you're on a POSIX machine, you can use e.g. select to check if there's anything on the standard input:

fd_set fds;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds)

timeval timeout;
timeout.tv_sec = 5;   // A five-second timeout
timeout.tv_usec = 0;

int rc = select(STDIN_FILENO + 1, &fds, nullptr, nullptr, &timeout);
if (rc < 0)
    perror("select");
else if (rc == 0)
{
    // Timeout
}
else
{
    // There is input to be read on standard input
}

Using poll (as suggested by Basile Starynkevitch) it could be done something like this:

struct pollfd poller;
poller.fd = STDIN_FILENO;
poller.events = POLLIN;
poller.revents = 0;

int rc = poll(&poller, 1, 5);  // Poll one descriptor with a five second timeout
if (rc < 0)
    perror("select");
else if (rc == 0)
{
    // Timeout
}
else
{
    // There is input to be read on standard input
}
like image 84
Some programmer dude Avatar answered Jan 29 '26 13:01

Some programmer dude


I've tried a few solutions to get this to work and my current solution is pretty hacky, however, it works for my where the other solutions here using select and poll have failed.

My code would sometimes give me a positive result for data being available but then block forever on std::cin.get(); or std::getline(std::cin, STRINGVARIABLE);

My solution:

// Reset flag, initially true so that if cin blocks it will be reset
bool Reset = true;
// Create a c++11 thread to reset cin after a timeout of 100ms
std::thread ResetThread([&Reset](void){
    // This thread will wait 100ms
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    // Then if the reset flag is still true it will reset cin by closing and opening it
    if (Reset) {
        close(STDIN_FILENO);
        // TODO: Really the output of this open call should be checked for errors
        open("/dev/null", O_RDONLY);
    }
});

// Now for the standard reading of cin:
std::string Line;
std::getline(std::cin, Line);.
// If cin was read correctly the getline command will return immediately
// This means that the reset flag will be set to false long before the thread tests it.
Reset = false;
// Finally join with the thread.
ResetThread.join();

This code can be joined with the select or poll methods (it is in my code) to prevent creating a new thread every 100ms.

Obviously the appropriateness of this code will depend on the rest of your project, but it works for me so I thought I'd share.

-- EDIT --

After moving between PCs it seems that this code isn't as robust as I'd hoped. I've now accepted just using a separate blocked thread for input, see my answer here:

After fighting with non-blocking cin for a long time it seems the only robust way of doing it is inside its own thread.

See my answer here for an implementation:

https://stackoverflow.com/a/39499548/1427932

like image 43
gpdaniels Avatar answered Jan 29 '26 12:01

gpdaniels