7

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.

FrozenHeart
  • 19,844
  • 33
  • 126
  • 242

2 Answers2

7

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.

ComicSansMS
  • 51,484
  • 14
  • 155
  • 166
  • 1
    Nice answer: I was wondering about the 'race' since there's a three second delay before the join attempt. But the race isn't with the other thread, it's with the timer within the `try_join_until()`. – Michael Burr Apr 10 '14 at 21:32
0

What is wrong with just calling .join()? If you insist you can check before you join:

#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.joinable())
      thd.join();
}

Note that the behaviour is Undefined anyway if you fail to join a thread before program exit. Use

  • futures,
  • condition variables or
  • semaphores

to signal job completion if that's what you were trying to monitor.

sehe
  • 374,641
  • 47
  • 450
  • 633