1 #include <system_error>
4 #include <unordered_map>
8 #include <sys/signalfd.h>
17 template <class Base> class epoll_loop;
21 template <class Base> friend class epoll_loop;
27 template <class Base> friend class epoll_loop;
29 struct signalfd_siginfo info;
33 int get_signo() { return info.ssi_signo; }
34 int get_sicode() { return info.ssi_code; }
35 pid_t get_sipid() { return info.ssi_pid; }
36 uid_t get_siuid() { return info.ssi_uid; }
37 void * get_siaddr() { return reinterpret_cast<void *>(info.ssi_addr); }
38 int get_sistatus() { return info.ssi_status; }
39 int get_sival_int() { return info.ssi_int; }
40 void * get_sival_ptr() { return reinterpret_cast<void *>(info.ssi_ptr); }
43 int get_sierrno() { return info.ssi_errno; }
45 // XSR (streams) OB (obselete)
46 int get_siband() { return info.ssi_band; }
49 int32_t get_sifd() { return info.ssi_fd; }
50 uint32_t get_sittimerid() { return info.ssi_tid; }
51 uint32_t get_sioverrun() { return info.ssi_overrun; }
52 uint32_t get_sitrapno() { return info.ssi_trapno; }
53 uint32_t get_siutime() { return info.ssi_utime; }
54 uint32_t get_sistime() { return info.ssi_stime; }
55 // Field exposed by Linux kernel but not Glibc:
56 // uint16_t get_siaddr_lsb() { return info.ssi_addr_lsb; }
58 void set_signo(int signo) { info.ssi_signo = signo; }
63 // File descriptor optional storage. If the mechanism can return the file descriptor, this
64 // class will be empty, otherwise it can hold a file descriptor.
68 // Epoll doesn't return the file descriptor (it can, but it can't return both file
69 // descriptor and user data).
73 fd_s(int fd_p) noexcept : fd(fd_p) { }
76 // File descriptor reference (passed to event callback). If the mechanism can return the
77 // file descriptor, this class holds the file descriptor. Otherwise, the file descriptor
78 // must be stored in an fd_s instance.
87 constexpr static bool has_bidi_fd_watch = true;
88 constexpr static bool has_separate_rw_fd_watches = false;
89 constexpr static bool interrupt_after_fd_add = false;
90 constexpr static bool interrupt_after_signal_add = false;
91 constexpr static bool supports_non_oneshot_fd = true;
95 template <class Base> class epoll_loop : public Base
98 int sigfd; // signalfd fd; -1 if not initialised
101 std::unordered_map<int, void *> sigdataMap;
104 // lock - a lock that can be used to protect internal structure.
105 // receive*() methods will be called with lock held.
106 // receive_signal(sigdata_t &, user *) noexcept
107 // receive_fd_event(fd_r, user *, int flags) noexcept
109 using sigdata_t = epoll_traits::sigdata_t;
110 using fd_r = typename epoll_traits::fd_r;
112 void process_events(epoll_event *events, int r)
114 std::lock_guard<decltype(Base::lock)> guard(Base::lock);
116 for (int i = 0; i < r; i++) {
117 void * ptr = events[i].data.ptr;
123 int r = read(sigfd, &siginfo.info, sizeof(siginfo.info));
125 auto iter = sigdataMap.find(siginfo.get_signo());
126 if (iter != sigdataMap.end()) {
127 void *userdata = (*iter).second;
128 if (Base::receive_signal(*this, siginfo, userdata)) {
129 sigdelset(&sigmask, siginfo.get_signo());
133 signalfd(sigfd, &sigmask, SFD_NONBLOCK | SFD_CLOEXEC);
137 (events[i].events & EPOLLIN) && (flags |= IN_EVENTS);
138 (events[i].events & EPOLLHUP) && (flags |= IN_EVENTS);
139 (events[i].events & EPOLLOUT) && (flags |= OUT_EVENTS);
140 (events[i].events & EPOLLERR) && (flags |= IN_EVENTS | OUT_EVENTS | ERR_EVENTS);
141 auto r = Base::receive_fd_event(*this, fd_r(), ptr, flags);
142 if (std::get<0>(r) != 0) {
143 enable_fd_watch_nolock(fd_r().get_fd(std::get<1>(r)), ptr, std::get<0>(r));
152 * epoll_loop constructor.
154 * Throws std::system_error or std::bad_alloc if the event loop cannot be initialised.
156 epoll_loop() : sigfd(-1)
158 epfd = epoll_create1(EPOLL_CLOEXEC);
160 throw std::system_error(errno, std::system_category());
162 sigemptyset(&sigmask);
174 // fd: file descriptor to watch
175 // userdata: data to associate with descriptor
176 // flags: IN_EVENTS | OUT_EVENTS | ONE_SHOT
177 // soft_fail: true if unsupported file descriptors should fail by returning false instead
178 // of throwing an exception
179 // returns: true on success; false if file descriptor type isn't supported and soft_fail == true
180 // throws: std::system_error or std::bad_alloc on failure
181 bool add_fd_watch(int fd, void *userdata, int flags, bool enabled = true, bool soft_fail = false)
183 struct epoll_event epevent;
184 // epevent.data.fd = fd;
185 epevent.data.ptr = userdata;
188 if (flags & ONE_SHOT) {
189 epevent.events = EPOLLONESHOT;
191 if ((flags & IN_EVENTS) && enabled) {
192 epevent.events |= EPOLLIN;
194 if ((flags & OUT_EVENTS) && enabled) {
195 epevent.events |= EPOLLOUT;
198 if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &epevent) == -1) {
199 if (soft_fail && errno == EPERM) {
202 throw new std::system_error(errno, std::system_category());
207 bool add_bidi_fd_watch(int fd, void *userdata, int flags, bool emulate)
209 // No implementation.
210 throw std::system_error(std::make_error_code(std::errc::not_supported));
213 // flags specifies which watch to remove; ignored if the loop doesn't support
214 // separate read/write watches.
215 void remove_fd_watch(int fd, int flags) noexcept
217 epoll_ctl(epfd, EPOLL_CTL_DEL, fd, nullptr);
220 void remove_fd_watch_nolock(int fd, int flags) noexcept
222 remove_fd_watch(fd, flags);
225 void remove_bidi_fd_watch(int fd) noexcept
227 // Shouldn't be called for epoll.
228 remove_fd_watch(fd, IN_EVENTS | OUT_EVENTS);
231 // Note this will *replace* the old flags with the new, that is,
232 // it can enable *or disable* read/write events.
233 void enable_fd_watch(int fd, void *userdata, int flags) noexcept
235 struct epoll_event epevent;
236 // epevent.data.fd = fd;
237 epevent.data.ptr = userdata;
240 if (flags & ONE_SHOT) {
241 epevent.events = EPOLLONESHOT;
243 if (flags & IN_EVENTS) {
244 epevent.events |= EPOLLIN;
246 if (flags & OUT_EVENTS) {
247 epevent.events |= EPOLLOUT;
250 if (epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &epevent) == -1) {
251 // Shouldn't be able to fail
252 // throw new std::system_error(errno, std::system_category());
256 void enable_fd_watch_nolock(int fd, void *userdata, int flags)
258 enable_fd_watch(fd, userdata, flags);
261 void disable_fd_watch(int fd, int flags) noexcept
263 struct epoll_event epevent;
264 // epevent.data.fd = fd;
265 epevent.data.ptr = nullptr;
268 // Epoll documentation says that hangup will still be reported, need to check
269 // whether this is really the case. Suspect it is really only the case if
271 if (epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &epevent) == -1) {
272 // Let's assume that this can't fail.
273 // throw new std::system_error(errno, std::system_category());
277 void disable_fd_watch_nolock(int fd, int flags) noexcept
279 disable_fd_watch(fd, flags);
282 // Note signal should be masked before call.
283 void add_signal_watch(int signo, void *userdata)
285 std::lock_guard<decltype(Base::lock)> guard(Base::lock);
286 add_signal_watch_nolock(signo, userdata);
289 // Note signal should be masked before call.
290 void add_signal_watch_nolock(int signo, void *userdata)
292 sigdataMap[signo] = userdata;
294 // Modify the signal fd to watch the new signal
295 bool was_no_sigfd = (sigfd == -1);
296 sigaddset(&sigmask, signo);
297 sigfd = signalfd(sigfd, &sigmask, SFD_NONBLOCK | SFD_CLOEXEC);
299 throw new std::system_error(errno, std::system_category());
303 // Add the signalfd to the epoll set.
304 struct epoll_event epevent;
305 epevent.data.ptr = &sigfd;
306 epevent.events = EPOLLIN;
307 // No need for EPOLLONESHOT - we can pull the signals out
309 if (epoll_ctl(epfd, EPOLL_CTL_ADD, sigfd, &epevent) == -1) {
312 throw new std::system_error(errno, std::system_category());
317 // Note, called with lock held:
318 void rearm_signal_watch_nolock(int signo, void *userdata) noexcept
320 sigaddset(&sigmask, signo);
321 signalfd(sigfd, &sigmask, SFD_NONBLOCK | SFD_CLOEXEC);
324 void remove_signal_watch_nolock(int signo) noexcept
326 sigdelset(&sigmask, signo);
327 signalfd(sigfd, &sigmask, 0);
330 void remove_signal_watch(int signo) noexcept
332 std::lock_guard<decltype(Base::lock)> guard(Base::lock);
333 remove_signal_watch_nolock(signo);
336 // If events are pending, process an unspecified number of them.
337 // If no events are pending, wait until one event is received and
338 // process this event (and possibly any other events received
340 // If processing an event removes a watch, there is a possibility
341 // that the watched event will still be reported (if it has
342 // occurred) before pull_events() returns.
344 // do_wait - if false, returns immediately if no events are
346 void pull_events(bool do_wait)
348 epoll_event events[16];
349 int r = epoll_wait(epfd, events, 16, do_wait ? -1 : 0);
350 if (r == -1 || r == 0) {
351 // signal or no events
356 process_events(events, r);
357 r = epoll_wait(epfd, events, 16, 0);