interval_timer.cc 4.95 KB
Newer Older
1
// Copyright (C) 2011-2019 Internet Systems Consortium, Inc. ("ISC")
2
//
3 4 5
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 7

#include <config.h>
8 9 10
#include <asiolink/asio_wrapper.h>
#include <asiolink/interval_timer.h>
#include <asiolink/io_service.h>
11

12
#include <boost/bind.hpp>
13 14
#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>
15 16 17

#include <exceptions/exceptions.h>

18 19
#include <atomic>

20
namespace isc {
21 22
namespace asiolink {

23 24 25 26 27 28 29
/// This class holds a call back function of asynchronous operations.
/// To ensure the object is alive while an asynchronous operation refers
/// to it, we use shared_ptr and enable_shared_from_this.
/// The object will be destructed in case IntervalTimer has been destructed
/// and no asynchronous operation refers to it.
/// Please follow the link to get an example:
/// http://think-async.com/asio/asio-1.4.8/doc/asio/tutorial/tutdaytime3.html#asio.tutorial.tutdaytime3.the_tcp_connection_class
30 31 32
class IntervalTimerImpl :
    public boost::enable_shared_from_this<IntervalTimerImpl>
{
33 34 35 36 37 38 39
private:
    // prohibit copy
    IntervalTimerImpl(const IntervalTimerImpl& source);
    IntervalTimerImpl& operator=(const IntervalTimerImpl& source);
public:
    IntervalTimerImpl(IOService& io_service);
    ~IntervalTimerImpl();
40 41 42
    void setup(const IntervalTimer::Callback& cbfunc, const long interval,
               const IntervalTimer::Mode& interval_mode
               = IntervalTimer::REPEATING);
43
    void callback(const boost::system::error_code& error);
44 45 46 47 48 49 50 51 52 53 54
    void cancel() {
        timer_.cancel();
        interval_ = 0;
    }
    long getInterval() const { return (interval_); }
private:
    // a function to update timer_ when it expires
    void update();
    // a function to call back when timer_ expires
    IntervalTimer::Callback cbfunc_;
    // interval in milliseconds
55
    std::atomic<long> interval_;
56
    // asio timer
57
    boost::asio::deadline_timer timer_;
58 59 60 61

    // Controls how the timer behaves after expiration.
    IntervalTimer::Mode mode_;

62 63 64
    // interval_ will be set to this value in destructor in order to detect
    // use-after-free type of bugs.
    static const long INVALIDATED_INTERVAL = -1;
65 66 67
};

IntervalTimerImpl::IntervalTimerImpl(IOService& io_service) :
68 69
    interval_(0), timer_(io_service.get_io_service()),
    mode_(IntervalTimer::REPEATING)
70 71
{}

72 73 74
IntervalTimerImpl::~IntervalTimerImpl() {
    interval_ = INVALIDATED_INTERVAL;
}
75 76 77

void
IntervalTimerImpl::setup(const IntervalTimer::Callback& cbfunc,
78 79
                         const long interval,
                         const IntervalTimer::Mode& mode)
80
{
81 82
    // Interval should not be less than 0.
    if (interval < 0) {
83 84 85 86 87 88 89 90 91
        isc_throw(isc::BadValue, "Interval should not be less than or "
                                 "equal to 0");
    }
    // Call back function should not be empty.
    if (cbfunc.empty()) {
        isc_throw(isc::InvalidParameter, "Callback function is empty");
    }
    cbfunc_ = cbfunc;
    interval_ = interval;
92 93
    mode_ = mode;

94 95 96 97 98 99 100 101 102 103
    // Set initial expire time.
    // At this point the timer is not running yet and will not expire.
    // After calling IOService::run(), the timer will expire.
    update();
}

void
IntervalTimerImpl::update() {
    try {
        // Update expire time to (current time + interval_).
104
        timer_.expires_from_now(boost::posix_time::millisec(long(interval_)));
105
        // Reset timer.
106
        // Pass a function bound with a shared_ptr to this.
107 108
        timer_.async_wait(boost::bind(&IntervalTimerImpl::callback,
                                      shared_from_this(),
109
                                      boost::asio::placeholders::error));
110
    } catch (const boost::system::system_error& e) {
111 112 113 114
        isc_throw(isc::Unexpected, "Failed to update timer: " << e.what());
    } catch (const boost::bad_weak_ptr&) {
        // Can't happen. It means a severe internal bug.
        assert(0);
115 116 117 118
    }
}

void
119
IntervalTimerImpl::callback(const boost::system::error_code& ec) {
120
    assert(interval_ != INVALIDATED_INTERVAL);
121 122 123
    if (interval_ == 0 || ec) {
        // timer has been canceled. Do nothing.
    } else {
124 125 126 127 128
        // If we should repeat, set next expire time.
        if (mode_ == IntervalTimer::REPEATING) {
            update();
        }

129 130
        // Invoke the call back function.
        cbfunc_();
131 132 133
    }
}

134 135 136
IntervalTimer::IntervalTimer(IOService& io_service) :
    impl_(new IntervalTimerImpl(io_service))
{}
137 138

IntervalTimer::~IntervalTimer() {
139 140
    // Cancel the timer to make sure cbfunc_() will not be called any more.
    cancel();
141 142 143
}

void
144 145 146
IntervalTimer::setup(const Callback& cbfunc, const long interval,
                     const IntervalTimer::Mode& mode) {
    return (impl_->setup(cbfunc, interval, mode));
147 148 149 150 151 152 153 154 155 156 157 158
}

void
IntervalTimer::cancel() {
    impl_->cancel();
}

long
IntervalTimer::getInterval() const {
    return (impl_->getInterval());
}

159 160
} // namespace asiolink
} // namespace isc