Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wake up a std::thread from usleep

Consider the following example:

#include <iostream>
#include <fstream>
#include <unistd.h>

#include <signal.h>
#include <thread>

void sleepy() {
    usleep(1.0E15);
}

int main() {
    std :: thread sleepy_thread(sleepy);

    // Wake it up somehow...?

    sleepy_thread.join();
}

Here we have a thread that just sleeps forever. I want to join it, without having to wait forever for it to spontaneously wake from usleep. Is there a way to tell it from the extern "hey man, wake up!", so that I can join it in a reasonable amount of time?

I am definitely not an expert on threads, so if possible don't assume anything.

like image 226
Matteo Monti Avatar asked Aug 26 '15 17:08

Matteo Monti


People also ask

How do you wake up a STD thread while it's asleep?

You can use std::promise/std::future as a simpler alternative to a bool / condition_variable / mutex in this case. A future is not susceptible to spurious wakes and doesn't require a mutex for synchronisation. Save this answer.

Can we wake up a thread that has been put to sleep by using thread sleep () method?

If the threads are sleeping with Thread. sleep(...) , you can wake them up with Thread. interrupt() . Make sure you're handling the InterruptedException and testing Thread.

What does sleep () do in C++?

The sleep () function causes the program or the process in which it is called, to suspend its execution temporarily for a period of time in seconds specified by the function parameter. Execution is suspended until the requested time is elapsed or a signal or an interrupt is delivered to the function.


2 Answers

No, it is not possible using the threads from the standard library.

One possible workaround is to use condition_variable::sleep_for along with a mutex and a boolean condition.

#include <mutex>
#include <thread>
#include <condition_variable>

std::mutex mymutex;
std::condition_variable mycond;
bool flag = false;

void sleepy() {
     std::unique_lock<std::mutex> lock(mymutex);
     mycond.wait_for( lock,
                      std::chrono::seconds(1000),
                      []() { return flag; } );
}

int main()
{
    std :: thread sleepy_thread(sleepy);

    {
       std::lock_guard<std::mutex> lock(mymutex);
       flag = true;
       mycond.notify_one();
    }

    sleepy_thread.join();
}

Alternatively, you can use the Boost.Thread library, which implements the interruption-point concept:

#include <boost/thread/thread.hpp>

void sleepy()
{
    // this_thread::sleep_for is an interruption point.
    boost::this_thread::sleep_for( boost::chrono::seconds(1000) );
}

int main()
{
    boost::thread t( sleepy );

    t.interrupt();
    t.join();

}
like image 122
sbabbi Avatar answered Oct 21 '22 17:10

sbabbi


Other answers are saying you can use a timed muted to accomplish this. I've put together a small class using a timed mutex to block the 'sleeping' threads, and release the mutex if you want to 'wake' them early. The standard library provides a function for timed_mutex called try_lock_for which will try to lock a mutex for a period of time, before continuing on anyway (and returning an indication of failure)

This can be encapsulated in a class, like the following implementation, which only allows a single call to wake waiting threads. It could also be improved by including a waitUntil function for waiting until a time series to correspond to the timed_mutex's other timed waiting function, try_lock_until but I will leave that as an exercise to the interested, since it seems a simple modification.


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

// one use wakable sleeping class
class InterruptableSleeper{
    std::timed_mutex
        mut_;
    std::atomic_bool
        locked_; // track whether the mutex is locked
    void lock(){ // lock mutex
        mut_.lock();
        locked_ = true;
    }
    void unlock(){ // unlock mutex
        locked_ = false;
        mut_.unlock();
    }
public:
    // lock on creation
    InterruptableSleeper() {
        lock();
    }
    // unlock on destruction, if wake was never called
    ~InterruptableSleeper(){
        if(locked_){
            unlock();
        }
    }
    // called by any thread except the creator
    // waits until wake is called or the specified time passes
    template< class Rep, class Period >
    void sleepFor(const std::chrono::duration<Rep,Period>& timeout_duration){
        if(mut_.try_lock_for(timeout_duration)){
            // if successfully locked, 
            // remove the lock
            mut_.unlock();
        }
    }
    // unblock any waiting threads, handling a situation
    // where wake has already been called.
    // should only be called by the creating thread
    void wake(){
        if(locked_){
            unlock();
        }
    }
};

The following code:

void printTimeWaited(
  InterruptableSleeper& sleeper, 
  const std::chrono::milliseconds& duration){
    auto start = std::chrono::steady_clock::now();
    std::cout << "Started sleep...";
    sleeper.sleepFor(duration);
    auto end = std::chrono::steady_clock::now();
    std::cout 
        << "Ended sleep after "
        << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()
        << "ms.\n";
}

void compareTimes(unsigned int sleep, unsigned int waker){
    std::cout << "Begin test: sleep for " << sleep << "ms, wakeup at " << waker << "ms\n";
    InterruptableSleeper
        sleeper;
    std::thread
        sleepy(&printTimeWaited, std::ref(sleeper), std::chrono::milliseconds{sleep});
    std::this_thread::sleep_for(std::chrono::milliseconds{waker});
    sleeper.wake();
    sleepy.join();
    std::cout << "End test\n";
}

int main(){

    compareTimes(1000, 50);
    compareTimes(50, 1000);

}

prints

Begin test: sleep for 1000ms, wakeup at 50ms
Started sleep...Ended sleep after 50ms.
End test
Begin test: sleep for 50ms, wakeup at 1000ms
Started sleep...Ended sleep after 50ms.
End test
Example & Use on Coliru
like image 43
jaggedSpire Avatar answered Oct 21 '22 15:10

jaggedSpire