Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qcfsocketnotifier.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5#include <QtCore/qcoreapplication.h>
6#include <QtCore/qsocketnotifier.h>
7#include <QtCore/qthread.h>
8
10
11/**************************************************************************
12 Socket Notifiers
13 *************************************************************************/
14void qt_mac_socket_callback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef,
15 const void *data, void *info)
16{
17
18 QCFSocketNotifier *cfSocketNotifier = static_cast<QCFSocketNotifier *>(info);
19 int nativeSocket = CFSocketGetNative(s);
20 MacSocketInfo *socketInfo = cfSocketNotifier->macSockets.value(nativeSocket);
21 QEvent notifierEvent(QEvent::SockAct);
22
23 // There is a race condition that happen where we disable the notifier and
24 // the kernel still has a notification to pass on. We then get this
25 // notification after we've successfully disabled the CFSocket, but our Qt
26 // notifier is now gone. The upshot is we have to check the notifier
27 // every time.
28 if (callbackType == kCFSocketConnectCallBack) {
29 // The data pointer will be non-null on connection error
30 if (data) {
31 if (socketInfo->readNotifier)
32 QCoreApplication::sendEvent(socketInfo->readNotifier, &notifierEvent);
33 if (socketInfo->writeNotifier)
34 QCoreApplication::sendEvent(socketInfo->writeNotifier, &notifierEvent);
35 }
36 } else if (callbackType == kCFSocketReadCallBack) {
37 if (socketInfo->readNotifier && socketInfo->readEnabled) {
38 socketInfo->readEnabled = false;
39 QCoreApplication::sendEvent(socketInfo->readNotifier, &notifierEvent);
40 }
41 } else if (callbackType == kCFSocketWriteCallBack) {
42 if (socketInfo->writeNotifier && socketInfo->writeEnabled) {
43 socketInfo->writeEnabled = false;
44 QCoreApplication::sendEvent(socketInfo->writeNotifier, &notifierEvent);
45 }
46 }
47
48 if (cfSocketNotifier->maybeCancelWaitForMoreEvents)
49 cfSocketNotifier->maybeCancelWaitForMoreEvents(cfSocketNotifier->eventDispatcher);
50}
51
52/*
53 Adds a loop source for the given socket to the current run loop.
54*/
55CFRunLoopSourceRef qt_mac_add_socket_to_runloop(const CFSocketRef socket)
56{
57 CFRunLoopSourceRef loopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0);
58 if (!loopSource)
59 return 0;
60
61 CFRunLoopAddSource(CFRunLoopGetCurrent(), loopSource, kCFRunLoopCommonModes);
62 return loopSource;
63}
64
65/*
66 Removes the loop source for the given socket from the current run loop.
67*/
68void qt_mac_remove_socket_from_runloop(const CFSocketRef socket, CFRunLoopSourceRef runloop)
69{
70 Q_ASSERT(runloop);
71 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), runloop, kCFRunLoopCommonModes);
72 CFSocketDisableCallBacks(socket, kCFSocketReadCallBack);
73 CFSocketDisableCallBacks(socket, kCFSocketWriteCallBack);
74}
75
77 : eventDispatcher(0)
78 , maybeCancelWaitForMoreEvents(0)
79 , enableNotifiersObserver(0)
80{
81
82}
83
88
90{
91 eventDispatcher = hostEventDispacher;
92}
93
95{
96 maybeCancelWaitForMoreEvents = callBack;
97}
98
100{
102 int nativeSocket = notifier->socket();
103 int type = notifier->type();
104#ifndef QT_NO_DEBUG
105 if (nativeSocket < 0 || nativeSocket > FD_SETSIZE) {
106 qWarning("QSocketNotifier: Internal error");
107 return;
108 } else if (notifier->thread() != eventDispatcher->thread()
109 || eventDispatcher->thread() != QThread::currentThread()) {
110 qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread");
111 return;
112 }
113#endif
114
116 qWarning("QSocketNotifier::Exception is not supported on iOS");
117 return;
118 }
119
120 // Check if we have a CFSocket for the native socket, create one if not.
121 MacSocketInfo *socketInfo = macSockets.value(nativeSocket);
122 if (!socketInfo) {
123 socketInfo = new MacSocketInfo();
124
125 // Create CFSocket, specify that we want both read and write callbacks (the callbacks
126 // are enabled/disabled later on).
127 const int callbackTypes = kCFSocketConnectCallBack | kCFSocketReadCallBack | kCFSocketWriteCallBack;
128 CFSocketContext context = {0, this, 0, 0, 0};
129 socketInfo->socket = CFSocketCreateWithNative(kCFAllocatorDefault, nativeSocket, callbackTypes, qt_mac_socket_callback, &context);
130 if (CFSocketIsValid(socketInfo->socket) == false) {
131 qWarning("QEventDispatcherMac::registerSocketNotifier: Failed to create CFSocket");
132 return;
133 }
134
135 CFOptionFlags flags = CFSocketGetSocketFlags(socketInfo->socket);
136 // QSocketNotifier doesn't close the socket upon destruction/invalidation
137 flags &= ~kCFSocketCloseOnInvalidate;
138 // Explicitly disable automatic re-enable, as we do that manually on each runloop pass
139 flags &= ~(kCFSocketAutomaticallyReenableWriteCallBack | kCFSocketAutomaticallyReenableReadCallBack);
140 CFSocketSetSocketFlags(socketInfo->socket, flags);
141
142 macSockets.insert(nativeSocket, socketInfo);
143 }
144
146 Q_ASSERT(socketInfo->readNotifier == 0);
147 socketInfo->readNotifier = notifier;
148 socketInfo->readEnabled = false;
149 } else if (type == QSocketNotifier::Write) {
150 Q_ASSERT(socketInfo->writeNotifier == 0);
151 socketInfo->writeNotifier = notifier;
152 socketInfo->writeEnabled = false;
153 }
154
155 if (!enableNotifiersObserver) {
156 // Create a run loop observer which enables the socket notifiers on each
157 // pass of the run loop, before any sources are processed.
158 CFRunLoopObserverContext context = {};
159 context.info = this;
160 enableNotifiersObserver = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopBeforeSources,
161 true, 0, enableSocketNotifiers, &context);
162 Q_ASSERT(enableNotifiersObserver);
163 CFRunLoopAddObserver(CFRunLoopGetCurrent(), enableNotifiersObserver, kCFRunLoopCommonModes);
164 }
165}
166
168{
170 int nativeSocket = notifier->socket();
171 int type = notifier->type();
172#ifndef QT_NO_DEBUG
173 if (nativeSocket < 0 || nativeSocket > FD_SETSIZE) {
174 qWarning("QSocketNotifier: Internal error");
175 return;
176 } else if (notifier->thread() != eventDispatcher->thread() || eventDispatcher->thread() != QThread::currentThread()) {
177 qWarning("QSocketNotifier: socket notifiers cannot be disabled from another thread");
178 return;
179 }
180#endif
181
183 qWarning("QSocketNotifier::Exception is not supported on iOS");
184 return;
185 }
186 MacSocketInfo *socketInfo = macSockets.value(nativeSocket);
187 if (!socketInfo) {
188 qWarning("QEventDispatcherMac::unregisterSocketNotifier: Tried to unregister a not registered notifier");
189 return;
190 }
191
192 // Decrement read/write counters and disable callbacks if necessary.
194 Q_ASSERT(notifier == socketInfo->readNotifier);
195 socketInfo->readNotifier = 0;
196 socketInfo->readEnabled = false;
197 CFSocketDisableCallBacks(socketInfo->socket, kCFSocketReadCallBack);
198 } else if (type == QSocketNotifier::Write) {
199 Q_ASSERT(notifier == socketInfo->writeNotifier);
200 socketInfo->writeNotifier = 0;
201 socketInfo->writeEnabled = false;
202 CFSocketDisableCallBacks(socketInfo->socket, kCFSocketWriteCallBack);
203 }
204
205 // Remove CFSocket from runloop if this was the last QSocketNotifier.
206 if (socketInfo->readNotifier == 0 && socketInfo->writeNotifier == 0) {
207 unregisterSocketInfo(socketInfo);
208 delete socketInfo;
209 macSockets.remove(nativeSocket);
210 }
211}
212
214{
215 // Remove CFSockets from the runloop.
216 for (MacSocketInfo *socketInfo : std::as_const(macSockets)) {
217 unregisterSocketInfo(socketInfo);
218 delete socketInfo;
219 }
220
221 macSockets.clear();
222
223 destroyRunLoopObserver();
224}
225
226void QCFSocketNotifier::destroyRunLoopObserver()
227{
228 if (!enableNotifiersObserver)
229 return;
230
231 CFRunLoopObserverInvalidate(enableNotifiersObserver);
232 CFRelease(enableNotifiersObserver);
233 enableNotifiersObserver = 0;
234}
235
236void QCFSocketNotifier::unregisterSocketInfo(MacSocketInfo *socketInfo)
237{
238 if (socketInfo->runloop) {
239 if (CFSocketIsValid(socketInfo->socket))
240 qt_mac_remove_socket_from_runloop(socketInfo->socket, socketInfo->runloop);
241 CFRunLoopSourceInvalidate(socketInfo->runloop);
242 CFRelease(socketInfo->runloop);
243 }
244 CFSocketInvalidate(socketInfo->socket);
245 CFRelease(socketInfo->socket);
246}
247
248void QCFSocketNotifier::enableSocketNotifiers(CFRunLoopObserverRef ref, CFRunLoopActivity activity, void *info)
249{
250 Q_UNUSED(ref);
251 Q_UNUSED(activity);
252
253 const QCFSocketNotifier *that = static_cast<QCFSocketNotifier *>(info);
254
255 for (MacSocketInfo *socketInfo : that->macSockets) {
256 if (!CFSocketIsValid(socketInfo->socket))
257 continue;
258
259 if (!socketInfo->runloop) {
260 // Add CFSocket to runloop.
261 if (!(socketInfo->runloop = qt_mac_add_socket_to_runloop(socketInfo->socket))) {
262 qWarning("QEventDispatcherMac::registerSocketNotifier: Failed to add CFSocket to runloop");
263 CFSocketInvalidate(socketInfo->socket);
264 continue;
265 }
266
267 // Apple docs say: "If a callback is automatically re-enabled,
268 // it is called every time the condition becomes true ... If a
269 // callback is not automatically re-enabled, then it gets called
270 // exactly once, and is not called again until you manually
271 // re-enable that callback by calling CFSocketEnableCallBacks()".
272 // So, we don't need to enable callbacks on registering.
273 socketInfo->readEnabled = (socketInfo->readNotifier != nullptr);
274 if (!socketInfo->readEnabled)
275 CFSocketDisableCallBacks(socketInfo->socket, kCFSocketReadCallBack);
276 socketInfo->writeEnabled = (socketInfo->writeNotifier != nullptr);
277 if (!socketInfo->writeEnabled)
278 CFSocketDisableCallBacks(socketInfo->socket, kCFSocketWriteCallBack);
279 continue;
280 }
281
282 if (socketInfo->readNotifier && !socketInfo->readEnabled) {
283 socketInfo->readEnabled = true;
284 CFSocketEnableCallBacks(socketInfo->socket, kCFSocketReadCallBack);
285 }
286 if (socketInfo->writeNotifier && !socketInfo->writeEnabled) {
287 socketInfo->writeEnabled = true;
288 CFSocketEnableCallBacks(socketInfo->socket, kCFSocketWriteCallBack);
289 }
290 }
291}
292
294
DarwinBluetooth::LECBManagerNotifier * notifier
void unregisterSocketNotifier(QSocketNotifier *notifier)
void setMaybeCancelWaitForMoreEventsCallback(MaybeCancelWaitForMoreEventsFn callBack)
void registerSocketNotifier(QSocketNotifier *notifier)
void setHostEventDispatcher(QAbstractEventDispatcher *hostEventDispacher)
friend void qt_mac_socket_callback(CFSocketRef, CFSocketCallBackType, CFDataRef, const void *, void *)
static bool sendEvent(QObject *receiver, QEvent *event)
Sends event event directly to receiver receiver, using the notify() function.
\inmodule QtCore
Definition qcoreevent.h:45
@ SockAct
Definition qcoreevent.h:98
bool remove(const Key &key)
Removes the item that has the key from the hash.
Definition qhash.h:958
T value(const Key &key) const noexcept
Definition qhash.h:1054
void clear() noexcept(std::is_nothrow_destructible< Node >::value)
Removes all items from the hash and frees up all memory used by it.
Definition qhash.h:951
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1303
QThread * thread() const
Returns the thread in which the object lives.
Definition qobject.cpp:1598
\inmodule QtCore
static QThread * currentThread()
Definition qthread.cpp:1039
Combined button and popup list for selecting options.
static void * context
CFRunLoopSourceRef qt_mac_add_socket_to_runloop(const CFSocketRef socket)
void qt_mac_remove_socket_from_runloop(const CFSocketRef socket, CFRunLoopSourceRef runloop)
QT_BEGIN_NAMESPACE void qt_mac_socket_callback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef, const void *data, void *info)
void(* MaybeCancelWaitForMoreEventsFn)(QAbstractEventDispatcher *hostEventDispacher)
#define qWarning
Definition qlogging.h:166
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum type
GLbitfield flags
GLint ref
GLdouble s
[6]
Definition qopenglext.h:235
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_UNUSED(x)
QTcpSocket * socket
[1]
QHostInfo info
[0]