1// excerpts from http://code.google.com/p/muduo/
2//
3// Use of this source code is governed by a BSD-style license
4// that can be found in the License file.
5//
6// Author: Shuo Chen (chenshuo at chenshuo dot com)
7
8#include "EPoller.h"
9
10#include "Channel.h"
11#include "logging/Logging.h"
12
13
14#include <boost/static_assert.hpp>
15
16#include <assert.h>
17#include <errno.h>
18#include <poll.h>
19#include <sys/epoll.h>
20
21using namespace muduo;
22
23// On Linux, the constants of poll(2) and epoll(4)
24// are expected to be the same.
25BOOST_STATIC_ASSERT(EPOLLIN == POLLIN);
26BOOST_STATIC_ASSERT(EPOLLPRI == POLLPRI);
27BOOST_STATIC_ASSERT(EPOLLOUT == POLLOUT);
28BOOST_STATIC_ASSERT(EPOLLRDHUP == POLLRDHUP);
29BOOST_STATIC_ASSERT(EPOLLERR == POLLERR);
30BOOST_STATIC_ASSERT(EPOLLHUP == POLLHUP);
31
32namespace
33{
34const int kNew = -1;
35const int kAdded = 1;
36const int kDeleted = 2;
37}
38
39EPoller::EPoller(EventLoop* loop)
40  : ownerLoop_(loop),
41    epollfd_(::epoll_create1(EPOLL_CLOEXEC)),
42    events_(kInitEventListSize)
43{
44  if (epollfd_ < 0)
45  {
46    LOG_SYSFATAL << "EPoller::EPoller";
47  }
48}
49
50EPoller::~EPoller()
51{
52  ::close(epollfd_);
53}
54
55Timestamp EPoller::poll(int timeoutMs, ChannelList* activeChannels)
56{
57  int numEvents = ::epoll_wait(epollfd_,
58                               &*events_.begin(),
59                               static_cast<int>(events_.size()),
60                               timeoutMs);
61  Timestamp now(Timestamp::now());
62  if (numEvents > 0)
63  {
64    LOG_TRACE << numEvents << " events happended";
65    fillActiveChannels(numEvents, activeChannels);
66    if (implicit_cast<size_t>(numEvents) == events_.size())
67    {
68      events_.resize(events_.size()*2);
69    }
70  }
71  else if (numEvents == 0)
72  {
73    LOG_TRACE << " nothing happended";
74  }
75  else
76  {
77    LOG_SYSERR << "EPoller::poll()";
78  }
79  return now;
80}
81
82void EPoller::fillActiveChannels(int numEvents,
83                                 ChannelList* activeChannels) const
84{
85  assert(implicit_cast<size_t>(numEvents) <= events_.size());
86  for (int i = 0; i < numEvents; ++i)
87  {
88    Channel* channel = static_cast<Channel*>(events_[i].data.ptr);
89#ifndef NDEBUG
90    int fd = channel->fd();
91    ChannelMap::const_iterator it = channels_.find(fd);
92    assert(it != channels_.end());
93    assert(it->second == channel);
94#endif
95    channel->set_revents(events_[i].events);
96    activeChannels->push_back(channel);
97  }
98}
99
100void EPoller::updateChannel(Channel* channel)
101{
102  assertInLoopThread();
103  LOG_TRACE << "fd = " << channel->fd() << " events = " << channel->events();
104  const int index = channel->index();
105  if (index == kNew || index == kDeleted)
106  {
107    // a new one, add with EPOLL_CTL_ADD
108    int fd = channel->fd();
109    if (index == kNew)
110    {
111      assert(channels_.find(fd) == channels_.end());
112      channels_[fd] = channel;
113    }
114    else // index == kDeleted
115    {
116      assert(channels_.find(fd) != channels_.end());
117      assert(channels_[fd] == channel);
118    }
119    channel->set_index(kAdded);
120    update(EPOLL_CTL_ADD, channel);
121  }
122  else
123  {
124    // update existing one with EPOLL_CTL_MOD/DEL
125    int fd = channel->fd();
126    (void)fd;
127    assert(channels_.find(fd) != channels_.end());
128    assert(channels_[fd] == channel);
129    assert(index == kAdded);
130    if (channel->isNoneEvent())
131    {
132      update(EPOLL_CTL_DEL, channel);
133      channel->set_index(kDeleted);
134    }
135    else
136    {
137      update(EPOLL_CTL_MOD, channel);
138    }
139  }
140}
141
142void EPoller::removeChannel(Channel* channel)
143{
144  assertInLoopThread();
145  int fd = channel->fd();
146  LOG_TRACE << "fd = " << fd;
147  assert(channels_.find(fd) != channels_.end());
148  assert(channels_[fd] == channel);
149  assert(channel->isNoneEvent());
150  int index = channel->index();
151  assert(index == kAdded || index == kDeleted);
152  size_t n = channels_.erase(fd);
153  (void)n;
154  assert(n == 1);
155
156  if (index == kAdded)
157  {
158    update(EPOLL_CTL_DEL, channel);
159  }
160  channel->set_index(kNew);
161}
162
163void EPoller::update(int operation, Channel* channel)
164{
165  struct epoll_event event;
166  bzero(&event, sizeof event);
167  event.events = channel->events();
168  event.data.ptr = channel;
169  int fd = channel->fd();
170  if (::epoll_ctl(epollfd_, operation, fd, &event) < 0)
171  {
172    if (operation == EPOLL_CTL_DEL)
173    {
174      LOG_SYSERR << "epoll_ctl op=" << operation << " fd=" << fd;
175    }
176    else
177    {
178      LOG_SYSFATAL << "epoll_ctl op=" << operation << " fd=" << fd;
179    }
180  }
181}
182
183