2021-06-17 14:43:29 +02:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @file TimerQueue.cc
|
|
|
|
* @author An Tao
|
|
|
|
*
|
|
|
|
* Public header file in trantor lib.
|
|
|
|
*
|
|
|
|
* Copyright 2018, An Tao. All rights reserved.
|
|
|
|
* Use of this source code is governed by a BSD-style license
|
|
|
|
* that can be found in the License file.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <trantor/net/EventLoop.h>
|
|
|
|
|
|
|
|
#include "Channel.h"
|
2021-06-17 14:53:13 +02:00
|
|
|
#include "TimerQueue.h"
|
2021-06-17 14:43:29 +02:00
|
|
|
#ifdef __linux__
|
|
|
|
#include <sys/timerfd.h>
|
|
|
|
#endif
|
|
|
|
#include <string.h>
|
|
|
|
#include <iostream>
|
|
|
|
#ifndef _WIN32
|
|
|
|
#include <unistd.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
using namespace trantor;
|
|
|
|
#ifdef __linux__
|
2021-06-17 14:53:13 +02:00
|
|
|
static int createTimerfd() {
|
|
|
|
int timerfd = ::timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
|
|
|
|
if (timerfd < 0) {
|
|
|
|
std::cerr << "create timerfd failed!" << std::endl;
|
|
|
|
}
|
|
|
|
return timerfd;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct timespec howMuchTimeFromNow(const TimePoint &when) {
|
|
|
|
auto microSeconds = std::chrono::duration_cast<std::chrono::microseconds>(
|
|
|
|
when - std::chrono::steady_clock::now())
|
|
|
|
.count();
|
|
|
|
if (microSeconds < 100) {
|
|
|
|
microSeconds = 100;
|
|
|
|
}
|
|
|
|
struct timespec ts;
|
|
|
|
ts.tv_sec = static_cast<time_t>(microSeconds / 1000000);
|
|
|
|
ts.tv_nsec = static_cast<long>((microSeconds % 1000000) * 1000);
|
|
|
|
return ts;
|
|
|
|
}
|
|
|
|
static void resetTimerfd(int timerfd, const TimePoint &expiration) {
|
|
|
|
// wake up loop by timerfd_settime()
|
|
|
|
struct itimerspec newValue;
|
|
|
|
struct itimerspec oldValue;
|
|
|
|
memset(&newValue, 0, sizeof(newValue));
|
|
|
|
memset(&oldValue, 0, sizeof(oldValue));
|
|
|
|
newValue.it_value = howMuchTimeFromNow(expiration);
|
|
|
|
int ret = ::timerfd_settime(timerfd, 0, &newValue, &oldValue);
|
|
|
|
if (ret) {
|
|
|
|
// LOG_SYSERR << "timerfd_settime()";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
static void readTimerfd(int timerfd, const TimePoint &) {
|
|
|
|
uint64_t howmany;
|
|
|
|
ssize_t n = ::read(timerfd, &howmany, sizeof howmany);
|
|
|
|
if (n != sizeof howmany) {
|
|
|
|
LOG_ERROR << "TimerQueue::handleRead() reads " << n
|
|
|
|
<< " bytes instead of 8";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TimerQueue::handleRead() {
|
|
|
|
loop_->assertInLoopThread();
|
|
|
|
const auto now = std::chrono::steady_clock::now();
|
|
|
|
readTimerfd(timerfd_, now);
|
|
|
|
|
|
|
|
std::vector<TimerPtr> expired = getExpired(now);
|
|
|
|
|
|
|
|
callingExpiredTimers_ = true;
|
|
|
|
// cancelingTimers_.clear();
|
|
|
|
// safe to callback outside critical section
|
|
|
|
for (auto const &timerPtr : expired) {
|
|
|
|
if (timerIdSet_.find(timerPtr->id()) != timerIdSet_.end()) {
|
|
|
|
timerPtr->run();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
callingExpiredTimers_ = false;
|
|
|
|
|
|
|
|
reset(expired, now);
|
2021-06-17 14:43:29 +02:00
|
|
|
}
|
|
|
|
#else
|
2021-06-17 14:53:13 +02:00
|
|
|
static int64_t howMuchTimeFromNow(const TimePoint &when) {
|
|
|
|
auto microSeconds = std::chrono::duration_cast<std::chrono::microseconds>(
|
|
|
|
when - std::chrono::steady_clock::now())
|
|
|
|
.count();
|
|
|
|
if (microSeconds < 1000) {
|
|
|
|
microSeconds = 1000;
|
|
|
|
}
|
|
|
|
return microSeconds / 1000;
|
|
|
|
}
|
|
|
|
void TimerQueue::processTimers() {
|
|
|
|
loop_->assertInLoopThread();
|
|
|
|
const auto now = std::chrono::steady_clock::now();
|
|
|
|
|
|
|
|
std::vector<TimerPtr> expired = getExpired(now);
|
|
|
|
|
|
|
|
callingExpiredTimers_ = true;
|
|
|
|
// cancelingTimers_.clear();
|
|
|
|
// safe to callback outside critical section
|
|
|
|
for (auto const &timerPtr : expired) {
|
|
|
|
if (timerIdSet_.find(timerPtr->id()) != timerIdSet_.end()) {
|
|
|
|
timerPtr->run();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
callingExpiredTimers_ = false;
|
|
|
|
|
|
|
|
reset(expired, now);
|
2021-06-17 14:43:29 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
///////////////////////////////////////
|
2021-06-17 14:53:13 +02:00
|
|
|
TimerQueue::TimerQueue(EventLoop *loop) :
|
|
|
|
loop_(loop),
|
2021-06-17 14:43:29 +02:00
|
|
|
#ifdef __linux__
|
2021-06-17 14:53:13 +02:00
|
|
|
timerfd_(createTimerfd()),
|
|
|
|
timerfdChannelPtr_(new Channel(loop, timerfd_)),
|
2021-06-17 14:43:29 +02:00
|
|
|
#endif
|
2021-06-17 14:53:13 +02:00
|
|
|
timers_(),
|
|
|
|
callingExpiredTimers_(false) {
|
2021-06-17 14:43:29 +02:00
|
|
|
#ifdef __linux__
|
2021-06-17 14:53:13 +02:00
|
|
|
timerfdChannelPtr_->setReadCallback(
|
|
|
|
std::bind(&TimerQueue::handleRead, this));
|
|
|
|
// we are always reading the timerfd, we disarm it with timerfd_settime.
|
|
|
|
timerfdChannelPtr_->enableReading();
|
2021-06-17 14:43:29 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#ifdef __linux__
|
2021-06-17 14:53:13 +02:00
|
|
|
void TimerQueue::reset() {
|
|
|
|
loop_->runInLoop([this]() {
|
|
|
|
timerfdChannelPtr_->disableAll();
|
|
|
|
timerfdChannelPtr_->remove();
|
|
|
|
close(timerfd_);
|
|
|
|
timerfd_ = createTimerfd();
|
|
|
|
timerfdChannelPtr_ = std::make_shared<Channel>(loop_, timerfd_);
|
|
|
|
timerfdChannelPtr_->setReadCallback(
|
|
|
|
std::bind(&TimerQueue::handleRead, this));
|
|
|
|
// we are always reading the timerfd, we disarm it with timerfd_settime.
|
|
|
|
timerfdChannelPtr_->enableReading();
|
|
|
|
if (!timers_.empty()) {
|
|
|
|
const auto nextExpire = timers_.top()->when();
|
|
|
|
resetTimerfd(timerfd_, nextExpire);
|
|
|
|
}
|
|
|
|
});
|
2021-06-17 14:43:29 +02:00
|
|
|
}
|
|
|
|
#endif
|
2021-06-17 14:53:13 +02:00
|
|
|
TimerQueue::~TimerQueue() {
|
2021-06-17 14:43:29 +02:00
|
|
|
#ifdef __linux__
|
2021-06-17 14:53:13 +02:00
|
|
|
auto chlPtr = timerfdChannelPtr_;
|
|
|
|
auto fd = timerfd_;
|
|
|
|
loop_->runInLoop([chlPtr, fd]() {
|
|
|
|
chlPtr->disableAll();
|
|
|
|
chlPtr->remove();
|
|
|
|
::close(fd);
|
|
|
|
});
|
2021-06-17 14:43:29 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
TimerId TimerQueue::addTimer(const TimerCallback &cb,
|
2021-06-17 14:53:13 +02:00
|
|
|
const TimePoint &when,
|
|
|
|
const TimeInterval &interval) {
|
|
|
|
std::shared_ptr<Timer> timerPtr =
|
|
|
|
std::make_shared<Timer>(cb, when, interval);
|
2021-06-17 14:43:29 +02:00
|
|
|
|
2021-06-17 14:53:13 +02:00
|
|
|
loop_->runInLoop([this, timerPtr]() { addTimerInLoop(timerPtr); });
|
|
|
|
return timerPtr->id();
|
2021-06-17 14:43:29 +02:00
|
|
|
}
|
|
|
|
TimerId TimerQueue::addTimer(TimerCallback &&cb,
|
2021-06-17 14:53:13 +02:00
|
|
|
const TimePoint &when,
|
|
|
|
const TimeInterval &interval) {
|
|
|
|
std::shared_ptr<Timer> timerPtr =
|
|
|
|
std::make_shared<Timer>(std::move(cb), when, interval);
|
|
|
|
|
|
|
|
loop_->runInLoop([this, timerPtr]() { addTimerInLoop(timerPtr); });
|
|
|
|
return timerPtr->id();
|
|
|
|
}
|
|
|
|
void TimerQueue::addTimerInLoop(const TimerPtr &timer) {
|
|
|
|
loop_->assertInLoopThread();
|
|
|
|
timerIdSet_.insert(timer->id());
|
|
|
|
if (insert(timer)) {
|
2021-06-17 14:43:29 +02:00
|
|
|
// the earliest timer changed
|
|
|
|
#ifdef __linux__
|
2021-06-17 14:53:13 +02:00
|
|
|
resetTimerfd(timerfd_, timer->when());
|
2021-06-17 14:43:29 +02:00
|
|
|
#endif
|
2021-06-17 14:53:13 +02:00
|
|
|
}
|
2021-06-17 14:43:29 +02:00
|
|
|
}
|
|
|
|
|
2021-06-17 14:53:13 +02:00
|
|
|
void TimerQueue::invalidateTimer(TimerId id) {
|
|
|
|
loop_->runInLoop([this, id]() { timerIdSet_.erase(id); });
|
2021-06-17 14:43:29 +02:00
|
|
|
}
|
|
|
|
|
2021-06-17 14:53:13 +02:00
|
|
|
bool TimerQueue::insert(const TimerPtr &timerPtr) {
|
|
|
|
loop_->assertInLoopThread();
|
|
|
|
bool earliestChanged = false;
|
|
|
|
if (timers_.size() == 0 || *timerPtr < *timers_.top()) {
|
|
|
|
earliestChanged = true;
|
|
|
|
}
|
|
|
|
timers_.push(timerPtr);
|
|
|
|
// std::cout<<"after push new
|
|
|
|
// timer:"<<timerPtr->when().microSecondsSinceEpoch()/1000000<<std::endl;
|
|
|
|
return earliestChanged;
|
2021-06-17 14:43:29 +02:00
|
|
|
}
|
|
|
|
#ifndef __linux__
|
2021-06-17 14:53:13 +02:00
|
|
|
int64_t TimerQueue::getTimeout() const {
|
|
|
|
loop_->assertInLoopThread();
|
|
|
|
if (timers_.empty()) {
|
|
|
|
return 10000;
|
|
|
|
} else {
|
|
|
|
return howMuchTimeFromNow(timers_.top()->when());
|
|
|
|
}
|
2021-06-17 14:43:29 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2021-06-17 14:53:13 +02:00
|
|
|
std::vector<TimerPtr> TimerQueue::getExpired(const TimePoint &now) {
|
|
|
|
std::vector<TimerPtr> expired;
|
|
|
|
while (!timers_.empty()) {
|
|
|
|
if (timers_.top()->when() < now) {
|
|
|
|
expired.push_back(timers_.top());
|
|
|
|
timers_.pop();
|
|
|
|
} else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return expired;
|
2021-06-17 14:43:29 +02:00
|
|
|
}
|
|
|
|
void TimerQueue::reset(const std::vector<TimerPtr> &expired,
|
2021-06-17 14:53:13 +02:00
|
|
|
const TimePoint &now) {
|
|
|
|
loop_->assertInLoopThread();
|
|
|
|
for (auto const &timerPtr : expired) {
|
|
|
|
auto iter = timerIdSet_.find(timerPtr->id());
|
|
|
|
if (iter != timerIdSet_.end()) {
|
|
|
|
if (timerPtr->isRepeat()) {
|
|
|
|
timerPtr->restart(now);
|
|
|
|
insert(timerPtr);
|
|
|
|
} else {
|
|
|
|
timerIdSet_.erase(iter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-06-17 14:43:29 +02:00
|
|
|
#ifdef __linux__
|
2021-06-17 14:53:13 +02:00
|
|
|
if (!timers_.empty()) {
|
|
|
|
const auto nextExpire = timers_.top()->when();
|
|
|
|
resetTimerfd(timerfd_, nextExpire);
|
|
|
|
}
|
2021-06-17 14:43:29 +02:00
|
|
|
#endif
|
|
|
|
}
|