Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
376 views
in Technique[技术] by (71.8m points)

c++ - How to exit sleep() when an interrupt arrives?

I'm looking for a way to exit sleep when an user interrupt arrives. It's important to exit sleep rather than do this: interrupt sleep, do ISR processing, and go back to sleep - which is what I'm finding solutions for.

I'm looking for something like this in C++ - an equivalent in C is even better:

void *timer_thread(void *dummy)
{
  while(1)
  {
    // Check if some callbacks are to be given
    // when all are given, Determine x duration to sleep
  
    try
    {
      sleep(x);
    }
    except(/* the except block should hit ONLY when an interrupt arrives, 
              that too only when sleep() is executed. It's OK to delay 
              interrupt until the sleep is beginning execution */) 
    {
      //Do something else
    }
  }
}

The arriving interrupt will mostly tell that the sleep should be reduced for the timer thread to give callbacks earlier. But irrespective of the use case, I just need the sleep to be exited in some way when interrupt arrives. I just couldn't find info on how to do this.

PS: It's ok to discard/NOP the interrupts if it occurs when sleep wasn't happening

This is on Cygwin gcc v9.3.0 on Windows 10 (C/C++). I don't need the code to be portable, so any platform specific solution is ok as well.

If there's some other solution which is similar to this working (which doesn't use sleep() and interrupts), would be welcome to hear it. I'm just looking for a way which doesn't involve polling.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

To wait for a certain time or on a certain event, I would use a combination of std::mutex and std::condition_variable and specifically std::condition_variable::wait_for() to await either time-out or a signaled change of something.

A minimal sample program for demonstration:

#include <atomic>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>
#include <chrono>
using namespace std::chrono_literals;

// a thread-shared flag to signal exit
std::atomic<bool> exitThread = false;

// a mutex to sync. inter-thread communication
std::mutex mtxAlert;
// a condition variable to signal changed data
std::condition_variable sigAlert;
// the data of inter-thread communication
bool alert = false;

void timerThread()
{
  // the timeout for timer thread
  auto timeout = 100ms;
  // runtime loop
  while (!exitThread) {
    // lock mutex (preparation to wait in cond. var.)
    std::unique_lock<std::mutex> lock(mtxAlert);
    // unlock mutex and wait for timeout or signaled alert
    sigAlert.wait_for(lock, timeout, []() { return alert || exitThread; });
    // mutex is locked again
    // check why wait_for() exited
    if (exitThread) {
      std::cout << "Timer thread exiting...
";
      return;
    } else if (alert) {
      std::cout << "Timer was interrupted due to alert.
";
      alert = false;
    } else {
      std::cout << "Timer achieved time-out.
";
    }
  }
}

int main()
{
  std::thread threadWait(&timerThread);
  // wait a bit
  std::cout << "main(): Waiting 300ms...
";
  std::this_thread::sleep_for(300ms);
  // sim. interrupt
  std::cout << "main(): Sim. interrupt.
";
  { std::lock_guard<std::mutex> lock(mtxAlert);
    alert = true;
  }
  sigAlert.notify_all();
  // wait a bit
  std::cout << "main(): Waiting 50 ms...
";
  std::this_thread::sleep_for(50ms);
  // sim. interrupt
  std::cout << "main(): Sim. interrupt.
";
  { std::lock_guard<std::mutex> lock(mtxAlert);
    alert = true;
  }
  sigAlert.notify_all();
  // wait a bit
  std::cout << "main(): Waiting 50 ms...
";
  std::this_thread::sleep_for(50ms);
  // exiting application
  exitThread = true;
  sigAlert.notify_all();
  threadWait.join();
  // done
  std::cout << "Done.
";
}

Output:

main(): Waiting 300ms...
Timer achieved time-out.
Timer achieved time-out.
main(): Sim. interrupt.
main(): Waiting 50 ms...
Timer was interrupted due to alert.
main(): Sim. interrupt.
main(): Waiting 50 ms...
Timer was interrupted due to alert.
Timer thread exiting...
Done.

Live Demo on coliru


OP claimed per comment that this sample didn't compile properly on cygwin. I tried on my side and can confirm some minor issues which I fixed:

  1. Missing #include <mutex> added

  2. Initialization of std::atomic<bool> exitThread = false; changed to

    std::atomic<bool> exitThread(false);
    

    I got this when I compiled with g++ as well as with g++ -std=c++14. (It seems that -std=c++14 is the default of my g++.)

    When I use g++ -std=c++17 instead I don't get any complaint. I strongly believe that has something to do with copy-elision which g++ applies with -std=c++17 but not prior.

However, this is my sample session with the slightly reviewed code on my Windows 10 laptop in cygwin64:

$ g++ --version
g++ (GCC) 7.4.0

$
$ cat >testCondVar.cc <<'EOF'
> #include <atomic>
> #include <condition_variable>
> #include <iostream>
> #include <mutex>
> #include <thread>
> #include <chrono>
> using namespace std::chrono_literals;
> 
> // a thread-shared flag to signal exit
> std::atomic<bool> exitThread(false);
> 
> // a mutex to sync. inter-thread communication
> std::mutex mtxAlert;
> // a condition variable to signal changed data
> std::condition_variable sigAlert;
> // the data of inter-thread communication
> bool alert = false;
> 
> void timerThread()
> {
>   // the timeout for timer thread
>   auto timeout = 100ms;
>   // runtime loop
>   while (!exitThread) {
>     // lock mutex (preparation to wait in cond. var.)
>     std::unique_lock<std::mutex> lock(mtxAlert);
>     // unlock mutex and wait for timeout or signaled alert
>     sigAlert.wait_for(lock, timeout, []() { return alert || exitThread; });
>     // mutex is locked again
>     // check why wait_for() exited
>     if (exitThread) {
>       std::cout << "Timer thread exiting...
";
>       return;
>     } else if (alert) {
>       std::cout << "Timer was interrupted due to alert.
";
>       alert = false;
>     } else {
>       std::cout << "Timer achieved time-out.
";
>     }
>   }
> }
> 
> int main()
> {
>   std::thread threadWait(&timerThread);
>   // wait a bit
>   std::cout << "main(): Waiting 300ms...
";
>   std::this_thread::sleep_for(300ms);
>   // sim. interrupt
>   std::cout << "main(): Sim. interrupt.
";
>   { std::lock_guard<std::mutex> lock(mtxAlert);
>     alert = true;
>   }
>   sigAlert.notify_all();
>   // wait a bit
>   std::cout << "main(): Waiting 50 ms...
";
>   std::this_thread::sleep_for(50ms);
>   // sim. interrupt
>   std::cout << "main(): Sim. interrupt.
";
>   { std::lock_guard<std::mutex> lock(mtxAlert);
>     alert = true;
>   }
>   sigAlert.notify_all();
>   // wait a bit
>   std::cout << "main(): Waiting 50 ms...
";
>   std::this_thread::sleep_for(50ms);
>   // exiting application
>   exitThread = true;
>   sigAlert.notify_all();
>   threadWait.join();
>   // done
>   std::cout << "Done.
";
> }
> EOF

$

Compiled and started:

$ g++ -std=c++14 -o testCondVar testCondVar.cc

$ ./testCondVar
main(): Waiting 300ms...
Timer achieved time-out.
Timer achieved time-out.
main(): Sim. interrupt.
main(): Waiting 50 ms...
Timer was interrupted due to alert.
main(): Sim. interrupt.
main(): Waiting 50 ms...
Timer was interrupted due to alert.
Timer thread exiting...
Done.

$

Note:

The only reason that at minimum C++14 is required for this sample is the usage of the std::chrono_literals which enables the usage of e.g. 300ms.

Without std::chrono_literals, this could be written as std::chrono::milliseconds(300) (which is admittedly less convenient). Replacing all std::chrono_literals respectively, I was able to compile and run the sample with -std=c++11 as well.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...