Can I create a software watchdog timer thread in C++ using Boost Signals2 and Threads?

后端 未结 4 2123
走了就别回头了
走了就别回头了 2020-12-06 06:28

I am running function Foo from somebody else\'s library in a single-threaded application currently. Most of the time, I make a call to Foo and it\'s really quick, some time

相关标签:
4条回答
  • 2020-12-06 07:04

    You can call Foo on a second thread with a timeout. For example:

    #include <boost/date_time.hpp> 
    #include <boost/thread/thread.hpp>
    
    boost::posix_time::time_duration timeout = boost::posix_time::milliseconds(500);
    boost::thread thrd(&Foo);
    
    if (thrd.timed_join(timeout))
    {
      //finished
    }
    else
    {
      //Not finished;
    }
    
    0 讨论(0)
  • 2020-12-06 07:06

    You can use the following class:

    class timer
    {
        typedef boost::signals2::signal<void ()> timeout_slot;
    public:
        typedef timeout_slot::slot_type timeout_slot_t;
    
    public:
        timer() : _interval(0), _is_active(false) {};
        timer(int interval) : _interval(interval), _is_active(false) {};
        virtual ~timer() { stop(); };
    
        inline boost::signals2::connection connect(const timeout_slot_t& subscriber) { return _signalTimeout.connect(subscriber); };
    
        void start()
        {
            boost::lock_guard<boost::mutex> lock(_guard);
    
            if (is_active())
                return; // Already executed.
            if (_interval <= 0)
                return;
    
            _timer_thread.interrupt();
            _timer_thread.join();
    
            timer_worker job;
            _timer_thread = boost::thread(job, this);
    
            _is_active = true;
        };
    
        void stop()
        {
            boost::lock_guard<boost::mutex> lock(_guard);
    
            if (!is_active())
                return; // Already executed.
    
            _timer_thread.interrupt();
            _timer_thread.join();
    
            _is_active = false;
        };
    
        inline bool is_active() const { return _is_active; };
    
        inline int get_interval() const { return _interval; };
    
        void set_interval(const int msec)
        {
            if (msec <= 0 || _interval == msec)
                return;
    
            boost::lock_guard<boost::mutex> lock(_guard);
            // Keep timer activity status.
            bool was_active = is_active();
    
            if (was_active)
                stop();
            // Initialize timer with new interval.
            _interval = msec;
    
            if (was_active)
                start();
        };
    
    protected:
        friend struct timer_worker;
        // The timer worker thread.
        struct timer_worker
        {
            void operator()(timer* t)
            {
                boost::posix_time::milliseconds duration(t->get_interval());
    
                try
                {
                    while (1)
                    {
                        boost::this_thread::sleep<boost::posix_time::milliseconds>(duration);
                        {
                            boost::this_thread::disable_interruption di;
                            {
                                t->_signalTimeout();
                            }
                        }
                    }
                }
                catch (boost::thread_interrupted const& )
                {
                    // Handle the thread interruption exception.
                    // This exception raises on boots::this_thread::interrupt.
                }
            };
        };
    
    protected:
        int             _interval;
        bool            _is_active;
    
        boost::mutex    _guard;
        boost::thread   _timer_thread;
    
        // Signal slots
        timeout_slot    _signalTimeout;
    };
    

    An example of usage:

    void _test_timer_handler()
    {
        std::cout << "_test_timer_handler\n";
    }
    
    BOOST_AUTO_TEST_CASE( test_timer )
    {
        emtorrus::timer timer;
    
        BOOST_CHECK(!timer.is_active());
        BOOST_CHECK(timer.get_interval() == 0);
    
        timer.set_interval(1000);
        timer.connect(_test_timer_handler);
    
        timer.start();
    
        BOOST_CHECK(timer.is_active());
    
        std::cout << "timer test started\n";
    
        boost::this_thread::sleep<boost::posix_time::milliseconds>(boost::posix_time::milliseconds(5500));
    
        timer.stop();
    
        BOOST_CHECK(!timer.is_active());
        BOOST_CHECK(_test_timer_count == 5);
    }
    
    0 讨论(0)
  • 2020-12-06 07:20

    You can also set an alarm right before calling that function, and catch SIGALRM.

    0 讨论(0)
  • 2020-12-06 07:24

    Vlad, excellent post! Your code compiled and works beautifully. I implemented a software watchdog timer with it. I made a few modifications:

    • To prevent pointer decay, store the signal in boost::shared_ptr and pass this to the thread worker instead of a weak pointer to the timer class. This eliminates the need for the thread worker to be a friend struct and guarantees the signal is in memory.
    • Add parameter _is_periodic to allow the caller to select whether or not the worker thread is periodic or if it terminates after expiration.
    • Store _is_active, _interval and _is_periodic in boost::atomic to allow thread-safe access.
    • Narrow the scope of mutex locking.
    • Add reset() method to "kick" the timer, preventing it from issuing the expiration signal.

    With these changes applied:

    #include <atomic>
    #include <boost/signals2.hpp>
    #include <boost/thread.hpp>
    
    class IntervalThread
    {
        using interval_signal = boost::signals2::signal<void(void)>;
    
    public:
        using interval_slot_t = interval_signal::slot_type;
    
        IntervalThread(const int interval_ms = 60)
          : _interval_ms(interval_ms),
            _is_active(false),
            _is_periodic(false),
            _signal_expired(new interval_signal()) {};
    
        inline ~IntervalThread(void) { stop(); };
    
        boost::signals2::connection connect(const interval_slot_t &subscriber)
        {
            // thread-safe: signals2 obtains a mutex on connect()
            return _signal_expired->connect(subscriber); 
        };
    
        void start(void)
        {
            if (is_active())
                return; // Already executed.
            if (get_interval_ms() <= 0)
                return;
    
            boost::lock_guard<boost::mutex> lock(_timer_thread_guard);
            _timer_thread.interrupt();
            _timer_thread.join();
    
            _timer_thread = boost::thread(timer_worker(),
                    static_cast<int>(get_interval_ms()),
                    static_cast<bool>(is_periodic()),
                    _signal_expired);
            _is_active = true;
        };
    
        void reset(void)
        {
            if (is_active())
                stop();
            start();
        }
    
        void stop(void)
        {
            if (!is_active())
                return; // Already executed.
    
            boost::lock_guard<boost::mutex> lock(_timer_thread_guard);
            _timer_thread.interrupt();
            _timer_thread.join();
            _is_active = false;
        };
    
        inline bool is_active(void) const { return _is_active; };
    
        inline int get_interval_ms(void) const { return _interval_ms; };
    
        void set_interval_ms(const int interval_ms)
        {
            if (interval_ms <= 0 || get_interval_ms() == interval_ms)
                return;
    
            // Cache timer activity state.
            const bool was_active = is_active();
            // Initialize timer with new interval.
            if (was_active)
                stop();
            _interval_ms = interval_ms;
            if (was_active)
                start();
        };
    
        inline bool is_periodic(void) const { return _is_periodic; }
        inline void set_periodic(const bool is_periodic = true) { _is_periodic = is_periodic; }
    
    private:
        // The timer worker for the interval thread.
        struct timer_worker {
            void operator()(const int interval_ms, const bool is_periodic, boost::shared_ptr<interval_signal> signal_expired)
            {
                boost::posix_time::milliseconds duration(interval_ms);
                try {
                    do {
                        boost::this_thread::sleep<boost::posix_time::milliseconds>(duration);
                        {
                            boost::this_thread::disable_interruption di;
                            signal_expired->operator()();
                        }
                    } while (is_periodic);
                } catch (const boost::thread_interrupted &) {
                    // IntervalThread start(), stop() and reset() throws boost::this_thread::interrupt,
                    // which is expected since this thread is interrupted. No action neccessary.
                }
            };
        };
    
        std::atomic<int> _interval_ms;  // Interval, in ms
        std::atomic<bool> _is_active;   // Is the timed interval active?
        std::atomic<bool> _is_periodic; // Is the timer periodic?
    
        boost::mutex _timer_thread_guard;
        boost::thread _timer_thread;
    
        // The signal to call on interval expiration.
        boost::shared_ptr<interval_signal> _signal_expired;
    };
    
    0 讨论(0)
提交回复
热议问题