DUDS
Distributed Update of Data from Something
Poller.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the Screentouch project. It is subject to the GPLv3
3  * license terms in the LICENSE file found in the top-level directory of this
4  * distribution and at
5  * https://github.com/jjackowski/screentouch/blob/master/LICENSE.
6  * No part of the Screentouch project, including this file, may be copied,
7  * modified, propagated, or distributed except according to the terms
8  * contained in the LICENSE file.
9  *
10  * Copyright (C) 2020 Jeff Jackowski
11  */
12 #include <boost/exception/errinfo_errno.hpp>
13 #include <duds/os/linux/Poller.hpp>
14 #include <duds/general/Errors.hpp>
15 #include <assert.h>
16 
17 namespace duds { namespace os { namespace linux {
18 
19 Poller::Poller(int reserveSize) {
20  epfd = epoll_create(1);
21  if (epfd < 0) {
23  boost::errinfo_errno(errno)
24  );
25  }
26  if (reserveSize) {
27  responders.reserve(reserveSize);
28  flist.reserve(reserveSize);
29  }
30 }
31 
34 flist(std::move(p.flist)),
35 epfd(p.epfd) {
36  p.epfd = -1;
37 }
38 
40  std::lock_guard<std::mutex> lock(block);
41  close(epfd);
42 }
43 
45  std::lock_guard<std::mutex> lock(block);
46  std::lock_guard<std::mutex> plock(p.block);
47  responders = std::move(p.responders);
49  epfd = p.epfd;
50  p.epfd = -1;
51  return *this;
52 }
53 
54 void Poller::add(const PollResponderSptr &prs, int fd, int events) {
55  if (!prs) {
57  }
58  epoll_event event;
59  event.events = events;
60  std::lock_guard<std::mutex> lock(block);
61  // set event.data.u32 to the index inside responders that will hold the
62  // responder record for this file descriptor
63  if (flist.empty()) {
64  event.data.u32 = responders.size();
65  } else {
66  event.data.u32 = flist.back();
67  }
68  if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event)) {
70  boost::errinfo_errno(errno) << PollerFileDescriptor(fd)
71  );
72  }
73  // put the responder record in place
74  if (flist.empty()) {
75  responders.emplace_back(prs, fd);
76  } else {
77  responders[event.data.u32].responder = prs;
78  responders[event.data.u32].fd = fd;
79  flist.pop_back();
80  }
81 }
82 
83 void Poller::remove(int fd) {
84  int err = 0;
85  std::lock_guard<std::mutex> lock(block);
86  // find the file descriptor in the ResponderRecord objects
87  ResponderVec::iterator iter = std::find_if(
88  responders.begin(),
89  responders.end(),
90  [fd](const ResponderRecord &rr) {
91  return rr.fd == fd;
92  }
93  );
94  if (iter != responders.end()) {
95  errno = 0;
96  // Attempt the removal and check for any error other than not finding
97  // the given file descriptor. If the descriptor is already closed, it
98  // may not be possible to remove the descriptor.
99  if (epoll_ctl(epfd, EPOLL_CTL_DEL, fd, nullptr) && ((err = errno) != ENOENT)) {
101  boost::errinfo_errno(err) << PollerFileDescriptor(fd)
102  );
103  }
104  // remove the descriptor and handler even if it cannot be removed from
105  // what epoll will check
106  iter->responder.reset();
107  iter->fd = -1;
108  // make it available for re-use
109  flist.push_back(iter - responders.begin());
110  assert(responders[flist.back()].fd == -1);
111  // report ENOENT below
112  if (!err) {
113  return;
114  }
115  }
116  // report ENOENT or other lack of fd
118  boost::errinfo_errno(err) << PollerFileDescriptor(fd)
119  );
120 }
121 
122 // Holds response data temporarily.
125  int fd;
126  ResponseRecord(PollResponderSptr &&p, int f) : prs(std::move(p)), fd(f) { }
127 };
128 
129 int Poller::wait(std::chrono::milliseconds timeout) {
130  epoll_event events[maxEvents];
131  int count = epoll_wait(epfd, events, maxEvents, timeout.count());
132  if (!count) {
133  // all done
134  return 0;
135  } else if (count < 0) {
136  if (errno == EINTR) {
137  return -1;
138  }
140  boost::errinfo_errno(errno)
141  );
142  }
143  // might be better if this was an array of 32 unconstructed objects to avoid
144  // dynamic memory allocation
145  std::vector<ResponseRecord> resprec;
146  resprec.reserve(count);
147  { // accessing responders needs a lock
148  std::lock_guard<std::mutex> lock(block);
149  for (int loop = 0; loop < count; ++loop) {
150  ResponderVec::iterator iter =
151  responders.begin() + events[loop].data.u32;
152  PollResponderSptr prs = iter->responder.lock();
153  // if the responder still exists . . .
154  if (prs) {
155  // . . . prepare to invoke it
156  resprec.emplace_back(std::move(prs), iter->fd);
157  }
158  // If the responder does not exist, do nothing. Anything would make
159  // this function slower, and would need to handle the possibility of
160  // occuring multiple times. Removing the file desriptor is a
161  // possibility, but may fail if the descriptor is already closed.
162  }
163  }
164  // invoke all queued responders
165  std::for_each(
166  resprec.begin(),
167  resprec.end(),
168  [this](const ResponseRecord &rr) {
169  // do not allow an exception to prevent other events from being
170  // processed
171  try {
172  rr.prs->respond(this, rr.fd);
173  } catch (...) {
174  // maybe record the exceptions and later throw an exception
175  // containing all the exceptions?
176  }
177  }
178  );
179  return count; //resprec.size();
180 }
181 
182 } } }
void remove(int fd)
Removes the entry for the given file descriptor.
Definition: Poller.cpp:83
ResponderVec responders
The responders and their file descriptors.
Definition: Poller.hpp:143
ResponseRecord(PollResponderSptr &&p, int f)
Definition: Poller.cpp:126
Poller(int reserveSize=0)
Constructs a new Poller and obtains a file descriptor for use with epoll.
Definition: Poller.cpp:19
Poller & operator=(Poller &&p) noexcept
Move assignment.
Definition: Poller.cpp:44
STL namespace.
move_impl move(unsigned int c, unsigned int r)
Display stream manipulator that moves the display cursor to the given location.
A simple C++ interface to using Linux&#39;s epoll functions.
Definition: Poller.hpp:117
The call to epoll_create() failed.
Definition: Poller.hpp:47
int epfd
The file descriptor provided by epoll_create().
Definition: Poller.hpp:155
boost::error_info< struct Info_PollerFileDescriptor, int > PollerFileDescriptor
Poller error attribute that includes the value of the file descriptor.
Definition: Poller.hpp:67
Attempted to use a non-existent PollResponder object.
Definition: Poller.hpp:59
std::shared_ptr< PollResponder > PollResponderSptr
Definition: Poller.hpp:96
int wait()
Waits indefinitely for events, only returning after an event is received.
Definition: Poller.hpp:270
std::vector< int > flist
Free spot list.
Definition: Poller.hpp:147
An operation (remove) resulted in an error from an epoll function indicating that the file descriptor...
Definition: Poller.hpp:54
Holds a PollResponder object and its associated file descriptor.
Definition: Poller.hpp:121
void add(const PollResponderSptr &prs, int fd, int events=EPOLLIN)
Adds a PollResponder to check for events on a file descriptor.
Definition: Poller.cpp:54
~Poller()
Closes the internal file desciptor used with epoll.
Definition: Poller.cpp:39
General errors.
std::mutex block
Used to allow for thread-safe operation.
Definition: Poller.hpp:151
static constexpr int maxEvents
The maximum number of events that will be read by a single call to wait(std::chrono::milliseconds).
Definition: Poller.hpp:161
#define DUDS_THROW_EXCEPTION(x)
Works like BOOST_THROW_EXCEPTION, but includes a stack trace if DUDS_ERRORS_VERBOSE is defined...
Definition: Errors.hpp:48
The base class for all Poller errors; used for general errors.
Definition: Poller.hpp:41