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/QSocketNotifier>
19 #include <QtCore/QThread>
20 
21 #include <sys/eventfd.h>
22 
23 #include "eventdispatcher_epoll.h"
24 #include "eventdispatcher_epoll_p.h"
25 
EventDispatcherEPoll(QObject * parent)26 EventDispatcherEPoll::EventDispatcherEPoll(QObject* parent)
27     : QAbstractEventDispatcher(parent), d_ptr(new EventDispatcherEPollPrivate(this))
28 {
29 }
30 
~EventDispatcherEPoll()31 EventDispatcherEPoll::~EventDispatcherEPoll()
32 {
33     delete d_ptr;
34 }
35 
processEvents(QEventLoop::ProcessEventsFlags flags)36 bool EventDispatcherEPoll::processEvents(QEventLoop::ProcessEventsFlags flags)
37 {
38     Q_D(EventDispatcherEPoll);
39     return d->processEvents(flags);
40 }
41 
registerSocketNotifier(QSocketNotifier * notifier)42 void EventDispatcherEPoll::registerSocketNotifier(QSocketNotifier* notifier)
43 {
44 #ifndef QT_NO_DEBUG
45     if (notifier->socket() < 0) {
46         qWarning("QSocketNotifier: Internal error: sockfd < 0");
47         return;
48     }
49 
50     if (notifier->thread() != thread() || thread() != QThread::currentThread()) {
51         qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread");
52         return;
53     }
54 #endif
55 
56     Q_D(EventDispatcherEPoll);
57     d->registerSocketNotifier(notifier);
58 }
59 
unregisterSocketNotifier(QSocketNotifier * notifier)60 void EventDispatcherEPoll::unregisterSocketNotifier(QSocketNotifier* notifier)
61 {
62 #ifndef QT_NO_DEBUG
63     if (notifier->socket() < 0) {
64         qWarning("QSocketNotifier: Internal error: sockfd < 0");
65         return;
66     }
67 
68     if (notifier->thread() != thread() || thread() != QThread::currentThread()) {
69         qWarning("QSocketNotifier: socket notifiers cannot be disabled from another thread");
70         return;
71     }
72 #endif
73 
74     Q_D(EventDispatcherEPoll);
75     d->unregisterSocketNotifier(notifier);
76 }
77 
unregisterTimer(int timerId)78 bool EventDispatcherEPoll::unregisterTimer(int timerId)
79 {
80 #ifndef QT_NO_DEBUG
81     if (timerId < 1) {
82         qWarning("%s: invalid arguments", Q_FUNC_INFO);
83         return false;
84     }
85 
86     if (thread() != QThread::currentThread()) {
87         qWarning("%s: timers cannot be stopped from another thread", Q_FUNC_INFO);
88         return false;
89     }
90 #endif
91 
92     Q_D(EventDispatcherEPoll);
93     return d->unregisterTimer(timerId);
94 }
95 
unregisterTimers(QObject * object)96 bool EventDispatcherEPoll::unregisterTimers(QObject *object)
97 {
98 #ifndef QT_NO_DEBUG
99     if (!object) {
100         qWarning("%s: invalid arguments", Q_FUNC_INFO);
101         return false;
102     }
103 
104     if (object->thread() != thread() && thread() != QThread::currentThread()) {
105         qWarning("%s: timers cannot be stopped from another thread", Q_FUNC_INFO);
106         return false;
107     }
108 #endif
109 
110     Q_D(EventDispatcherEPoll);
111     return d->unregisterTimers(object);
112 }
113 
registeredTimers(QObject * object) const114 QList<QAbstractEventDispatcher::TimerInfo> EventDispatcherEPoll::registeredTimers(QObject *object) const
115 {
116     if (!object) {
117         qWarning("%s: invalid argument", Q_FUNC_INFO);
118         return QList<QAbstractEventDispatcher::TimerInfo>();
119     }
120 
121     Q_D(const EventDispatcherEPoll);
122     return d->registeredTimers(object);
123 }
124 
remainingTime(int timerId)125 int EventDispatcherEPoll::remainingTime(int timerId)
126 {
127     Q_D(const EventDispatcherEPoll);
128     return d->remainingTime(timerId);
129 }
130 
wakeUp()131 void EventDispatcherEPoll::wakeUp()
132 {
133     Q_D(EventDispatcherEPoll);
134 
135     if (d->m_wakeups.testAndSetAcquire(0, 1)) {
136         const eventfd_t value = 1;
137         int res;
138 
139         do {
140             res = eventfd_write(d->m_event_fd, value);
141         } while (Q_UNLIKELY(-1 == res && EINTR == errno));
142 
143         if (Q_UNLIKELY(-1 == res)) {
144             qErrnoWarning("%s: eventfd_write() failed", Q_FUNC_INFO);
145         }
146     }
147 }
148 
interrupt()149 void EventDispatcherEPoll::interrupt()
150 {
151     Q_D(EventDispatcherEPoll);
152     d->m_interrupt = true;
153     wakeUp();
154 }
155 
156 extern uint qGlobalPostedEventsCount();
157 
hasPendingEvents()158 bool EventDispatcherEPoll::hasPendingEvents()
159 {
160     return qGlobalPostedEventsCount() > 0;
161 }
162 
163 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
registerTimer(int timerId,int interval,Qt::TimerType timerType,QObject * object)164 void EventDispatcherEPoll::registerTimer(
165         int timerId,
166         int interval,
167         Qt::TimerType timerType,
168         QObject *object
169         )
170 {
171 #ifndef QT_NO_DEBUG
172     if (timerId < 1 || interval < 0 || !object) {
173         qWarning("%s: invalid arguments", Q_FUNC_INFO);
174         return;
175     }
176 
177     if (object->thread() != thread() && thread() != QThread::currentThread()) {
178         qWarning("%s: timers cannot be started from another thread", Q_FUNC_INFO);
179         return;
180     }
181 #endif
182 
183     Q_D(EventDispatcherEPoll);
184     if (interval) {
185         d->registerTimer(timerId, interval, timerType, object);
186     } else {
187         d->registerZeroTimer(timerId, object);
188     }
189 }
190 
flush()191 void EventDispatcherEPoll::flush()
192 {
193     Q_D(EventDispatcherEPoll);
194     d->createEpoll();
195 }
196 #else
registerTimer(int timerId,qint64 interval,Qt::TimerType timerType,QObject * object)197 void EventDispatcherEPoll::registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *object)
198 {
199 #ifndef QT_NO_DEBUG
200     if (timerId < 1 || interval < 0 || !object) {
201         qWarning("%s: invalid arguments", Q_FUNC_INFO);
202         return;
203     }
204 
205     if (object->thread() != thread() && thread() != QThread::currentThread()) {
206         qWarning("%s: timers cannot be started from another thread", Q_FUNC_INFO);
207         return;
208     }
209 #endif
210 
211     Q_D(EventDispatcherEPoll);
212     if (interval) {
213         d->registerTimer(timerId, interval, timerType, object);
214     } else {
215         d->registerZeroTimer(timerId, object);
216     }
217 }
218 #endif
219 
220 #include "moc_eventdispatcher_epoll.cpp"
221