1 /*
2 * Copyright (C) 2017 Daniel Nicoletti <dantti12@gmail.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18 #include <QtCore/QCoreApplication>
19 #include <QPointer>
20 #include <QSocketNotifier>
21 #include <QVector>
22
23 #include <unistd.h>
24 #include <sys/epoll.h>
25 #include <sys/eventfd.h>
26 #include <sys/timerfd.h>
27 #include <stdlib.h>
28 #include <errno.h>
29 #include "eventdispatcher_epoll.h"
30 #include "eventdispatcher_epoll_p.h"
31
EventDispatcherEPollPrivate(EventDispatcherEPoll * const q)32 EventDispatcherEPollPrivate::EventDispatcherEPollPrivate(EventDispatcherEPoll* const q)
33 : q_ptr(q)
34 {
35 createEpoll();
36 }
37
~EventDispatcherEPollPrivate()38 EventDispatcherEPollPrivate::~EventDispatcherEPollPrivate()
39 {
40 close(m_event_fd);
41 close(m_epoll_fd);
42
43 auto it = m_handles.constBegin();
44 while (it != m_handles.constEnd()) {
45 delete it.value();
46 ++it;
47 }
48 delete m_event_fd_info;
49 }
50
createEpoll()51 void EventDispatcherEPollPrivate::createEpoll()
52 {
53 m_epoll_fd = epoll_create1(EPOLL_CLOEXEC);
54 if (Q_UNLIKELY(-1 == m_epoll_fd)) {
55 qErrnoWarning("epoll_create1() failed");
56 abort();
57 }
58
59 m_event_fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
60 if (Q_UNLIKELY(-1 == m_event_fd)) {
61 qErrnoWarning("eventfd() failed");
62 abort();
63 }
64
65 struct epoll_event e;
66 e.events = EPOLLIN;
67 m_event_fd_info = new EventFdInfo(m_event_fd, this);
68 e.data.ptr = m_event_fd_info;
69 if (Q_UNLIKELY(-1 == epoll_ctl(m_epoll_fd, EPOLL_CTL_ADD, m_event_fd, &e))) {
70 qErrnoWarning("%s: epoll_ctl() failed", Q_FUNC_INFO);
71 }
72 }
73
processEvents(QEventLoop::ProcessEventsFlags flags)74 bool EventDispatcherEPollPrivate::processEvents(QEventLoop::ProcessEventsFlags flags)
75 {
76 Q_Q(EventDispatcherEPoll);
77
78 const bool exclude_notifiers = (flags & QEventLoop::ExcludeSocketNotifiers);
79 const bool exclude_timers = (flags & QEventLoop::X11ExcludeTimers);
80
81 exclude_notifiers && disableSocketNotifiers(true);
82 exclude_timers && disableTimers(true);
83
84 m_interrupt = false;
85 Q_EMIT q->awake();
86
87 bool result = q->hasPendingEvents();
88
89 QCoreApplication::sendPostedEvents();
90
91 bool can_wait =
92 !m_interrupt
93 && (flags & QEventLoop::WaitForMoreEvents)
94 && !result
95 ;
96
97 int n_events = 0;
98
99 if (!m_interrupt) {
100 int timeout = 0;
101
102 if (!exclude_timers && !m_zero_timers.isEmpty()) {
103 QVector<ZeroTimer*> timers;
104 auto it = m_zero_timers.constBegin();
105 while (it != m_zero_timers.constEnd()) {
106 ZeroTimer *data = it.value();
107 data->ref();
108 timers.push_back(data);
109 ++it;
110 }
111
112 for (ZeroTimer *data : timers) {
113 if (data->canProcess() && data->active) {
114 data->active = false;
115
116 QTimerEvent event(data->timerId);
117 QCoreApplication::sendEvent(data->object, &event);
118
119 result = true;
120 if (!data->active) {
121 data->active = true;
122 }
123 }
124
125 data->deref();
126 }
127 }
128
129 if (can_wait && !result) {
130 Q_EMIT q->aboutToBlock();
131 timeout = -1;
132 }
133
134 struct epoll_event events[10024];
135 do {
136 n_events = epoll_wait(m_epoll_fd, events, 10024, timeout);
137 } while (Q_UNLIKELY(-1 == n_events && errno == EINTR));
138
139 for (int i = 0; i < n_events; ++i) {
140 struct epoll_event &e = events[i];
141 auto data = static_cast<EpollAbastractEvent*>(e.data.ptr);
142 data->ref();
143 }
144
145 for (int i = 0; i < n_events; ++i) {
146 struct epoll_event &e = events[i];
147 auto data = static_cast<EpollAbastractEvent*>(e.data.ptr);
148 if (data->canProcess()) {
149 data->process(e.events);
150 }
151
152 data->deref();
153 }
154 }
155
156 exclude_notifiers && disableSocketNotifiers(false);
157 exclude_timers && disableTimers(false);
158
159 return result || n_events > 0;
160 }
161
wake_up_handler()162 void EventDispatcherEPollPrivate::wake_up_handler()
163 {
164 eventfd_t value;
165 int res;
166 do {
167 res = eventfd_read(m_event_fd, &value);
168 } while (Q_UNLIKELY(-1 == res && EINTR == errno));
169
170 if (Q_UNLIKELY(-1 == res)) {
171 qErrnoWarning("%s: eventfd_read() failed", Q_FUNC_INFO);
172 }
173
174 if (Q_UNLIKELY(!m_wakeups.testAndSetRelease(1, 0))) {
175 qCritical("%s: internal error, testAndSetRelease(1, 0) failed!", Q_FUNC_INFO);
176 }
177 }
178
process(quint32 events)179 void SocketNotifierInfo::process(quint32 events)
180 {
181 QEvent e(QEvent::SockAct);
182
183 if (r && (events & EPOLLIN)) {
184 QCoreApplication::sendEvent(r, &e);
185 }
186
187 if (w && (events & EPOLLOUT)) {
188 QCoreApplication::sendEvent(w, &e);
189 }
190
191 if (x && (events & EPOLLPRI)) {
192 QCoreApplication::sendEvent(x, &e);
193 }
194 }
195
process(quint32 events)196 void EventFdInfo::process(quint32 events)
197 {
198 if (Q_LIKELY(events & EPOLLIN)) {
199 epPriv->wake_up_handler();
200 }
201 }
202
process(quint32 events)203 void TimerInfo::process(quint32 events)
204 {
205 Q_UNUSED(events)
206
207 uint64_t value;
208 int res;
209 do {
210 res = read(fd, &value, sizeof(value));
211 } while (-1 == res && EINTR == errno);
212
213 if (Q_UNLIKELY(-1 == res)) {
214 qErrnoWarning("%s: read() failed", Q_FUNC_INFO);
215 }
216
217 QTimerEvent event(timerId);
218 QCoreApplication::sendEvent(object, &event);
219
220 // Check if we are NOT going to be deleted
221 if (canProcess()) {
222 struct timeval now;
223 struct timeval delta;
224 struct itimerspec spec;
225
226 spec.it_interval.tv_sec = 0;
227 spec.it_interval.tv_nsec = 0;
228
229 gettimeofday(&now, 0);
230 EventDispatcherEPollPrivate::calculateNextTimeout(this, now, delta);
231 TIMEVAL_TO_TIMESPEC(&delta, &spec.it_value);
232 if (0 == spec.it_value.tv_sec && 0 == spec.it_value.tv_nsec) {
233 spec.it_value.tv_nsec = 500;
234 }
235
236 if (-1 == timerfd_settime(fd, 0, &spec, 0)) {
237 qErrnoWarning("%s: timerfd_settime() failed", Q_FUNC_INFO);
238 }
239 }
240 }
241
process(quint32 events)242 void ZeroTimer::process(quint32 events)
243 {
244 Q_UNUSED(events)
245 }
246