AdeleGoldberg
AdeleGoldberg

Reputation: 1339

Interruptible sleep in std::thread

I have a simple C++11 thread program like below.

Code:

#include <iostream>
#include <thread>
#include <chrono>
#include <atomic>

int main(int argc, char *argv[]) {

   std::cout << "My program starts" << std::endl;
   std::atomic<bool> exit_thread(false);
   std::thread my_thread = std::thread([&exit_thread]{
        do {
            std::cout << "Thread is doing something..." << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(5));
        } while (!exit_thread);
    });

   std::this_thread::sleep_for(std::chrono::seconds(12));
   exit_thread = true;
   std::cout << "Might have to wait to exit thread" << std::endl;
   my_thread.join();
   return 0;
}

As you can see above, there is a loop which has a sleep_for which makes the thread sleep for 5 seconds and then it wakes and loops again provided that exit_thread is set to false. Main thread waits for 12 seconds and prepares to exit firstly by setting exit_thread to true and then does a join on the thread. All good until now.

Problem:
Above is okay and works for objective. But there is a "potential problem". If the thread has just now started to sleep then it would take it 4 seconds more before it gets out of sleep to discover that it now needs to exit. This delays the exit process and destruction.

Question:
How to can I make the thread sleep in an interruptible way? So that I can interrupt the sleep and make the thread exit right away instead by cancelling out of sleep instead of waiting for the potential 4 or 3 or 2 seconds.

I think that the solution to this might be achievable using a std::condition_variable? Probably? I am looking for a piece of code to show how.

Note that my code runs on both clang and gcc.

Upvotes: 3

Views: 2821

Answers (1)

Toby Speight
Toby Speight

Reputation: 30728

We should be waiting on a condition variable or semaphore instead of sleeping. Here's the minimal change to do that:

#include <atomic>
#include <chrono>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>

int main()
{
   std::cout << "My program starts" << std::endl;
   std::atomic<bool> exit_thread(false);
   std::condition_variable cv;
   std::mutex m;

   std::thread my_thread = std::thread([&exit_thread,&cv,&m]{
        do {
            std::cout << "Thread is doing something..." << std::endl;
            {
                std::unique_lock<std::mutex> lock(m);
                cv.wait_for(lock, std::chrono::seconds(5));
            }
        } while (!exit_thread);
    });

   std::this_thread::sleep_for(std::chrono::seconds(12));
   {
       std::lock_guard<std::mutex> guard(m);
       exit_thread = true;
   }
   cv.notify_all();

   std::cout << "Thread stops immediately" << std::endl;
   my_thread.join();
}

Apparently, we do need the mutex:

Even if the shared variable is atomic, it must be modified under the mutex in order to correctly publish the modification to the waiting thread.

Upvotes: 6

Related Questions