1

I need to implement a delay or sleep function that is accurate and consistent, and must be able to be cancelled.

Here's my code:

bool cancel_flag(false);

void My_Sleep(unsigned int duration)
{
  static const size_t SLEEP_INTERVAL = 10U; // 10 milliseconds
  while ((!cancel_flag) && (duration > SLEEP_INTERVAL))
  {
    Sleep(duration);
    duration -= SLEEP_INTERVAL;
  }
  if ((!cancel_flag) && (duration > 0U))
  {
    Sleep(duration);
  }
}

The above function is run in a worker thread. The main thread is able to change the value of the "cancel_flag" in order to abort (cancel) the sleeping.

At my shop, we have different results when the duration is 10 seconds (10000 ms). Some PCs are showing a sleep duration of 10 seconds, other PCs are showing 16 seconds.

Articles about the Sleep() function say that it is bound to the windows interrupt and when the duration elapses, the thread is rescheduled (may not be run immediately). The function above may be encountering a propagation of time error due to rescheduling and interrupt latency.

The Windows Timestamp Project describes another technique of waiting on a timer object. My understanding is that this technique doesn't provide a means of cancellation (by another, main, thread).

Question:
1. How can I improve my implementation for a thread delay or sleep, that can be cancelled by another task, and is more consistent?

  1. Can a sleeping AFX thread be terminated?
    (What happens when a main thread terminates a sleeping AFX thread?)

  2. What happens when a main thread terminates a thread that has called WaitForSingleObject?

Accuracy should be around 10ms, as in 10 seconds + 10ms.
Results should be consistent across various PCs (all running Windows 7 or 10).

Background
PCs that have correct timings are running Windows 7, at 2.9 GHz.
The PCs that have incorrect timings are running Windows 7, at 3.1 GHz and have fewer simultaneous tasks and applications running.
Application is developed using Visual Studio 2017, and MFC framework (using AFX for thread creation).

Thomas Matthews
  • 56,849
  • 17
  • 98
  • 154

2 Answers2

0

You shouldn't be implementing this at all.

In C++11 all basic necessary utilities for multithreading are implemented in the standard. If you do not use C++11 - then switch to C++11 or higher - in the unfortunate case that you cannot, then use Boost which has the same features.

Basically, what you want to do with this functionality is covered by std::condition_variable. You can put a thread into waiting mode by using function wait (it accepts a condition function necessary for leaving the wait), wait_for and wait_until (same as wait but with total waiting time limit) as well as notify_one and notify_all methods that wake the sleeping threads (one or all) and make them check the awakening condition and proceed with their tasks.

Check out std::conditional_variable in the reference. Just google it and you'll find enough information about it with examples.

In case you do not trust std::conditional_variable implementation for some reason, you can still utilize it for mini waits and awakening.

Maxwell Kunes
  • 52
  • 1
  • 7
ALX23z
  • 4,456
  • 1
  • 11
  • 18
  • Thank you for the information about C++11 and beyond. Unfortunately, the project was developed using MFC and AFX thread control. Converting to `std::thread` and the group, is on my list, when there is time to convert and retest. – Thomas Matthews May 18 '19 at 00:08
  • I don't think that you need to use `std::thread` for `std::condition_variable` to work. @ThomasMatthews – ALX23z May 18 '19 at 02:03
0
  • How can I improve my implementation for a thread delay or sleep, that can be cancelled by another task, and is more consistent?

  • Can a sleeping AFX thread be terminated? (What happens when a main thread terminates a sleeping AFX thread?)

    • I assume you mean terminate with TerminateThread? AFX threads are simply wrappers around standard Windows threads. There is nothing special or magical about them that would differentiate their behavior. So what would happen is a bunch of bad stuff:
      • If the target thread owns a critical section, the critical section will not be released.
      • If the target thread is allocating memory from the heap, the heap lock will not be released.
      • If the target thread is executing certain kernel32 calls when it is terminated, the kernel32 state for the thread's process could be inconsistent.
      • If the target thread is manipulating the global state of a shared DLL, the state of the DLL could be destroyed, affecting other users of the DLL.
    • You should never have to do this if you have access to all the source code of the application.
  • What happens when a main thread terminates a thread that has called WaitForSingleObject?

mnistic
  • 10,866
  • 2
  • 19
  • 33