1 muduo::net::EchoServer class reference
Collaboration diagram of EchoServer:
[legend]
Public member function | |
---|---|
EchoServer (EventLoop *loop, const InetAddress &listenAddr) | |
void | start () |
Private member function | |
---|---|
void | onConnection (const TcpConnectionPtr &conn) |
void | onMessage (const TcpConnectionPtr &conn, Buffer *buf, Timestamp time) |
Private property | |
---|---|
EventLoop * | loop_ |
TcpServer | server_ |
- When an event arrives, it will call channel::handleEvent() and Channel::handleEventWithGuard(Timestamp receiveTime) for processing. The channel class is responsible for event registration and response encapsulation
void Channel::handleEventWithGuard(Timestamp receiveTime) { eventHandling_ = true; // Place status in processing event if ((revents_ & POLLHUP) && !(revents_ & POLLIN)) // If it is half closed and there is no data to read { if (logHup_) { LOG_WARN << "Channel::handle_event() POLLHUP"; } if (closeCallback_) closeCallback_(); // Closed callback function } if (revents_ & (POLLIN | POLLPRI | POLLRDHUP)) // If it is a read event, you need to pass in an additional time stamp object { if (readCallback_) readCallback_(receiveTime); } if (revents_ & POLLOUT) // If it is a write event { if (writeCallback_) // And the event has been registered writeCallback_(); } eventHandling_ = false; }
- Update and register channel void Channel::update() call void EventLoop::updateChannel(Channel *channel)
void EventLoop::updateChannel(Channel *channel){ assert(channel->ownerLoop() == this); // Assert the loop object to which the channel belongs assertInLoopThread(); // Is loop in the open event loop poller_->updateChannel(channel); // }
Calling void pollpolller:: updateChannel (channel * channel) is a pure virtual function that overrides the updateChannel of the parent class
void PollPoller::fillActiveChannels(int numEvents,ChannelList *activeChannels) const { for (PollFdList::const_iterator pfd = pollfds_.begin();pfd != pollfds_.end() && numEvents > 0; ++pfd) // Traverse vector container STD:: vector < struct pollfd > { if (pfd->revents > 0) { --numEvents; ChannelMap::const_iterator ch = channels_.find(pfd->fd); // Find channels through file descriptors assert(ch != channels_.end()); // Asserts whether the channel can be found Channel *channel = ch->second; // Get event type assert(channel->fd() == pfd->fd); // Assign a value to the file descriptor of the channel channel->set_revents(pfd->revents); // Set event type activeChannels->push_back(channel); } } }
Mechanism for registering EPollPoller events
class EPollPoller : public Poller { public: void update(int operation, Channel *channel); private: typedef std::vector<struct epoll_event> EventList; typedef std::map<int, Channel *> ChannelMap; // Registration of file descriptors and pipeline events EventList events_; ChannelMap channels_; };
The difference between the method I wrote and this method is that ChannelMap uses the object-based callback function registration method. I use the virtual function method. In terms of performance and efficiency, it is better to use the callback function method, but it is difficult to understand from the code reading.
- EventLoop constructor
EventLoop::EventLoop() : looping_(false), quit_(false), eventHandling_(false), threadId_(CurrentThread::tid()), poller_(Poller::newDefaultPoller(this)), // poller objects will be built, and different io multiplexing objects will be generated according to environment variables currentActiveChannel_(NULL){} Poller* Poller::newDefaultPoller(EventLoop* loop) { if (::getenv("MUDUO_USE_POLL")) { return new PollPoller(loop); // Generate poll } else { return new EPollPoller(loop);// Generate epoll } }
It mainly depends on the encapsulation of epoll
If there is a return ready event fd, press the return collection and the pipeline responsible for event registration into the function fillactivchannels
Not clear enough. Last pseudo code
int epfd = epoll_create(1); epollAddFd(sfd, epfd); epollAddFd(exitPipe[0],epfd); // Put the pipeline reader into the listening queue struct epoll_event evs[2]; // Collection of events int newFd = 0; int sums = 0; while(1){ // Turn on uninterrupted waiting polling - 1, that is, do not set timeout sums = epoll_wait(epfd, evs, 2, -1); for(int i = 0; i < sums; ++i) { if(evs[i].data.fd == sfd) // evs[i].data.fd is to monitor the returned ready file descriptor and compare it with what needs attention { newFd = accept(sfd, NULL, NULL); printf("The connection was established successfully\n"); } } }
struct epoll_event evs[2] is equivalent to & (* (events_. Begin()))
sfd is equivalent to ChannelList *activeChannels
Timestamp EPollPoller::poll(int timeoutMs, ChannelList *activeChannels) { int numEvents = ::epoll_wait(epollfd_, & (*(events_.begin() ) ),static_cast<int>(events_.size()),timeoutMs); Timestamp now(Timestamp::now()); if (numEvents > 0) { fillActiveChannels(numEvents, activeChannels); // In this function if (implicit_cast<size_t>(numEvents) == events_.size()) // If the vector has reached its capacity limit, double its capacity { events_.resize(events_.size() * 2); } } else if (numEvents == 0) { LOG_TRACE << " nothing happended"; } else { LOG_SYSERR << "EPollPoller::poll()"; } return now; }
Fillactivchannels function
void EPollPoller::fillActiveChannels(int numEvents,ChannelList *activeChannels) const { assert(implicit_cast<size_t>(numEvents) <= events_.size()); // The ready event fd returned by the assertion cannot be out of order for (unsigned long i = 0; i < static_cast<unsigned>(numEvents); ++i) // Circular scan ready file descriptor { Channel *channel = static_cast<Channel *>(events_[i].data.ptr); // Return the registered event list. ptr is some key information that can be attached to the epoll red black tree. You also need to determine which events are returned #ifndef NDEBUG int fd = channel->fd(); ChannelMap::const_iterator it = channels_.find(fd); assert(it != channels_.end()); assert(it->second == channel); #endif channel->set_revents(static_cast<int>(events_[i].events)); // Set the events registered for each pipeline activeChannels->push_back(channel); // Add pipeline events to the pipeline collection } }
Then it is used in this way in the call, and the encapsulation is very complex
while (!quit_) { activeChannels_.clear(); pollReturnTime_ = poller_->poll(kPollTimeMs, &activeChannels_); // The events returned by epoll should be saved in this list if (Logger::logLevel() <= Logger::TRACE) { printActiveChannels(); } eventHandling_ = true; for (ChannelList::iterator it = activeChannels_.begin(); it != activeChannels_.end(); ++it) { currentActiveChannel_ = *it; currentActiveChannel_->handleEvent(pollReturnTime_); } currentActiveChannel_ = NULL; eventHandling_ = false; // Termination event handling ID }
- updateChannel function, used to update and add events
void EPollPoller::updateChannel(Channel *channel) { Poller::assertInLoopThread(); // Whether the assertion is in the io thread LOG_TRACE << "fd = " << channel->fd() << " events = " << channel->events(); // Output file descriptor and event type const int index = channel->index(); if (index == kNew || index == kDeleted) { // a new one, add with EPOLL_CTL_ADD int fd = channel->fd(); if (index == kNew) { assert(channels_.find(fd) == channels_.end()); // Assert that the file descriptor has not been registered in the pipeline event channels_[fd] = channel; // Registering events is similar to the method I wrote myself ["cmd command"] = "task_data * parent class pointer" } else // index == kDeleted { assert(channels_.find(fd) != channels_.end()); assert(channels_[fd] == channel); // Assert again whether the registration has been successful } channel->set_index(kAdded); // Set the current state of this channel to the added state update(EPOLL_CTL_ADD, channel); // epoll_ctr_add to the red black tree } else { // ...... EPOLL_CTL_MOD } }
- update adds the event to the red black tree
void EPollPoller::update(int operation, Channel *channel) { struct epoll_event event; bzero(&event, sizeof event); event.events = static_cast<uint32_t>(channel->events()); // epoll focuses on reading and writing, timeout, and various events, signalfd and pipeline fd event.data.ptr = channel; // Take the channel as the carried data and return the channel. Then you will know which channel generated the event int fd = channel->fd(); // File descriptor // Register on red black tree if (::epoll_ctl(epollfd_, operation, fd, &event) < 0) { if (operation == EPOLL_CTL_DEL) { LOG_SYSERR << "epoll_ctl op=" << operation << " fd=" << fd; } else { LOG_SYSFATAL << "epoll_ctl op=" << operation << " fd=" << fd; } } }
- Delete event attention on red black tree
void EventLoop::removeChannel(Channel *channel) { assert(channel->ownerLoop() == this); assertInLoopThread(); if (eventHandling_) { assert(currentActiveChannel_ == channel || std::find(activeChannels_.begin(), activeChannels_.end(), channel) == activeChannels_.end()); } poller_->removeChannel(channel); } // Called by removeChannel overridden by poller void EPollPoller::removeChannel(Channel *channel) { Poller::assertInLoopThread(); int fd = channel->fd(); LOG_TRACE << "fd = " << fd; assert(channels_.find(fd) != channels_.end()); assert(channels_[fd] == channel); assert(channel->isNoneEvent()); int index = channel->index(); assert(index == kAdded || index == kDeleted); // Asserts whether to add or remove identities size_t n = channels_.erase(fd); (void)n; assert(n == 1); if (index == kAdded) { update(EPOLL_CTL_DEL, channel); } channel->set_index(kNew); }
2 Realization of timer
Timerld,Timer,TimerQueue
The latter two classes are internal implementation details
muduo::net::Timer class reference or Muduo / base / timestamp CC operation
#include <Timer.h>
Public member function | |
---|---|
Timer (const TimerCallback &cb, Timestamp when, double interval) | |
void | run () const |
Timestamp | expiration () const |
bool | repeat () const |
int64_t | sequence () const |
void | restart (Timestamp now) |
Static Public member function | |
---|---|
static int64_t | numCreated () |
Private property | |
---|---|
const TimerCallback | callback_ Timer activated callback function |
Timestamp | expiration_ Next time interval |
const double | interval_ Timeout interval, if it is a one-time setting, 0 |
const bool | repeat_ Repeat |
const int64_t | sequence_ Timer serial number |
Static Private property | |
---|---|
static AtomicInt64 | s_numCreated_ Timer count |
#ifdef __GXX_EXPERIMENTAL_CXX0X__ Timer(TimerCallback&& cb, Timestamp when, double interval) : callback_(cb), expiration_(when), interval_(interval), repeat_(interval > 0.0), sequence_(s_numCreated_.incrementAndGet()) // Timer serial number = timer count + 1, counting starts from 0 // incrementAndGet is an atomic operation of one plus one { } void run() const { callback_(); // Execute callback function } // Reset Timer void Timer::restart(Timestamp now) { if (repeat_) // Recalculate the next timeout { expiration_ = addTime(now, interval_); // Add the current time + time interval to get the next time // This object has only one 64 bit integer, so it's too lazy to pass by reference. Directly put the value into an 8-byte register } else { expiration_ = Timestamp::invalid(); // If it is not a repeated timer, it means illegal time } }
In terms of user operation, the timer is not called directly, but in EventLoop
- runAt runs the timer at a certain time
- runAfter runs the timer after a period of time
- runEvery runs the timer at regular intervals
- Cancel cancel timer
TimerQueue internally maintains a timer list. Timer only operates on timing logic and does not directly call timer related functions. It is a high-level Abstract encapsulation
muduo::net::TimerQueue class reference
#include <TimerQueue.h>
Inherited from noncopyable
Collaboration diagram of muduo::net::TimerQueue:
[legend]
Public member function
Public member function | |
---|---|
TimerQueue (EventLoop *loop | |
~TimerQueue () | |
TimerId | addTimer (const TimerCallback &cb, Timestamp when, double interval) |
void | cancel (TimerId timerId) |
Private type | |
---|---|
typedef std::pair< Timestamp, Timer * > | Entry |
typedef std::set< Entry > | TimerList |
typedef std::pair< Timer *, int64_t > | ActiveTimer |
typedef std::set< ActiveTimer > | ActiveTimerSet |
Private member function | |
---|---|
void | addTimerInLoop (Timer *timer) |
void | cancelInLoop (TimerId timerId) |
void | handleRead () |
std::vector< Entry > | getExpired (Timestamp now) returns the list of timers that have timed out |
void | reset (const std::vector< Entry > &expired, Timestamp now) |
bool | insert (Timer *timer) |
Private property | |
---|---|
EventLoop * | loop_ eventloop to which it belongs |
const int | timerfd_ The file descriptor of the timer is saved |
Channel | timerfdChannel_ The channel of the timer. When the timer expires, a readable event is generated, and the callback function handleRead() |
TimerList | timers_ List of timers sorted by expiration time |
ActiveTimerSet | activeTimers List of timers sorted by object address |
bool | callingExpiredTimers_ Whether to process the call processing timeout timer |
ActiveTimerSet | cancelingTimers_ List of cancelled timers |
The TimerQueue value provides addTimer and cancel to add and cancel timers
typedef std::pair< Timestamp, Timer *> store timer object and address of timer
typedef std::set< Entry >Stored is a unique key value pair
typedef std::pair<Timestamp, Timer*> Entry; typedef std::set<Entry> TimerList; // List of stored timers typedef std::pair<Timer*, int64_t> ActiveTimer; // Timer address and serial number typedef std::set<ActiveTimer> ActiveTimerSet; // The storage is the same. Both sets are timer lists, but one is sorted by time and the other by address
It can be called by other threads, which means it can belong to different eventloop calls
You need to be able to quickly find expired timers according to the current time, and you also need to efficiently add and delete timers, so use binary tree
Add a timer to the two exposed interfaces addTimer
///Constructor TimerQueue::TimerQueue(EventLoop *loop) : loop_(loop), timerfd_(createTimerfd()), // Create a timer timerfdChannel_(loop, timerfd_), timers_(), callingExpiredTimers_(false) { timerfdChannel_.setReadCallback(boost::bind(&TimerQueue::handleRead, this)); timerfdChannel_.enableReading(); }
timerfdChannel_.enableReading();
Add the file descriptor of the timer to the epoller class
->update->UpdateChannel->updateChannel
/** * @brief Add a timer * * @param cb Callback function of timer * @param when Timeout event * @param interval Interval time * @return TimerId */ TimerId TimerQueue::addTimer(const TimerCallback &cb, Timestamp when, double interval) { Timer *timer = new Timer(cb, when, interval); // It is thread safe to construct timer objects and register callback functions /* Original writing, thread safety loop_->runInLoop( boost::bind(&TimerQueue::addTimerInLoop, this, timer)); return TimerId(timer, timer->sequence()); */ addTimerInLoop(timer); return TimerId(timer,timer->sequence()); }
Timerfd in constructor_ (createtimerfd()) create a timer file descriptor for listening
1. Introduction to timerfd
timerfd yes Linux A timer interface provided for user programs.
This interface is based on the file descriptor, and the timeout notification is performed through the readable events of the file descriptor, so it can be used in the application scenario of select/poll/epoll.
man 2 timerfd_create
These system calls create and operate on a timer that delivers timer expiration notifications via a file descriptor. They provide an alternative to the use of setitimer(2) or timer_create(2), with the advantage
that the file descriptor may be monitored by select(2), poll(2), and epoll(7).
// Create timer file descriptor #include <iostream> #include <sys/socket.h> #include <sys/epoll.h> #include <sys/timerfd.h> #include <time.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <stdint.h> #include <atomic> #define handle_error(msg) \ do \ { \ perror(msg); \ exit(EXIT_FAILURE); \ } while (0) using namespace std; atomic<int> fd_; static void print_elapsed_time() { static struct timespec start; struct timespec curr; static int first_call = 1; int secs, nsecs; if (first_call) { first_call = 0; if (clock_gettime(CLOCK_MONOTONIC, &start) == -1) handle_error("clock_gettime"); } if (clock_gettime(CLOCK_MONOTONIC, &curr) == -1) handle_error("clock_gettime"); secs = curr.tv_sec - start.tv_sec; nsecs = curr.tv_nsec - start.tv_nsec; if (nsecs < 0) { secs--; nsecs += 1000000000; } printf("%d.%03d: ", secs, (nsecs + 500000) / 1000000); } void printids(const char *s) { uint64_t exp; pid_t pid; pthread_t tid; pid = getpid(); tid = pthread_self(); int fd = fd_.load(); read(fd_.load(),&exp,0); cout<<"exp = "<<(unsigned long long)exp<<endl; printf("%s pid %u tid %u (0x%x)\n", s, (unsigned int)pid, (unsigned int)tid, (unsigned int)tid); } void *thr_fn(void *arg) { printids("new thread: "); return NULL; } int main(int argc, char *argv[]) { itimerspec new_value; int max_exp, fd; struct timespec now; uint64_t exp, tot_exp; ssize_t s; if ((argc != 2) && (argc != 4)) { fprintf(stderr, "%s init-secs [interval-secs max-exp]\n", argv[0]); exit(EXIT_FAILURE); } if (clock_gettime(CLOCK_REALTIME, &now) == -1) handle_error("clock_gettime"); new_value.it_value.tv_sec = now.tv_sec + atoi(argv[1]); // The initial expiration time of the timer is seconds new_value.it_value.tv_nsec = now.tv_nsec; // Nanosecond unit if (argc == 2) { new_value.it_interval.tv_sec = 0; // Periodic interval max_exp = 1; } else { new_value.it_interval.tv_sec = atoi(argv[2]); // Periodic interval of timer max_exp = atoi(argv[3]); } new_value.it_interval.tv_nsec = 0; int t = timerfd_create(CLOCK_REALTIME, 0); fd_.exchange(t); fd = timerfd_create(CLOCK_REALTIME, 0); // Create timer file descriptor if (fd_.load() == -1) handle_error("timerfd_create"); if (timerfd_settime(fd_.load(), TFD_TIMER_ABSTIME, &new_value, NULL) == -1) // Start timer file descriptor handle_error("timerfd_settime"); int err; pthread_t ntid; print_elapsed_time(); // Prints the value of the current clock printf("timer started\n"); for (tot_exp = 0; tot_exp < max_exp;) { cout << "Waiting" << endl; err = pthread_create(&ntid, NULL, thr_fn, NULL); s = read(fd_.load(), &exp, sizeof(uint64_t)); // The blocking state is not released until the file descriptor is ready if (s != sizeof(uint64_t)) handle_error("read"); tot_exp += exp; print_elapsed_time(); printf("read: %llu; total=%llu\n", (unsigned long long)exp, (unsigned long long)tot_exp); } exit(EXIT_SUCCESS); }
$g++ test_timer.cc -lpthread -std=c++11 $./a.out 2 5 2 # Timer test case
2 create timer and set non blocking mode
int createTimerfd() { int timerfd = ::timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); // Create a timer, non blocking mode if (timerfd < 0) { LOG_SYSFATAL << "Failed in timerfd_create"; } return timerfd; }
3 insert timer
The interface exposes addTimer to the outside, and the real work is the insert function. The specific implementation is not exposed to the user
void TimerQueue::addTimerInLoop(Timer *timer) { loop_->assertInLoopThread(); bool earliestChanged = insert(timer); // When inserting a timer, its expiration time may affect the first expired timer and change the sequence if (earliestChanged) // Is it necessary to change the sequence and reset the timeout of the timer { resetTimerfd(timerfd_, timer->expiration()); } }
1. Insertion implementation of internal package
bool TimerQueue::insert(Timer *timer) { loop_->assertInLoopThread(); assert(timers_.size() == activeTimers_.size()); // Assert whether the two lists are the same size bool earliestChanged = false; // Does the expiration time need to change the flag bit Timestamp when = timer->expiration(); // Take out the expiration time of the timer to be added TimerList::iterator it = timers_.begin(); // For the earliest timer, set is arranged from small to large by default if (it == timers_.end() || when < it->first) // If the expiration time of the newly inserted timer is less than the minimum time of the timer in the container or the container is empty { earliestChanged = true; // Set flag to 1 } { std::pair<TimerList::iterator, bool> result = timers_.insert(Entry(when, timer)); // Insert the new timer into two containers respectively assert(result.second); (void)result; // Is it written to cancel wall } { std::pair<ActiveTimerSet::iterator, bool> result = activeTimers_.insert(ActiveTimer(timer, timer->sequence())); assert(result.second); (void)result; } assert(timers_.size() == activeTimers_.size()); return earliestChanged; }
2 the timeout needs to be reset during insertion
void resetTimerfd(int timerfd, Timestamp expiration) // Take out the timeout of the current new insertion { struct itimerspec newValue{}; struct itimerspec oldValue{}; // It is slightly changed here. If the list is initialized to null, there is no need to call memset or bzero newValue.it_value = howMuchTimeFromNow(expiration); // Is to convert the class of muduo/base into a type that can be used for time int ret = ::timerfd_settime(timerfd, 0, &newValue, &oldValue); // Set the new timeout if (ret){ LOG_SYSERR << "timerfd_settime()";} }
3. The time class and timestamp need to be converted during reset
struct timespec howMuchTimeFromNow(Timestamp when) { int64_t microseconds = when.microSecondsSinceEpoch() - Timestamp::now().microSecondsSinceEpoch(); // Get incoming timestamp - current timestamp if (microseconds < 100) { microseconds = 100; // Accurate to 100 } struct timespec ts; ts.tv_sec = static_cast<time_t>( microseconds / Timestamp::kMicroSecondsPerSecond); // Divide by 100W and convert to seconds ts.tv_nsec = static_cast<long>( (microseconds % Timestamp::kMicroSecondsPerSecond) * 1000); // Convert to nanosecond units return ts; }
3 timer callback event
The event callback function has been set in TimerQueue::TimerQueue(EventLoop *loop) through setReadCallback: TimerQueue::handleRead passes in the passed in eventloop and timer file descriptor
void TimerQueue::handleRead() { loop_->assertInLoopThread(); Timestamp now(Timestamp::now()); // Get current time readTimerfd(timerfd_, now); // Process ready file descriptor std::vector<Entry> expired = getExpired(now); // Get the list of all timers at this time, that is, the timer whose timeout is ready callingExpiredTimers_ = true; // Processing timer expired cancelingTimers_.clear(); // Clear the timer that has been cancelled for (std::vector<Entry>::iterator it = expired.begin();it != expired.end(); ++it) { it->second->run(); // Loop calls the callback function processed by the timer } callingExpiredTimers_ = false; // Processing completed reset(expired, now); // It is not a one-time timer and needs to be restarted }
1 processing ready timer file descriptor
void readTimerfd(int timerfd, Timestamp now) { uint64_t howmany; ssize_t n = ::read(timerfd, &howmany, sizeof howmany); LOG_TRACE << "TimerQueue::handleRead() " << howmany << " at " << now.toString(); if (n != sizeof howmany) { LOG_ERROR << "TimerQueue::handleRead() reads " << n << " bytes instead of 8"; } }
2 get all timers at this time, that is, the timers that have timed out
getExpired (Timestamp now) returns the list of timers that have timed out
std::vector<TimerQueue::Entry> TimerQueue::getExpired(Timestamp now) { assert(timers_.size() == activeTimers_.size()); std::vector<Entry> expired; Entry sentry(now, reinterpret_cast<Timer *>(UINTPTR_MAX));// UINTPTR_MAX set maximum address // It is possible to prevent. now is as big as Timer TimerList::iterator end = timers_.lower_bound(sentry); // lower_bound binary search returns the iterator with the first value > = sentry, and returns the first iterator position greater than the current time slice assert(end == timers_.end() || now < end->first); // Assert that the timer of the current time slice is the largest and runs behind the whole iterator // Or the current time is less than the first returned position greater than the current time // If now == *end(), uintptr is fired_ Max role, sentry value std::copy(timers_.begin(), end, back_inserter(expired)); // Push the interval of the first unexpired timer from begin() - >_ Copy the method of back () to the expired list timers_.erase(timers_.begin(), end); // Remove the expired timer for (std::vector<Entry>::iterator it = expired.begin();it != expired.end(); ++it) { ActiveTimer timer(it->second, it->second->sequence()); size_t n = activeTimers_.erase(timer); // Remove expiration timer assert(n == 1); (void)n; } assert(timers_.size() == activeTimers_.size()); // Asserts whether the two containers are consistent return expired; }
queue only focuses on the earliest timer time slice
3 application examples
///Explore whether the timer reset condition is the same as the source code analysis void prints(const char *msg) { printf("==========================\n,%s\n",msg); } int main() { printTid(); sleep(1); { EventLoop loop; g_loop = &loop; print("main"); loop.runAfter(2,boost::bind(prints,"Hit me"));// The registered callback function and timer are 2 seconds and 1 second respectively loop.runAfter(1,boost::bind(prints,"Ha ha ha")); loop.loop(); // Turn on event polling print("main loop exits"); } }
Output results
Because the timer of 1 second will cause the timer of the previous 2 seconds to no longer be the minimum expiration time
msg 1642152525.200913 main
Insert a timer
Timeout when the timer needs to be reset
Insert a timer
Timeout when the timer needs to be reset
20220114 09:28:45.200953Z 19142 TRACE loop EventLoop 0x7FFD2E1BA050 start looping - EventLoop.cc:70
20220114 09:28:46.200961Z 19142 TRACE poll 1 events happended - EPollPoller.cc:65
20220114 09:28:46.201379Z 19142 TRACE printActiveChannels {4: IN } - EventLoop.cc:163
4 restart the non disposable timer
void TimerQueue::reset(const std::vector<Entry> &expired, Timestamp now) { Timestamp nextExpire; for (std::vector<Entry>::const_iterator it = expired.begin(); it != expired.end(); ++it) { ActiveTimer timer(it->second, it->second->sequence()); // If the timer is repeated and not cancelled, restart the timer if (it->second->repeat() && cancelingTimers_.find(timer) == cancelingTimers_.end()) { it->second->restart(now); insert(it->second); } else { // A one-time timer or a timer that has been cancelled cannot be reset, so delete the timer delete it->second; // FIXME: no delete please } } if (!timers_.empty()) { // Gets the timer timeout for the earliest expiration nextExpire = timers_.begin()->second->expiration(); } if (nextExpire.valid()) { // Reset timer timeout (timerfd_settime) resetTimerfd(timerfd_, nextExpire); } }
5 cancel timer
TimerId is a visible class used to cancel the timer
class TimerId : public muduo::copyable { public: TimerId() : timer_(NULL), sequence_(0) { } TimerId(Timer* timer, int64_t seq) : timer_(timer), sequence_(seq) { } friend class TimerQueue; private: Timer* timer_; // Address of timer int64_t sequence_;// Serial number of timer }; } }
void TimerQueue::cancelInLoop(TimerId timerId) { loop_->assertInLoopThread(); assert(timers_.size() == activeTimers_.size()); ActiveTimer timer(timerId.timer_, timerId.sequence_); ActiveTimerSet::iterator it = activeTimers_.find(timer); // Find out whether the timer to be cancelled is in the normal list if (it != activeTimers_.end()) // If you find it { size_t n = timers_.erase(Entry(it->first->expiration(), it->first)); assert(n == 1); (void)n; delete it->first; // FIXME: no delete please activeTimers_.erase(it); // Both lists need to be removed } else if (callingExpiredTimers_) // If it is not in the list, it indicates that it has been removed { cancelingTimers_.insert(timer); // Insert the timer into the cancel list } assert(timers_.size() == activeTimers_.size()); }
pyable
{
public:
TimerId()
: timer_(NULL),
sequence_(0)
{
}
sequence_(seq)
{
}
friend class TimerQueue;
private:
Timer* timer_; // Address of timer
int64_t sequence_;// Serial number of timer
};
}
}
```cpp void TimerQueue::cancelInLoop(TimerId timerId) { loop_->assertInLoopThread(); assert(timers_.size() == activeTimers_.size()); ActiveTimer timer(timerId.timer_, timerId.sequence_); ActiveTimerSet::iterator it = activeTimers_.find(timer); // Find out whether the timer to be cancelled is in the normal list if (it != activeTimers_.end()) // If you find it { size_t n = timers_.erase(Entry(it->first->expiration(), it->first)); assert(n == 1); (void)n; delete it->first; // FIXME: no delete please activeTimers_.erase(it); // Both lists need to be removed } else if (callingExpiredTimers_) // If it is not in the list, it indicates that it has been removed { cancelingTimers_.insert(timer); // Insert the timer into the cancel list } assert(timers_.size() == activeTimers_.size()); }