Line data Source code
1 : /* 2 : * Copyright (C) 2016 Intel Corporation. All rights reserved. 3 : * 4 : * This file is free software: you can redistribute it and/or modify it 5 : * under the terms of the GNU General Public License as published by the 6 : * Free Software Foundation, either version 3 of the License, or 7 : * (at your option) any later version. 8 : * 9 : * This file is distributed in the hope that it will be useful, but 10 : * WITHOUT ANY WARRANTY; without even the implied warranty of 11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 : * See the GNU General Public License for more details. 13 : * 14 : * You should have received a copy of the GNU General Public License along 15 : * with this program. If not, see <http://www.gnu.org/licenses/>. 16 : */ 17 : #include "PollerThread.h" 18 : 19 : #include <algorithm> 20 : #include <poll.h> 21 : #include <sys/epoll.h> 22 : #include <sys/timerfd.h> 23 : 24 : #include <AP_Math/AP_Math.h> 25 : 26 : namespace Linux { 27 : 28 0 : void TimerPollable::on_can_read() 29 : { 30 0 : if (_removeme) { 31 0 : return; 32 : } 33 : 34 0 : uint64_t nevents = 0; 35 0 : int r = read(_fd, &nevents, sizeof(nevents)); 36 0 : if (r < 0) { 37 0 : return; 38 : } 39 : 40 0 : if (_wrapper) { 41 0 : _wrapper->start_cb(); 42 : } 43 : 44 0 : _cb(); 45 : 46 0 : if (_wrapper) { 47 0 : _wrapper->end_cb(); 48 : } 49 : } 50 : 51 0 : bool TimerPollable::setup_timer(uint32_t timeout_usec) 52 : { 53 0 : if (_fd >= 0) { 54 0 : return false; 55 : } 56 : 57 0 : _fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC|TFD_NONBLOCK); 58 0 : if (_fd < 0) { 59 0 : return false; 60 : } 61 : 62 0 : if (!adjust_timer(timeout_usec)) { 63 0 : ::close(_fd); 64 0 : _fd = -1; 65 0 : return false; 66 : } 67 : 68 0 : return true; 69 : } 70 : 71 0 : bool TimerPollable::adjust_timer(uint32_t timeout_usec) 72 : { 73 0 : if (_fd < 0) { 74 0 : return false; 75 : } 76 : 77 0 : struct itimerspec spec = { }; 78 : 79 0 : spec.it_interval.tv_nsec = timeout_usec * AP_NSEC_PER_USEC; 80 0 : spec.it_value.tv_nsec = timeout_usec * AP_NSEC_PER_USEC; 81 : 82 0 : if (timerfd_settime(_fd, 0, &spec, nullptr) < 0) { 83 0 : return false; 84 : } 85 : 86 0 : return true; 87 : } 88 : 89 0 : TimerPollable *PollerThread::add_timer(TimerPollable::PeriodicCb cb, 90 : TimerPollable::WrapperCb *wrapper, 91 : uint32_t timeout_usec) 92 : { 93 0 : if (!_poller) { 94 0 : return nullptr; 95 : } 96 0 : TimerPollable *p = NEW_NOTHROW TimerPollable(cb, wrapper); 97 0 : if (!p || !p->setup_timer(timeout_usec) || 98 0 : !_poller.register_pollable(p, POLLIN)) { 99 0 : delete p; 100 0 : return nullptr; 101 : } 102 : 103 0 : _timers.push_back(p); 104 : 105 0 : return p; 106 : } 107 : 108 0 : bool PollerThread::adjust_timer(TimerPollable *p, uint32_t timeout_usec) 109 : { 110 : /* Make sure the handle points to a valid timer */ 111 0 : auto it = std::find(_timers.begin(), _timers.end(), p); 112 0 : if (it == _timers.end()) { 113 0 : return false; 114 : } 115 : 116 0 : return (*it)->adjust_timer(timeout_usec); 117 : } 118 : 119 1 : void PollerThread::_cleanup_timers() 120 : { 121 1 : if (!_poller) { 122 0 : return; 123 : } 124 : 125 1 : for (auto it = _timers.begin(); it != _timers.end(); it++) { 126 0 : TimerPollable *p = *it; 127 0 : if (p->_removeme) { 128 0 : _timers.erase(it); 129 0 : _poller.unregister_pollable(p); 130 0 : delete p; 131 : } 132 : } 133 : } 134 : 135 1 : void PollerThread::mainloop() 136 : { 137 1 : if (!_poller) { 138 0 : return; 139 : } 140 : 141 2 : while (!_should_exit) { 142 1 : _poller.poll(); 143 1 : _cleanup_timers(); 144 : } 145 : 146 1 : _started = false; 147 1 : _should_exit = false; 148 : } 149 : 150 1 : bool PollerThread::stop() 151 : { 152 1 : if (!is_started()) { 153 0 : return false; 154 : } 155 : 156 1 : _should_exit = true; 157 1 : _poller.wakeup(); 158 : 159 1 : return true; 160 : } 161 : 162 : }