Matteo Monti
Matteo Monti

Reputation: 8950

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.

Upvotes: 14

Views: 21569

Answers (6)

sbabbi
sbabbi

Reputation: 11181

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

One possible workaround is to use condition_variable::wait_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();

}

Upvotes: 14

jaggedSpire
jaggedSpire

Reputation: 4513

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

Upvotes: 6

Robert Jacobs
Robert Jacobs

Reputation: 3360

Sleep for a short amount of time and look to see if a variable has changed.

#include <atomic>
#include <unistd.h>
#include <thread>

std::atomic<int> sharedVar(1);
void sleepy()
{
    while (sharedVar.load())
    {
        usleep(500);
    }
}

int main()
{
    std :: thread sleepy_thread(sleepy);
    // wake up
    sharedVar.store(0);
}

Upvotes: 0

Ritesh
Ritesh

Reputation: 1847

One possible approach:(There are many ways to accomplish..also its not good idea to use sleep in your thread)

///Define a mutex
void sleepy()
{
    //try to take mutex lock which this thread will get if main thread leaves that
    //usleep(1.0E15);
}


int main()
{
    //Init the Mutex
    //take mutex lock
    std :: thread sleepy_thread(sleepy);

    //Do your work
    //unlock the mutex...This will enable the sleepy thread to run
    sleepy_thread.join();
}

Upvotes: -1

πάντα ῥεῖ
πάντα ῥεῖ

Reputation: 1

"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?"

No, there's no way to do so according c++ standard mechanisms.

Well, to get your thread being woken, you'll need a mechanism that leaves other threads in control of it. Besides usleep() is a deprecated POSIX function:

Issue 6

The DESCRIPTION is updated to avoid use of the term "must" for application requirements.

This function is marked obsolescent.

IEEE Std 1003.1-2001/Cor 2-2004, item XSH/TC2/D6/144 is applied, updating the DESCRIPTION from "process' signal mask" to "thread's signal mask", and adding a statement that the usleep() function need not be reentrant.

there's no way you could get control of another thread, that's going to call that function.
Same thing for any other sleep() functions even if declared from std::thread.

As mentioned in other answers or comments, you'll need to use a timeable synchronization mechanism like a std::timed_mutex or a std::condition_variable from your thread function.

Upvotes: 2

pelya
pelya

Reputation: 4494

Just use a semaphore, call sem_timedwait instead of usleep, and call sem_post before calling join

Upvotes: 2

Related Questions