summaryrefslogtreecommitdiffstats
path: root/src/lib/asiolink/interval_timer.cc
blob: 3789b3bc9e4f841648b36f5ed31777cc6171356e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// Copyright (C) 2011, 2014-2015 Internet Systems Consortium, Inc. ("ISC")
//
// 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/.

#include <config.h>

#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>

#include <exceptions/exceptions.h>

#include <boost/asio.hpp>
#include <asiolink/interval_timer.h>
#include <asiolink/io_service.h>

namespace isc {
namespace asiolink {

/// 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
class IntervalTimerImpl :
    public boost::enable_shared_from_this<IntervalTimerImpl>
{
private:
    // prohibit copy
    IntervalTimerImpl(const IntervalTimerImpl& source);
    IntervalTimerImpl& operator=(const IntervalTimerImpl& source);
public:
    IntervalTimerImpl(IOService& io_service);
    ~IntervalTimerImpl();
    void setup(const IntervalTimer::Callback& cbfunc, const long interval,
               const IntervalTimer::Mode& interval_mode
               = IntervalTimer::REPEATING);
    void callback(const boost::system::error_code& error);
    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
    long interval_;
    // asio timer
    boost::asio::deadline_timer timer_;

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

    // 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;
};

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

IntervalTimerImpl::~IntervalTimerImpl() {
    interval_ = INVALIDATED_INTERVAL;
}

void
IntervalTimerImpl::setup(const IntervalTimer::Callback& cbfunc,
                         const long interval,
                         const IntervalTimer::Mode& mode)
{
    // Interval should not be less than or equal to 0.
    if (interval <= 0) {
        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;
    mode_ = mode;

    // 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_).
        timer_.expires_from_now(boost::posix_time::millisec(interval_));
        // Reset timer.
        // Pass a function bound with a shared_ptr to this.
        timer_.async_wait(boost::bind(&IntervalTimerImpl::callback,
                                      shared_from_this(),
                                      boost::asio::placeholders::error));
    } catch (const boost::system::system_error& e) {
        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);
    }
}

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

        // Invoke the call back function.
        cbfunc_();
    }
}

IntervalTimer::IntervalTimer(IOService& io_service) :
    impl_(new IntervalTimerImpl(io_service))
{}

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

void
IntervalTimer::setup(const Callback& cbfunc, const long interval,
                     const IntervalTimer::Mode& mode) {
    return (impl_->setup(cbfunc, interval, mode));
}

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

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

} // namespace asiolink
} // namespace isc