Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What type to use for a timeout variable in C++?

Tags:

When writing a function in C++ that takes a timeout as one of its arguments, what type should I use for the timeout argument itself?

An example of such a function might be:

void my_function(bool work_really_hard, timeout_type timeout) { // Do stuff, until timeout is reached. } 

I have thought about using std::chrono::seconds for timeout_type, but that disallows using any timeout in the sub-second realm.

When using std::chrono::nanoseconds instead, it is cumbersome to specify, say, 10 minutes.

Any way to resolve this in a reasonable way, while keeping the function signature and calls neat and simple?

like image 259
JohnCand Avatar asked Nov 15 '13 19:11

JohnCand


People also ask

How do I set timeout in CPP?

The interface of Timer The interface of the Timer object is this: class Timer { bool clear = false; public: void setTimeout(auto function, int delay); void setInterval(auto function, int interval); void stop(); };


2 Answers

When using std::chrono::nanoseconds instead, it is cumbersome to specify, say, 10 minutes.

Actually, it doesn't make it cumbersome in the least:

#include <chrono> #include <iostream>  void my_function(bool work_really_hard, std::chrono::nanoseconds timeout) {     std::cout << timeout.count() << '\n'; }  int main() {     my_function(true, std::chrono::minutes(10)); } 

Output:

600000000000 

The only time you'll have trouble with nanoseconds is if you want to pass in something that won't exactly convert to nanoseconds, such as picoseconds, or duration<long, ratio<1, 3>> (1/3 second units).

Update

I intended this answer to be additional information for an already accepted answer that I thought was a good answer (by sehe). sehe recommended a templated solution, which I also consider fine.

If you want to accept any std::chrono::duration, even one that you may have to truncate or round, then going with sehe's deleted answer is the way to go:

template <typename Rep, typename Period> void my_function(bool work_really_hard, std::chrono::duration<Rep, Period> timeout) {     // Do stuff, until timeout is reached.     std::this_thread::sleep_for(timeout); } 

If for some reason you do not want to deal with templates and/or you are content with having your clients having to specify only units that are exactly convertible to std::chrono:nanoseconds, then using std::chrono:nanoseconds as I show above is also completely acceptable.

All of the std::chrono "pre-defined" units:

hours minutes seconds milliseconds microseconds nanoseconds 

are implicitly convertible to nanoseconds, and will not involve any truncation or round off error. Overflow will not happen as long as you keep it within the two bright white lines (obscure reference to keeping your car in your own lane). As long as the duration is within +/- 292 years, you don't have to worry about overflow with these pre-defined units.

The std-defined functions such as std::this_thread::sleep_for are templated as sehe suggests for exactly the reason of wanting to be interoperable with every chrono:duration imaginable (e.g. 1/3 of a floating-point femtosecond). It is up to the API designer to decide if they need that much flexibility in their own API.

If I've now managed to confuse you instead of clarify, don't worry too much. If you choose to use nanoseconds, things will either work exactly, with no truncation or round off error, or the client will get a compile time error. There will be no run time error.

void my_function(bool work_really_hard, std::chrono::nanoseconds timeout) {     std::cout << timeout.count() << '\n'; }  int main() {     using namespace std;     using namespace std::chrono;     typedef duration<double, pico> picoseconds;     my_function(true, picoseconds(100000.25)); }  test.cpp:15:9: error: no matching function for call to 'my_function'         my_function(true, picoseconds(100000.25));         ^~~~~~~~~~~ test.cpp:4:10: note: candidate function not viable: no known conversion from 'duration<double, ratio<[...], 1000000000000>>' to       'duration<long long, ratio<[...], 1000000000>>' for 2nd argument     void my_function(bool work_really_hard, std::chrono::nanoseconds timeout)          ^ 1 error generated. 

And if the client gets a compile-time error, he can always use duration_cast to work around it:

my_function(true, duration_cast<nanoseconds>(picoseconds(100000.25)));  // Ok 

For further details, please see:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2661.htm

Sweet update to the original code at the top of this answer. In C++1y, which we hope means C++14:

using namespace std::literals; my_function(true, 10min);  // also ok, is equal to 600000000000 nanoseconds 

Question: What would you recommend as a timeout of "infinity" (i.e. don't time out)

I would first try to use an API that didn't take a timeout, and which implied "doesn't time out." For example condition_variable::wait. If I had control over the API, I would create such a signature without a timeout.

Failing that, I would create a series a "large" chrono::durations:

using days = std::chrono::duration <     std::int32_t, std::ratio_multiply<std::chrono::hours::period, std::ratio<24>> >;  using weeks = std::chrono::duration <     std::int32_t, std::ratio_multiply<days::period, std::ratio<7>> >;  using years = std::chrono::duration <     std::int32_t, std::ratio_multiply<days::period, std::ratio<146097, 400>> >;  using months = std::chrono::duration <     std::int32_t, std::ratio_divide<years::period, std::ratio<12>> >; 

And then I would use one of these large durations in my call, for example:

std::this_thread::sleep_for(years(3)); 

I would not try to push_things to the maximum (you might break the underlying assumptions the OS has made for example). Just arbitrarily pick something ridiculously large (like 3 years). That will catch your code reviewer's eye, and likely strike up an informative conversation. :-)

Now available in video: https://www.youtube.com/watch?v=P32hvk8b13M :-)

like image 185
Howard Hinnant Avatar answered Oct 14 '22 08:10

Howard Hinnant


Use a template parameter for the timeout type and expect one of the std::chrono types, or something with a similar interface; this will allow you to pass in any unit of time.

template<typename T> void wait_a_while(const T& duration) {    std::this_thread::sleep(duration); }  wait_a_while(boost::posix_time::seconds(5)); wait_a_while(std::chrono::milliseconds(30)); 
like image 24
Collin Dauphinee Avatar answered Oct 14 '22 07:10

Collin Dauphinee