1

I need to wait in my program for a subsystem. In different places a have to wait for different conditions. I know I could also make use of threads and conditions variables. But since the subsystem (bare metal programmed in C) is connected via shared memory with no interrupts registered to it -- one thread needs to poll anyways.

So I did the following template to be able to wait for anything. I was wondering whether there is already a STL function which could be used for that?

#include <chrono>
#include <thread>


//given poll interval
template<typename predicate, 
         typename Rep1, typename Period1, 
         typename Rep2, typename Period2> 
bool waitActiveFor(predicate check,
                   std::chrono::duration<Rep1, Period1> x_timeout,
                   std::chrono::duration<Rep2, Period2> x_pollInterval)
{
  auto x_start = std::chrono::steady_clock::now();
  while (true)
  {
    if (check())
      return true;

    if ((std::chrono::steady_clock::now() - x_start) > x_timeout)
      return false;

    std::this_thread::sleep_for(x_pollInterval);
  }
}

//no poll interval defined
template<typename predicate, 
         typename Rep, typename Period>
bool waitActiveFor(predicate check,
                   std::chrono::duration<Rep, Period> x_timeout)
{
  auto x_start = std::chrono::steady_clock::now();
  while (true)
  {
    if (check())
      return true;

    if ((std::chrono::steady_clock::now() - x_start) > x_timeout)
      return false;

    std::this_thread::yield();
  }
}

running sample


2019-05-23: Code update regarding the comments and answers

florgeng
  • 856
  • 1
  • 9
  • 17
  • `stl` is a bit ambiguous. I allowed myself to remove the tag, because its description says that the tag refers to the Standard Template Library (the one that was adopted partly into the c++ standard), not to the c++ standard library. – 463035818_is_not_an_ai May 22 '19 at 16:25
  • 1
    [std::timed_mutex](https://en.cppreference.com/w/cpp/thread/timed_mutex) maybe? – Jesper Juhl May 22 '19 at 16:34
  • Condition_variable? – JVApen May 22 '19 at 16:38
  • 1
    @JVApen Condition variable in combination with `std::timed_mutex` :) – πάντα ῥεῖ May 22 '19 at 16:39
  • 1
    Not needed: https://en.cppreference.com/w/cpp/thread/condition_variable/wait_for – JVApen May 22 '19 at 16:41
  • "one thread needs to poll anyways." -- The word 'poll' implies unlimited testing, a waste of resources. Consider alternatives ... how often does your software need to check the shared memory? A 100 hz check is likely a trivial cpu load. And for user triggered shared-memory-activity, 10 hz is usually more than sufficient. This idea is sometimes referred to as periodic status update. – 2785528 May 22 '19 at 17:01
  • Looks like I read over the fact that condition_variable can't be used in this case. – JVApen May 22 '19 at 17:07
  • @2785528 the poll intervall highly depends to the scenario, in case of a power fail, 10hz would not be sufficient. – florgeng May 22 '19 at 17:08
  • @florgeng Do I understand your question correctly if I see that you want to 'watch' a certain bit/byte in memory and be notified as soon as it gets changed? (Though, prevent infinite wait via a time-out) – JVApen May 22 '19 at 17:11
  • @JVApen exactly, I am waiting for certain responses in different use cases but need to abort if the subsystem needs too long – florgeng May 22 '19 at 17:13
  • @florgeng I don't think there is anything in the STL that provides such functionality. I think you are best off with looking at system calls instead. Another option: [Boost::interprocess](https://www.boost.org/doc/libs/master/doc/html/interprocess/synchronization_mechanisms.html#interprocess.synchronization_mechanisms.conditions) If you can't use it, you might find some tricks in what they do. – JVApen May 22 '19 at 17:18
  • This sounds kind of like readers/writers scenario. One thread should be pumping a message queue (reader) that others write to (writer). Is this kind of approach not feasible on your target platform? – AndyG May 22 '19 at 17:44
  • @JVApen Boost.Interprocess can't be used here since it needs c++ in the subsystem. – florgeng May 23 '19 at 08:16
  • @AndyG yes, but still then one thread is needed to poll the interface, but that could fire events/condition variables to waiting threads. But then there is no scenario based controls over the reaction speed like with passed poll intervals – florgeng May 23 '19 at 08:18

2 Answers2

1

Not to my knowledge. In general, the goal is to wait without burning clock cycles, so the standard library is geared toward that usage.

I'm aware of std::this_thread::yield() which is what I usually use when I want to busy wait, but since you've got a poll interval, sleep_for() is probably your best bet.

John Tyner
  • 1,181
  • 1
  • 12
  • 15
1

Is there a standard function to busy wait for a condition or until a timeout

No. There are functions for blocking until timeout or notification, but not for busy waiting. However, that is indeed fairly simple to write, as you've demonstrated.

An important consideration regarding the context: There is no guarantee in general that the check() is still true after the function has returned true. To achieve that guarantee, you must make sure that anything that touches the shared memory (including the subsystem) won't change the check to be false, with the exception of the thread that does the polling (and that means there can only be one thread doing so).


Bonus code review

  • Since you have a template anyway, it is probably a good idea to let the type of the time arguments be templated as well, so that the user can use any std::chrono::time_point and thereby any unit the wish. In addition, you can get rid of duration_casts and count in your template.
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • thanks for your answer and the review. I will consider all your remarks. I think I will do it like you suggested with a time_point as parameter. I havent seen a template which take the predicate function by reference until now, but yes -- I can't see a problem in doing that right now. – florgeng May 22 '19 at 17:40
  • @florgeng actually never mind the reference suggestion. Use value instead, and if you need to avoid copy, pass the functor with `std::ref(check)`. – eerorika May 22 '19 at 17:47