Reputation: 562
I want to have a program where the user have 10 seconds to enter the password. If the timer goes over 10 seconds, the program displays a message. My current code is this:
#include <iostream>
#include <ctime>
#include <string>
int main(){
std::string password;
int start_s=clock();
int stop_s=clock();
if(stop_s-start_s <= 0){
std::cout << "TIME RAN OUT!";
}
std::cout << "Enter your password! \n";
std::cout << "Password: ";
std::cin >> password;
std::cout << "\n \n";
if (password == "password123"){
std::cout << "Correct!";
} else {
std::cout << "Wrong!";
}
}
This of course is not working... But I am not sure what to do next... Any ideas?
If you need more details, ask in the comments.
EDIT:
I just realized what the problem was... It took a time stamp and then quickly made another time stamp. And when the difference was found, it was below 0...
But I still don't know what to do next...
Upvotes: 9
Views: 11863
Reputation: 1
I recently ran in the same problem. I find the answer from @user1952500 presenting interesting methods. Among them, the second was the easier for me to understand.
However, I haven't found good code solutions for that. More generally, using detach won't kill the thread, but it let running standalone ("outside the main thread"). This means that the thread will run forever until the entire code terminates, which is even a worse scenario if we detach multiple threads.
To correctly achieve a keyboard input with timeout using a thread, it is necessary to kill that thread from a different one. The problem is: it is impossible to do it using only C++ standard libraries.
It is necessary to use platform-dependent functions. This is my solution for Linux:
#include <thread>
#include <chrono>
#include <stdio.h>
#include <pthread.h>
#include <string>
std::chrono::milliseconds now_ms(){
return std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()
);
}
int main(){
// set timeout
std::chrono::milliseconds timeout(10000);
// variable to check if the thread terminated by itself
bool thread_terminated = false;
// variable to store input
std::string answer;
// create thread
std::thread t1([&]() {
std::cin >> answer;
thread_terminated = true;
});
// get native handle
auto t_handle = t1.native_handle();
// start the thread
auto start_ts = now_ms();
t1.detach()
// check for timeout or answer received
while(this->now_ms_d() - start_ts < timeout && answer.empty()){
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
// if the thread didn't terminate, kill it
if(! thread_terminated) pthread_cancel(t_handle);
if (answer.empty()){
std::cout << "no answer received" << std::endl;
}
else{
/* do what you need to do with answer */
}
return 0;
}
For different platforms, you need to call a different function instead of pthread_cancel(t_handle)
.
On Windows for example, you may use TerminateThread.
Upvotes: 0
Reputation: 146
This code does it using thread.
#include <iostream>
#include <thread>
#include <chrono>
#include <string>
int main()
{
std::string password;
std::cout << "Enter your password! \n";
std::cout << "Password: ";
//try to read a passsword in 10 seconds
std::thread t1([&]() {
std::cin >> password;
});
std::this_thread::sleep_for(std::chrono::seconds(10));
t1.detach();
//check if password was entered
if (password.empty())
std::cout << "TIME IS OUT\n";
else
if (password == "password123")
std::cout << "Correct";
else
std::cout << "Worng";
return 0;
}
But if you check out you can see that it waits untill the end of the 10 seconds even if the user entered a passsword in time. So you can improve it this way:
#include <iostream>
#include <thread>
#include <chrono>
#include <string>
int main()
{
std::string password;
std::cout << "Enter your password! \n";
std::cout << "Password: ";
time_t start = time(NULL);
time_t waitTime = 10;
//try to read a passsword in 10 seconds
while (time(NULL) < start + waitTime && password.empty())
{
std::thread t1([&]() {
std::cin >> password;
});
std::this_thread::sleep_for(std::chrono::milliseconds(20));
t1.detach();
}
//check if password was entered
if (password.empty())
std::cout << "\nTIME IS OUT\n";
else
if (password == "password123")
std::cout << "Correct";
else
std::cout << "Worng";
return 0;
}
Upvotes: 0
Reputation: 6771
What you are trying to do is to have an non-blocking (asynchronous) read from stdin
with a timeout of 10 seconds. This is not too tough but may involve many new concepts depending on your current level.
The key concept here is that cin >> password;
is a blocking call, i.e., until it is completed, control will not flow further in this code. So we need to make it non-blocking in some way, or keep it blocking and break out of it when the timeout expires.
There are a few common implementations based on the design requirements and constraints of the system. Each implementation is different depending on the OS but the techniques are very similar.
1. Asynchronous: STDIN with timeout This approach is commonly used in network programming and can be extended to other forms of input such as the current case.
In Linux (and many other Unix flavors), the watch-list can be handled using FD_SET
and a select
system call. In Windows, you will need to use WaitForMultipleEvents
.
I'm not sure I can do justice to explaining these concepts accurately for the purposes of this question. As a reference, another question which has some code pointers for exactly the same thing is here.
2. Synchronous: Multithreaded with Interrupt This is a common technique used for cases where we need a fine-grained event-scheduler / timer.
A
and B
.A
will wait on the indicated timeout.B
will wait on a blocking readA
terminates (times out) before B
finishes, A
signals B
and B
decides what to do next (terminate, repeat a message etc)B
reads the password and it's fine, B
signals A
and asks it to die.Another way to achieve the same is to make the OS interrupt thread B
as described in one of the comments.
3. Synchronous: Polling This is used for cases where we don't need too much of a fine-grained control over time.
kbhit()
)delta
(say 10ms
)Note that in this case, depending on the delta
, the approach may consume a lot of CPU and may be inefficient. For example, if delta=10ms
as above, the thread will be woken up 100 times every second and it will be not efficient, especially when users do not type characters on their keyboard that fast.
Upvotes: 12
Reputation: 1
A canonical c++ solution should look like:
#include <iostream>
#include <chrono>
#include <string>
int main() {
std::string password;
std::cout << "Enter your password! \n";
std::cout << "Password: ";
auto start_s = std::chrono::system_clock::now();
std::cin >> password;
auto stop_s = std::chrono::system_clock::now();
if(stop_s - start_s >= std::chrono::seconds(10)) {
std::cout << "TIME RAN OUT!";
}
else if (password == "password123") {
std::cout << "Correct!";
} else {
std::cout << "Wrong!";
}
}
Upvotes: -3