Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

boost thread and try_join_for gives different output each time

Suppose that I have the following code:

#include <boost/chrono.hpp>
#include <boost/thread.hpp>

#include <iostream>

int main()
{
  boost::thread thd([]{ std::cout << "str \n"; });

  boost::this_thread::sleep_for(boost::chrono::seconds(3));

  if (thd.try_join_for(boost::chrono::nanoseconds(1)))
  {
    std::cout << "Finished \n";
  }
  else
  {
    std::cout << "Running \n";
  }
}

MSVC-12.0 and boost 1.55 gives me the different output each time when I start this program. For example,

str
Finished

str
Finished

str
Running

When I change boost::chrono::nanoseconds to boost::chrono::microseconds the output is looks as expected.

Why? What am I doing wrong? Is it a bug in boost library? Is there a ticket about in in boost bug tracker?

Thanks in advance.

like image 588
FrozenHeart Avatar asked Apr 10 '14 14:04

FrozenHeart


1 Answers

Your program simply has a race, most probably due to the fact that 1 nanosecond is awfully short.

try_join_for is implemented by calling try_join_until, a function that will attempt joining until a certain timepoint has been reached:

// I stripped some unrelated template stuff from the code
//  to make it more readable

bool try_join_for(const chrono::duration& rel_time)
{
  return try_join_until(chrono::steady_clock::now() + rel_time);
}

bool try_join_until(const chrono::time_point& t)
{
  system_clock::time_point     s_now = system_clock::now();
  bool joined= false;
  do {
    Clock::duration   d = ceil<nanoseconds>(t-Clock::now());
    if (d <= Clock::duration::zero())
        return false; // in case the Clock::time_point t is already reached
    // only here we attempt to join for the first time:
    joined = try_join_until(s_now + d);
  } while (! joined);
  return true;
}

The problem is now that try_join_until will check whether the requested time_point has been reached before attempting the join. As you can see, it needs to perform two other calls to clock::now() and some computation to compare the obtained values to the deadline given by the user. This may or may not be completed before the clock jumps beyond your given 1 nanosecond deadline, resulting in the unpredictability of the output.

Be aware that in general timing dependent code like this is fragile. Even with timeouts in the order of milliseconds, if you get preempted at a bad point during execution and there is a high load on the CPU, you might miss a deadline in rare cases. So be sure to always chose your deadlines carefully and never make assumptions that a deadline will be big enough in all possible cases.

like image 177
ComicSansMS Avatar answered Nov 18 '22 03:11

ComicSansMS