C++ Equivalent of SpinWait.SpinUntil

I like to use threads in my code, but the problem with threads is that sometimes you need to wait for them to finish.

In c++ it is very easy to do.

std::thread t (my_function,1);
// do something
// do something else
t.join();

All the code above does is, start a thread, call the function my_function and passes the argument 1.
The t.join(); tells the main thread to wait until the thread is finished.

Now the problem is that t.join() could hang forever … and I would never know about it.

What I needed was something like join_until( 1000) where we either wait for the thread to finish or wait for 1000ms. What I needed was something similar to c# SpinUntil

In very simple terms all SpinUntil does is check a condition, (very often), and if it is true then return (true), but if a timeout happens then we return (false).
So in my case I would wait for the thread to complete, (yes I know, I know, you can’t easily tell if a std::thread is complete or not.

Enter Wait.SpinUntil …

static bool SpinUntil(std::function<bool()> condition, const long long milliseconds)
{
  // magic
}

Basically we will

  1. Start a new thread
  2. Check if the given condition is true
    1. If the condition is true, return true
    2. If the condition is false … continue.
  3. Wait for a couple of nanoseconds for other threads to do their bit
  4. Look how long we have been spinning
    1. If less than milliseconds go back to number 2
    2. If more than milliseconds, return false.

The code

This is a very simplified version of the code, you can find a working solution on my github page

// this code is inside our running thread
// while we will join our thread, it is possible for it to block, (see below)

{
    // assume that we will timeout
    bool result = false;
    std::unique_lock<std::mutex> lock(_mutex);
    try
    {
      // one ms wait.
      const auto oneMillisecond = std::chrono::milliseconds(1);

      // when we want to sleep until.
      const auto until = std::chrono::high_resolution_clock::now() + std::chrono::milliseconds(milliseconds);
      for (auto count = 0; count < std::numeric_limits<int>::max(); ++count)
        {
        if (condition())
        {
          // we have not timeout!
          result = true;
          break;
        }

        if (count % 4 == 0)
        {
          // slee a little bit
          std::this_thread::sleep_for(oneMillisecond);
        }
        else
        {
          // yield.
          std::this_thread::yield();
        }

        // are we done?
        if (std::chrono::high_resolution_clock::now() >= until)
        {
          break;
        }
      }
    }
  }
  catch(...)
  {
    //  something broke ... maybe we should re-throw.
    result = false;
  }
  // return the result
  return result;
}

So now the code above will wait for a couple of ms while running in the background and if the given condition is true we will get out and return true, otherwise we will return false after a while.

Possible issues

  • For one thing, the condition() function could hang, it is up to the caller to make sure this never happens.
  • The condition() function could take a rather long time to execute … it is up to the caller to make sure this never happens

In other words, don’t stuff things up in your condition() function.

Leave a comment

Leave a Reply