Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Toggle main menu visibility
Loading...
Searching...
No Matches
qwasmsocket.cpp
Go to the documentation of this file.
1
// Copyright (C) 2024 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
// Qt-Security score:significant reason:default
4
5
#
include
"qwasmsocket_p.h"
6
#
include
"qwasmglobal_p.h"
7
8
#
include
<
QtCore
/
qcoreapplication
.
h
>
9
#
include
<
QtCore
/
qsocketnotifier
.
h
>
10
11
#
include
"emscripten.h"
12
#
include
<
sys
/
ioctl
.
h
>
13
14
#
if
QT_CONFIG
(
thread
)
15
#
define
LOCK_GUARD
(
M
)
std
::
lock_guard
<
std
::
mutex
>
lock
(
M
)
16
#
else
17
#
define
LOCK_GUARD
(
M
)
18
#
endif
19
20
#
if
QT_CONFIG
(
thread
)
21
Q_CONSTINIT std::mutex QWasmSocket::g_socketDataMutex;
22
#
endif
23
24
// ### dynamic initialization:
25
std::multimap<
int
, QSocketNotifier *>
QWasmSocket
::g_socketNotifiers;
26
std::map<
int
, QWasmSocket::SocketReadyState>
QWasmSocket
::g_socketState;
27
28
void
QWasmSocket
::registerSocketNotifier(QSocketNotifier *notifier)
29
{
30
LOCK_GUARD
(g_socketDataMutex);
31
32
bool
wasEmpty = g_socketNotifiers.empty();
33
g_socketNotifiers.insert({notifier->socket(), notifier});
34
if
(wasEmpty)
35
qwasmglobal
::
runOnMainThread
([] {
setEmscriptenSocketCallbacks
(
)
; });
36
37
int
count;
38
ioctl(notifier->socket(), FIONREAD, &count);
39
40
// message may have arrived already
41
if
(count > 0 && notifier->type() == QSocketNotifier::Read) {
42
QCoreApplication::postEvent(notifier,
new
QEvent(QEvent::SockAct));
43
}
44
}
45
46
void
QWasmSocket
::unregisterSocketNotifier(QSocketNotifier *notifier)
47
{
48
LOCK_GUARD
(g_socketDataMutex);
49
50
auto
notifiers = g_socketNotifiers.equal_range(notifier->socket());
51
for
(
auto
it = notifiers.first; it != notifiers.second; ++it) {
52
if
(it->second == notifier) {
53
g_socketNotifiers.erase(it);
54
break
;
55
}
56
}
57
58
if
(g_socketNotifiers.empty())
59
qwasmglobal
::
runOnMainThread
([] {
clearEmscriptenSocketCallbacks
(
)
; });
60
}
61
62
void
QWasmSocket
::
clearSocketNotifiers
()
63
{
64
LOCK_GUARD
(g_socketDataMutex);
65
if
(!g_socketNotifiers.empty()) {
66
qWarning(
"QWasmSocket: Sockets cleared with active socket notifiers"
);
67
clearEmscriptenSocketCallbacks
(
)
;
68
g_socketNotifiers.clear();
69
}
70
g_socketState.clear();
71
}
72
73
void
QWasmSocket
::
setEmscriptenSocketCallbacks
()
74
{
75
qCDebug(lcEventDispatcher) <<
"setEmscriptenSocketCallbacks"
;
76
77
emscripten_set_socket_error_callback(
nullptr
,
QWasmSocket
::
socketError
);
78
emscripten_set_socket_open_callback(
nullptr
,
QWasmSocket
::
socketOpen
);
79
emscripten_set_socket_listen_callback(
nullptr
,
QWasmSocket
::
socketListen
);
80
emscripten_set_socket_connection_callback(
nullptr
,
QWasmSocket
::
socketConnection
);
81
emscripten_set_socket_message_callback(
nullptr
,
QWasmSocket
::
socketMessage
);
82
emscripten_set_socket_close_callback(
nullptr
,
QWasmSocket
::
socketClose
);
83
}
84
85
void
QWasmSocket
::
clearEmscriptenSocketCallbacks
()
86
{
87
qCDebug(lcEventDispatcher) <<
"clearEmscriptenSocketCallbacks"
;
88
89
emscripten_set_socket_error_callback(
nullptr
,
nullptr
);
90
emscripten_set_socket_open_callback(
nullptr
,
nullptr
);
91
emscripten_set_socket_listen_callback(
nullptr
,
nullptr
);
92
emscripten_set_socket_connection_callback(
nullptr
,
nullptr
);
93
emscripten_set_socket_message_callback(
nullptr
,
nullptr
);
94
emscripten_set_socket_close_callback(
nullptr
,
nullptr
);
95
}
96
97
void
QWasmSocket
::
socketError
(
int
socket,
int
err,
const
char
* msg,
void
*context)
98
{
99
Q_UNUSED(err);
100
Q_UNUSED(msg);
101
Q_UNUSED(context);
102
103
// Emscripten makes socket callbacks while the main thread is busy-waiting for a mutex,
104
// which can cause deadlocks if the callback code also tries to lock the same mutex.
105
// This is most easily reproducible by adding print statements, where each print requires
106
// taking a mutex lock. Work around this by running the callback asynchronously, i.e. by using
107
// a native zero-timer, to make sure the main thread stack is completely unwond before calling
108
// the Qt handler.
109
// It is currently unclear if this problem is caused by code in Qt or in Emscripten, or
110
// if this completely fixes the problem.
111
qwasmglobal
::
runAsync
([socket](){
112
auto
notifiersRange = g_socketNotifiers.equal_range(socket);
113
std::vector<std::pair<
int
, QSocketNotifier *>> notifiers(notifiersRange.first, notifiersRange.second);
114
for
(
auto
[_, notifier]: notifiers) {
115
QCoreApplication::postEvent(notifier,
new
QEvent(QEvent::SockAct));
116
}
117
setSocketState
(
socket
,
true
,
true
)
;
118
});
119
}
120
121
void
QWasmSocket
::
socketOpen
(
int
socket,
void
*context)
122
{
123
Q_UNUSED(context);
124
125
qwasmglobal
::
runAsync
([socket](){
126
auto
notifiersRange = g_socketNotifiers.equal_range(socket);
127
std::vector<std::pair<
int
, QSocketNotifier *>> notifiers(notifiersRange.first, notifiersRange.second);
128
for
(
auto
[_, notifier]: notifiers) {
129
if
(notifier->type() == QSocketNotifier::Write) {
130
QCoreApplication::postEvent(notifier,
new
QEvent(QEvent::SockAct));
131
}
132
}
133
setSocketState
(
socket
,
false
,
true
)
;
134
});
135
}
136
137
void
QWasmSocket
::
socketListen
(
int
socket,
void
*context)
138
{
139
Q_UNUSED(socket);
140
Q_UNUSED(context);
141
}
142
143
void
QWasmSocket
::
socketConnection
(
int
socket,
void
*context)
144
{
145
Q_UNUSED(socket);
146
Q_UNUSED(context);
147
}
148
149
void
QWasmSocket
::
socketMessage
(
int
socket,
void
*context)
150
{
151
Q_UNUSED(context);
152
153
qwasmglobal
::
runAsync
([socket](){
154
auto
notifiersRange = g_socketNotifiers.equal_range(socket);
155
std::vector<std::pair<
int
, QSocketNotifier *>> notifiers(notifiersRange.first, notifiersRange.second);
156
for
(
auto
[_, notifier]: notifiers) {
157
if
(notifier->type() == QSocketNotifier::Read) {
158
QCoreApplication::postEvent(notifier,
new
QEvent(QEvent::SockAct));
159
}
160
}
161
setSocketState
(
socket
,
true
,
false
)
;
162
});
163
}
164
165
void
QWasmSocket
::
socketClose
(
int
socket,
void
*context)
166
{
167
Q_UNUSED(context);
168
169
// Emscripten makes emscripten_set_socket_close_callback() calls to socket 0,
170
// which is not a valid socket. see https://github.com/emscripten-core/emscripten/issues/6596
171
if
(socket == 0)
172
return
;
173
174
qwasmglobal
::
runAsync
([socket](){
175
auto
notifiersRange = g_socketNotifiers.equal_range(socket);
176
std::vector<std::pair<
int
, QSocketNotifier *>> notifiers(notifiersRange.first, notifiersRange.second);
177
for
(
auto
[_, notifier]: notifiers)
178
QCoreApplication::postEvent(notifier,
new
QEvent(QEvent::SockClose));
179
180
setSocketState
(
socket
,
true
,
true
)
;
181
clearSocketState
(
socket
)
;
182
});
183
}
184
185
void
QWasmSocket
::
setSocketState
(
int
socket,
bool
setReadyRead,
bool
setReadyWrite)
186
{
187
LOCK_GUARD
(g_socketDataMutex);
188
SocketReadyState &state = g_socketState[socket];
189
190
// Additively update socket ready state, e.g. if it
191
// was already ready read then it stays ready read.
192
state.readyRead |= setReadyRead;
193
state.readyWrite |= setReadyWrite;
194
195
// Wake any waiters for the given readiness. The waiter consumes
196
// the ready state, returning the socket to not-ready.
197
if
(QEventDispatcherWasm *waiter = state.waiter)
198
if
((state.readyRead && state.waitForReadyRead) || (state.readyWrite && state.waitForReadyWrite))
199
waiter->wakeUp();
200
}
201
202
void
QWasmSocket
::
clearSocketState
(
int
socket)
203
{
204
LOCK_GUARD
(g_socketDataMutex);
205
g_socketState.erase(socket);
206
}
207
208
void
QWasmSocket
::waitForSocketState(QEventDispatcherWasm *eventDispatcher,
int
timeout,
int
socket,
bool
checkRead,
209
bool
checkWrite,
bool
*selectForRead,
bool
*selectForWrite,
bool
*socketDisconnect)
210
{
211
// Loop until the socket becomes readyRead or readyWrite. Wait for
212
// socket activity if it currently is neither.
213
while
(
true
) {
214
*selectForRead =
false
;
215
*selectForWrite =
false
;
216
217
{
218
LOCK_GUARD
(g_socketDataMutex);
219
220
// Access or create socket state: we want to register that a thread is waitng
221
// even if we have not received any socket callbacks yet.
222
SocketReadyState &state = g_socketState[socket];
223
if
(state.waiter) {
224
qWarning() <<
"QEventDispatcherWasm::waitForSocketState: a thread is already waiting"
;
225
break
;
226
}
227
228
bool
shouldWait =
true
;
229
if
(checkRead && state.readyRead) {
230
shouldWait =
false
;
231
state.readyRead =
false
;
232
*selectForRead =
true
;
233
}
234
if
(checkWrite && state.readyWrite) {
235
shouldWait =
false
;
236
state.readyWrite =
false
;
237
*selectForRead =
true
;
238
}
239
if
(!shouldWait)
240
break
;
241
242
state.waiter = eventDispatcher;
243
state.waitForReadyRead = checkRead;
244
state.waitForReadyWrite = checkWrite;
245
}
246
247
bool
didTimeOut = !eventDispatcher->wait(timeout);
248
{
249
LOCK_GUARD
(g_socketDataMutex);
250
251
// Missing socket state after a wakeup means that the socket has been closed.
252
auto
it = g_socketState.find(socket);
253
if
(it == g_socketState.end()) {
254
*socketDisconnect =
true
;
255
break
;
256
}
257
it->second.waiter =
nullptr
;
258
it->second.waitForReadyRead =
false
;
259
it->second.waitForReadyWrite =
false
;
260
}
261
262
if
(didTimeOut)
263
break
;
264
}
265
}
QWasmSocket
Definition
qwasmsocket_p.h:22
QWasmSocket::setSocketState
static void setSocketState(int socket, bool setReadyRead, bool setReadyWrite)
Definition
qwasmsocket.cpp:185
QWasmSocket::setEmscriptenSocketCallbacks
static void setEmscriptenSocketCallbacks()
Definition
qwasmsocket.cpp:73
QWasmSocket::socketConnection
static void socketConnection(int fd, void *context)
Definition
qwasmsocket.cpp:143
QWasmSocket::socketClose
static void socketClose(int fd, void *context)
Definition
qwasmsocket.cpp:165
QWasmSocket::clearSocketState
static void clearSocketState(int socket)
Definition
qwasmsocket.cpp:202
QWasmSocket::clearEmscriptenSocketCallbacks
static void clearEmscriptenSocketCallbacks()
Definition
qwasmsocket.cpp:85
QWasmSocket::socketListen
static void socketListen(int fd, void *context)
Definition
qwasmsocket.cpp:137
QWasmSocket::socketOpen
static void socketOpen(int fd, void *context)
Definition
qwasmsocket.cpp:121
QWasmSocket::clearSocketNotifiers
static void clearSocketNotifiers()
Definition
qwasmsocket.cpp:62
QWasmSocket::socketError
static void socketError(int fd, int err, const char *msg, void *context)
Definition
qwasmsocket.cpp:97
QWasmSocket::socketMessage
static void socketMessage(int fd, void *context)
Definition
qwasmsocket.cpp:149
qwasmglobal
Definition
qwasmglobal.cpp:8
qwasmglobal::runAsync
void runAsync(std::function< void(void)> fn)
Definition
qwasmglobal.cpp:35
qwasmglobal::runOnMainThread
void runOnMainThread(std::function< void(void)> fn)
Definition
qwasmglobal.cpp:25
LOCK_GUARD
#define LOCK_GUARD(M)
Definition
qeventdispatcher_wasm.cpp:28
qtbase
src
corelib
platform
wasm
qwasmsocket.cpp
Generated on
for Qt by
1.18.0