1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qwineventnotifier_p.h"
41 
42 #ifdef Q_OS_WINRT
43 #include "qeventdispatcher_winrt_p.h"
44 #else
45 #include "qeventdispatcher_win_p.h"
46 #endif
47 #include "qcoreapplication.h"
48 
49 #include <private/qthread_p.h>
50 
51 QT_BEGIN_NAMESPACE
52 
53 /*!
54     \class QWinEventNotifier
55     \inmodule QtCore
56     \since 5.0
57     \brief The QWinEventNotifier class provides support for the Windows Wait functions.
58 
59     The QWinEventNotifier class makes it possible to use the wait
60     functions on windows in a asynchronous manner. With this class,
61     you can register a HANDLE to an event and get notification when
62     that event becomes signalled. The state of the event is not modified
63     in the process so if it is a manual reset event you will need to
64     reset it after the notification.
65 
66     Once you have created a event object using Windows API such as
67     CreateEvent() or OpenEvent(), you can create an event notifier to
68     monitor the event handle. If the event notifier is enabled, it will
69     emit the activated() signal whenever the corresponding event object
70     is signalled.
71 
72     The setEnabled() function allows you to disable as well as enable the
73     event notifier. It is generally advisable to explicitly enable or
74     disable the event notifier. A disabled notifier does nothing when the
75     event object is signalled (the same effect as not creating the
76     event notifier).  Use the isEnabled() function to determine the
77     notifier's current status.
78 
79     Finally, you can use the setHandle() function to register a new event
80     object, and the handle() function to retrieve the event handle.
81 
82     \b{Further information:}
83     Although the class is called QWinEventNotifier, it can be used for
84     certain other objects which are so-called synchronization
85     objects, such as Processes, Threads, Waitable timers.
86 
87     \warning This class is only available on Windows.
88 */
89 
90 /*!
91     \fn void QWinEventNotifier::activated(HANDLE hEvent)
92 
93     This signal is emitted whenever the event notifier is enabled and
94     the corresponding HANDLE is signalled.
95 
96     The state of the event is not modified in the process, so if it is a
97     manual reset event, you will need to reset it after the notification.
98 
99     The object is passed in the \a hEvent parameter.
100 
101     \sa handle()
102 */
103 
104 /*!
105     Constructs an event notifier with the given \a parent.
106 */
107 
QWinEventNotifier(QObject * parent)108 QWinEventNotifier::QWinEventNotifier(QObject *parent)
109   : QObject(*new QWinEventNotifierPrivate, parent)
110 {}
111 
112 /*!
113     Constructs an event notifier with the given \a parent. It enables
114     the notifier, and watches for the event \a hEvent.
115 
116     The notifier is enabled by default, i.e. it emits the activated() signal
117     whenever the corresponding event is signalled. However, it is generally
118     advisable to explicitly enable or disable the event notifier.
119 
120     \sa setEnabled(), isEnabled()
121 */
122 
QWinEventNotifier(HANDLE hEvent,QObject * parent)123 QWinEventNotifier::QWinEventNotifier(HANDLE hEvent, QObject *parent)
124  : QObject(*new QWinEventNotifierPrivate(hEvent, false), parent)
125 {
126     Q_D(QWinEventNotifier);
127     QAbstractEventDispatcher *eventDispatcher = d->threadData.loadRelaxed()->eventDispatcher.loadRelaxed();
128     if (Q_UNLIKELY(!eventDispatcher)) {
129         qWarning("QWinEventNotifier: Can only be used with threads started with QThread");
130         return;
131     }
132     eventDispatcher->registerEventNotifier(this);
133     d->enabled = true;
134 }
135 
136 /*!
137     Destroys this notifier.
138 */
139 
~QWinEventNotifier()140 QWinEventNotifier::~QWinEventNotifier()
141 {
142     setEnabled(false);
143 }
144 
145 /*!
146     Register the HANDLE \a hEvent. The old HANDLE will be automatically
147     unregistered.
148 
149     \b Note: The notifier will be disabled as a side effect and needs
150     to be re-enabled.
151 
152     \sa handle(), setEnabled()
153 */
154 
setHandle(HANDLE hEvent)155 void QWinEventNotifier::setHandle(HANDLE hEvent)
156 {
157     Q_D(QWinEventNotifier);
158     setEnabled(false);
159     d->handleToEvent = hEvent;
160 }
161 
162 /*!
163     Returns the HANDLE that has been registered in the notifier.
164 
165     \sa setHandle()
166 */
167 
handle() const168 HANDLE  QWinEventNotifier::handle() const
169 {
170     Q_D(const QWinEventNotifier);
171     return d->handleToEvent;
172 }
173 
174 /*!
175     Returns \c true if the notifier is enabled; otherwise returns \c false.
176 
177     \sa setEnabled()
178 */
179 
isEnabled() const180 bool QWinEventNotifier::isEnabled() const
181 {
182     Q_D(const QWinEventNotifier);
183     return d->enabled;
184 }
185 
186 /*!
187     If \a enable is true, the notifier is enabled; otherwise the notifier
188     is disabled.
189 
190     \sa isEnabled(), activated()
191 */
192 
setEnabled(bool enable)193 void QWinEventNotifier::setEnabled(bool enable)
194 {
195     Q_D(QWinEventNotifier);
196     if (d->enabled == enable)                        // no change
197         return;
198     d->enabled = enable;
199 
200     QAbstractEventDispatcher *eventDispatcher = d->threadData.loadRelaxed()->eventDispatcher.loadRelaxed();
201     if (!eventDispatcher) // perhaps application is shutting down
202         return;
203     if (Q_UNLIKELY(thread() != QThread::currentThread())) {
204         qWarning("QWinEventNotifier: Event notifiers cannot be enabled or disabled from another thread");
205         return;
206     }
207 
208     if (enable) {
209         d->signaledCount = 0;
210         eventDispatcher->registerEventNotifier(this);
211     } else {
212         eventDispatcher->unregisterEventNotifier(this);
213     }
214 }
215 
216 /*!
217     \reimp
218 */
219 
event(QEvent * e)220 bool QWinEventNotifier::event(QEvent * e)
221 {
222     Q_D(QWinEventNotifier);
223     if (e->type() == QEvent::ThreadChange) {
224         if (d->enabled) {
225             QMetaObject::invokeMethod(this, "setEnabled", Qt::QueuedConnection,
226                                       Q_ARG(bool, true));
227             setEnabled(false);
228         }
229     }
230     QObject::event(e);                        // will activate filters
231     if (e->type() == QEvent::WinEventAct) {
232         emit activated(d->handleToEvent, QPrivateSignal());
233         return true;
234     }
235     return false;
236 }
237 
238 #if defined(Q_OS_WINRT)
239 
registerWaitObject()240 bool QWinEventNotifierPrivate::registerWaitObject()
241 {
242     Q_UNIMPLEMENTED();
243     return false;
244 }
245 
unregisterWaitObject()246 void QWinEventNotifierPrivate::unregisterWaitObject()
247 {
248     Q_UNIMPLEMENTED();
249 }
250 
251 #else // defined(Q_OS_WINRT)
252 
wfsoCallback(void * context,BOOLEAN)253 static void CALLBACK wfsoCallback(void *context, BOOLEAN /*ignore*/)
254 {
255     QWinEventNotifierPrivate *nd = reinterpret_cast<QWinEventNotifierPrivate *>(context);
256     QAbstractEventDispatcher *eventDispatcher = nd->threadData.loadRelaxed()->eventDispatcher.loadRelaxed();
257 
258     // Happens when Q(Core)Application is destroyed before QWinEventNotifier.
259     // https://bugreports.qt.io/browse/QTBUG-70214
260     if (!eventDispatcher) { // perhaps application is shutting down
261         qWarning("QWinEventNotifier: no event dispatcher, application shutting down? Cannot deliver event.");
262         return;
263     }
264 
265     QEventDispatcherWin32Private *edp = QEventDispatcherWin32Private::get(
266                 static_cast<QEventDispatcherWin32 *>(eventDispatcher));
267     ++nd->signaledCount;
268     SetEvent(edp->winEventNotifierActivatedEvent);
269 }
270 
registerWaitObject()271 bool QWinEventNotifierPrivate::registerWaitObject()
272 {
273     if (RegisterWaitForSingleObject(&waitHandle, handleToEvent, wfsoCallback, this,
274                                     INFINITE, WT_EXECUTEONLYONCE) == 0) {
275         qErrnoWarning("QWinEventNotifier: RegisterWaitForSingleObject failed.");
276         return false;
277     }
278     return true;
279 }
280 
unregisterWaitObject()281 void QWinEventNotifierPrivate::unregisterWaitObject()
282 {
283     // Unregister the wait handle and wait for pending callbacks to finish.
284     if (UnregisterWaitEx(waitHandle, INVALID_HANDLE_VALUE))
285         waitHandle = NULL;
286     else
287         qErrnoWarning("QWinEventNotifier: UnregisterWaitEx failed.");
288 }
289 
290 #endif // !defined(Q_OS_WINRT)
291 
292 QT_END_NAMESPACE
293