cutelyst 4.3.0
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
unixfork.cpp
1/*
2 * SPDX-FileCopyrightText: (C) 2014-2020 Daniel Nicoletti <dantti12@gmail.com>
3 * SPDX-License-Identifier: BSD-3-Clause
4 */
5#include "unixfork.h"
6
7#include "server.h"
8
9#if defined(HAS_EventLoopEPoll)
10# include "EventLoopEPoll/eventdispatcher_epoll.h"
11#endif
12
13#include <sys/stat.h>
14#include <sys/types.h>
15#include <unistd.h>
16
17#if defined(__FreeBSD__) || defined(__GNU_kFreeBSD__)
18# include <sys/cpuset.h>
19# include <sys/param.h>
20#endif
21
22#include <errno.h>
23#include <grp.h>
24#include <iostream>
25#include <pwd.h>
26#include <signal.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <sys/socket.h>
30#include <sys/wait.h>
31#include <unistd.h>
32
33#include <QAbstractEventDispatcher>
34#include <QCoreApplication>
35#include <QFile>
36#include <QLoggingCategory>
37#include <QMutex>
38#include <QSocketNotifier>
39#include <QThread>
40#include <QTimer>
41
42Q_LOGGING_CATEGORY(C_SERVER_UNIX, "cutelyst.server.unix", QtWarningMsg)
43
44#pragma GCC diagnostic push
45#pragma GCC diagnostic ignored "-Wunused-result"
46
47static int signalsFd[2];
48
49UnixFork::UnixFork(int process, int threads, bool setupSignals, QObject *parent)
50 : AbstractFork(parent)
51 , m_threads(threads)
52 , m_processes(process)
53{
54 if (setupSignals) {
55 setupUnixSignalHandlers();
56 }
57}
58
59UnixFork::~UnixFork()
60{
61 if (m_child) {
62 _exit(0);
63 }
64}
65
67{
68 Q_UNUSED(exit)
69 return true;
70}
71
72int UnixFork::exec(bool lazy, bool master)
73{
74 if (master) {
75 std::cout << "spawned WSGI master process (pid: " << QCoreApplication::applicationPid()
76 << ")" << std::endl;
77 }
78
79 int ret;
80 if (lazy) {
81 if (master) {
82 ret = internalExec();
83 } else {
84 std::cerr << "*** Master mode must be set on lazy mode" << std::endl;
85 ret = -1;
86 }
87 } else {
88 if (m_processes > 0) {
89 ret = internalExec();
90 } else {
91 Q_EMIT forked(0);
92 ret = qApp->exec();
93 }
94 }
95
96 return ret;
97}
98
100{
101 auto it = m_childs.begin();
102 while (it != m_childs.end()) {
103 it.value().restart = 1; // Mark as requiring restart
104 terminateChild(it.key());
105 ++it;
106 }
107
108 setupCheckChildTimer();
109}
110
111int UnixFork::internalExec()
112{
113 int ret;
114 bool respawn = false;
115 do {
116 if (!createProcess(respawn)) {
117 return 1;
118 }
119 respawn = true;
120
121 installTouchReload();
122
123 ret = qApp->exec();
124
125 removeTouchReload();
126 } while (!m_terminating);
127
128 return ret;
129}
130
131bool UnixFork::createProcess(bool respawn)
132{
133 if (respawn) {
134 auto it = m_recreateWorker.begin();
135 while (it != m_recreateWorker.end()) {
136 Worker worker = *it;
137 worker.restart = 0;
138 if (!createChild(worker, respawn)) {
139 std::cout << "CHEAPING worker: " << worker.id << std::endl;
140 --m_processes;
141 }
142 it = m_recreateWorker.erase(it);
143 }
144 } else {
145 for (int i = 0; i < m_processes; ++i) {
146 Worker worker;
147 worker.id = i + 1;
148 worker.null = false;
149 createChild(worker, respawn);
150 }
151 }
152
153 return !m_childs.empty();
154}
155
156void UnixFork::decreaseWorkerRespawn()
157{
158 int missingRespawn = 0;
159 auto it = m_childs.begin();
160 while (it != m_childs.end()) {
161 if (it.value().respawn > 0) {
162 --it.value().respawn;
163 missingRespawn += it.value().respawn;
164 }
165 ++it;
166 }
167
168 if (missingRespawn) {
169 QTimer::singleShot(std::chrono::seconds{1}, this, &UnixFork::decreaseWorkerRespawn);
170 }
171}
172
174{
175 const auto childs = m_childs.keys();
176 for (qint64 pid : childs) {
177 killChild(pid);
178 }
179}
180
181void UnixFork::killChild(qint64 pid)
182{
183 // qCDebug(C_SERVER_UNIX) << "SIGKILL " << pid;
184 ::kill(pid_t(pid), SIGKILL);
185}
186
188{
189 const auto childs = m_childs.keys();
190 for (qint64 pid : childs) {
191 terminateChild(pid);
192 }
193}
194
195void UnixFork::terminateChild(qint64 pid)
196{
197 // qCDebug(C_SERVER_UNIX) << "SIGQUIT " << pid;
198 ::kill(pid_t(pid), SIGQUIT);
199}
200
201void UnixFork::stopWSGI(const QString &pidfile)
202{
203 QFile file(pidfile);
204 if (!file.open(QFile::ReadOnly | QFile::Text)) {
205 std::cerr << "Failed open pid file " << qPrintable(pidfile) << std::endl;
206 exit(1);
207 }
208
209 QByteArray piddata = file.readLine().simplified();
210 qint64 pid = piddata.toLongLong();
211 if (pid < 2) {
212 std::cerr << "Failed read pid file " << qPrintable(pidfile) << std::endl;
213 exit(1);
214 }
215
216 ::kill(pid_t(pid), SIGINT);
217 exit(0);
218}
219
220bool UnixFork::setUmask(const QByteArray &valueStr)
221{
222 if (valueStr.size() < 3) {
223 std::cerr << "umask too small" << std::endl;
224 return false;
225 }
226
227 const char *value = valueStr.constData();
228 mode_t mode = 0;
229 if (valueStr.size() == 3) {
230 mode = (mode << 3) + (value[0] - '0');
231 mode = (mode << 3) + (value[1] - '0');
232 mode = (mode << 3) + (value[2] - '0');
233 } else {
234 mode = (mode << 3) + (value[1] - '0');
235 mode = (mode << 3) + (value[2] - '0');
236 mode = (mode << 3) + (value[3] - '0');
237 }
238 std::cout << "umask() " << value << std::endl;
239
240 umask(mode);
241
242 return true;
243}
244
245void UnixFork::signalHandler(int signal)
246{
247 // qDebug() << Q_FUNC_INFO << signal << QCoreApplication::applicationPid();
248 char sig = signal;
249 write(signalsFd[0], &sig, sizeof(sig));
250}
251
252void UnixFork::setupCheckChildTimer()
253{
254 if (!m_checkChildRestart) {
255 m_checkChildRestart = new QTimer(this);
256 m_checkChildRestart->start(std::chrono::milliseconds{500});
257 connect(m_checkChildRestart, &QTimer::timeout, this, &UnixFork::handleSigChld);
258 }
259}
260
261void UnixFork::postFork(int workerId)
262{
263 // Child must not have parent timers
264 delete m_checkChildRestart;
265
266 Q_EMIT forked(workerId - 1);
267}
268
269bool UnixFork::setGidUid(const QString &gid, const QString &uid, bool noInitgroups)
270{
271 bool ok;
272
273 if (!gid.isEmpty()) {
274 uint gidInt = gid.toUInt(&ok);
275 if (!ok) {
276 struct group *ugroup = getgrnam(qUtf8Printable(gid));
277 if (ugroup) {
278 gidInt = ugroup->gr_gid;
279 } else {
280 std::cerr << "setgid group %s not found." << qUtf8Printable(gid) << std::endl;
281 return false;
282 }
283 }
284
285 if (setgid(gidInt)) {
286 std::cerr << "Failed to set gid '%s'" << strerror(errno) << std::endl;
287 return false;
288 }
289 std::cout << "setgid() to " << gidInt << std::endl;
290
291 if (noInitgroups || uid.isEmpty()) {
292 if (setgroups(0, nullptr)) {
293 std::cerr << "Failed to setgroups()" << std::endl;
294 return false;
295 }
296 } else {
297 QByteArray uidname;
298 uint uidInt = uid.toUInt(&ok);
299 if (ok) {
300 struct passwd *pw = getpwuid(uidInt);
301 if (pw) {
302 uidname = pw->pw_name;
303 }
304 } else {
305 uidname = uid.toUtf8();
306 }
307
308 if (initgroups(uidname.constData(), gidInt)) {
309 std::cerr << "Failed to setgroups()" << std::endl;
310 return false;
311 }
312 }
313 }
314
315 if (!uid.isEmpty()) {
316 uint uidInt = uid.toUInt(&ok);
317 if (!ok) {
318 struct passwd *upasswd = getpwnam(qUtf8Printable(uid));
319 if (upasswd) {
320 uidInt = upasswd->pw_uid;
321 } else {
322 std::cerr << "setuid user" << qUtf8Printable(uid) << "not found." << std::endl;
323 return false;
324 }
325 }
326
327 if (setuid(uidInt)) {
328 std::cerr << "Failed to set uid:" << strerror(errno) << std::endl;
329 return false;
330 }
331 std::cout << "setuid() to " << uidInt << std::endl;
332 }
333 return true;
334}
335
336void UnixFork::chownSocket(const QString &filename, const QString &uidGid)
337{
338 struct group *new_group = nullptr;
339 struct passwd *new_user = nullptr;
340
341 const QString owner = uidGid.section(QLatin1Char(':'), 0, 0);
342
343 bool ok;
344 uid_t new_uid = owner.toUInt(&ok);
345
346 if (!ok) {
347 new_user = getpwnam(qUtf8Printable(owner));
348 if (!new_user) {
349 qFatal("unable to find user '%s'", qUtf8Printable(owner));
350 }
351 new_uid = new_user->pw_uid;
352 }
353
354 gid_t new_gid = -1u;
355 const QString group = uidGid.section(QLatin1Char(':'), 1, 1);
356 if (!group.isEmpty()) {
357 new_gid = group.toUInt(&ok);
358 if (!ok) {
359 new_group = getgrnam(qUtf8Printable(group));
360 if (!new_group) {
361 qFatal("unable to find group '%s'", qUtf8Printable(group));
362 }
363 new_gid = new_group->gr_gid;
364 }
365 }
366
367 if (chown(qUtf8Printable(filename), new_uid, new_gid)) {
368 qFatal("chown() error '%s'", strerror(errno));
369 }
370}
371
372#ifdef Q_OS_LINUX
373// static int cpuSockets = -1;
374
375// socket/cores
376int parseProcCpuinfo()
377{
378 int cpuSockets = 1;
379 // std::pair<int, int> ret;
380
381 // static QMutex mutex;
382 // QMutexLocker locker(&mutex);
383 QFile file(QStringLiteral("/proc/cpuinfo"));
384 if (!file.open(QFile::ReadOnly | QFile::Text)) {
385 qCWarning(C_SERVER_UNIX) << "Failed to open file" << file.errorString();
386 // cpuSockets = 1;
387 // cpuCores = QThread::idealThreadCount();
388 return cpuSockets;
389 }
390
391 char buf[1024];
392 qint64 lineLength;
393 QByteArrayList physicalIds;
394 // cpuCores = 0;
395 while ((lineLength = file.readLine(buf, sizeof(buf))) != -1) {
396 const QByteArray line(buf, int(lineLength));
397 if (line.startsWith("physical id\t: ")) {
398 const QByteArray id = line.mid(14).trimmed();
399 if (!physicalIds.contains(id)) {
400 physicalIds.push_back(id);
401 }
402 } /* else if (line.startsWith("processor \t: ")) {
403 ++cpuCores;
404 }*/
405 }
406
407 // if (cpuCores == 0) {
408 // cpuCores = QThread::idealThreadCount();
409 // }
410
411 if (physicalIds.size()) {
412 cpuSockets = physicalIds.size();
413 } else {
414 cpuSockets = 1;
415 }
416 return cpuSockets;
417}
418#endif
419
420int UnixFork::idealProcessCount()
421{
422#ifdef Q_OS_LINUX
423 static int cpuSockets = parseProcCpuinfo();
424
425 return cpuSockets;
426#else
427 return 1;
428#endif
429}
430
431int UnixFork::idealThreadCount()
432{
433#ifdef Q_OS_LINUX
434 static int cpuCores = qMax(1, QThread::idealThreadCount());
435
436 return cpuCores;
437#else
438 return qMax(1, QThread::idealThreadCount());
439#endif
440}
441
442void UnixFork::handleSigHup()
443{
444 // do Qt stuff
445 // qDebug() << Q_FUNC_INFO << QCoreApplication::applicationPid();
446 // m_proc->kill();
447}
448
449void UnixFork::handleSigTerm()
450{
451 // do Qt stuff
452 // qDebug() << Q_FUNC_INFO << QCoreApplication::applicationPid();
453 // qApp->quit();
454 // m_proc->terminate();
455}
456
457void UnixFork::handleSigInt()
458{
459 // do Qt stuff
460 // qDebug() << Q_FUNC_INFO << QCoreApplication::applicationPid();
461 m_terminating = true;
462 if (m_child || (m_childs.isEmpty())) {
463 qDebug(C_SERVER_UNIX) << "SIGINT/SIGQUIT received, worker shutting down...";
464 Q_EMIT shutdown();
465 } else {
466 std::cout << "SIGINT/SIGQUIT received, terminating workers..." << std::endl;
467 setupCheckChildTimer();
468
469 static int count = 0;
470 if (count++ > 2) {
471 std::cout << "KILL workers..." << std::endl;
472 killChild();
473 QTimer::singleShot(std::chrono::seconds{3}, qApp, &QCoreApplication::quit);
474 } else if (count > 1) {
476 } else {
477 QTimer::singleShot(std::chrono::seconds{30}, this, [this]() {
478 std::cout << "workers terminating timeout, KILL ..." << std::endl;
479 killChild();
480 QTimer::singleShot(std::chrono::seconds{30}, qApp, &QCoreApplication::quit);
481 });
482
484 }
485 }
486}
487
488void UnixFork::handleSigChld()
489{
490 pid_t p;
491 int status;
492
493 while ((p = waitpid(-1, &status, WNOHANG)) > 0) {
494 /* Handle the death of pid p */
495 // qCDebug(C_SERVER_UNIX) << "SIGCHLD worker died" << p << WEXITSTATUS(status);
496 // SIGTERM is used when CHEAPED (ie post fork failed)
497 int exitStatus = WEXITSTATUS(status);
498
499 Worker worker;
500 auto it = m_childs.find(p);
501 if (it != m_childs.end()) {
502 worker = it.value();
503 m_childs.erase(it);
504 } else {
505 std::cout << "DAMN ! *UNKNOWN* worker (pid: " << p << ") died, killed by signal "
506 << exitStatus << " :( ignoring .." << std::endl;
507 continue;
508 }
509
510 if (WIFEXITED(status) && exitStatus == 15 && worker.restart == 0) {
511 // Child process cheaping
512 worker.null = true;
513 }
514
515 if (!worker.null && !m_terminating) {
516 if (worker.restart == 0) {
517 std::cout << "DAMN ! worker " << worker.id << " (pid: " << p
518 << ") died, killed by signal " << exitStatus << " :( trying respawn .."
519 << std::endl;
520 }
521 worker.restart = 0;
522 ++worker.respawn;
523 QTimer::singleShot(std::chrono::seconds{1}, this, &UnixFork::decreaseWorkerRespawn);
524 m_recreateWorker.push_back(worker);
525 qApp->quit();
526 } else if (!m_child && m_childs.isEmpty()) {
527 qApp->quit();
528 }
529 }
530
531 if (m_checkChildRestart) {
532 bool allRestarted = true;
533 auto it = m_childs.begin();
534 while (it != m_childs.end()) {
535 if (it.value().restart) {
536 if (++it.value().restart > 10) {
537 killChild(it.key());
538 }
539 allRestarted = false;
540 }
541 ++it;
542 }
543
544 if (allRestarted) {
545 m_checkChildRestart->deleteLater();
546 m_checkChildRestart = nullptr;
547 }
548 }
549}
550
551void UnixFork::setSched(Cutelyst::Server *wsgi, int workerId, int workerCore)
552{
553 int cpu_affinity = wsgi->cpuAffinity();
554 if (cpu_affinity) {
555 char buf[4096];
556
557 int pos =
558 snprintf(buf, 4096, "mapping worker %d core %d to CPUs:", workerId + 1, workerCore + 1);
559 if (pos < 25 || pos >= 4096) {
560 qCCritical(C_SERVER_UNIX) << "unable to initialize cpu affinity !!!";
561 exit(1);
562 }
563#if defined(__linux__) || defined(__GNU_kFreeBSD__)
564 cpu_set_t cpuset;
565#elif defined(__FreeBSD__)
566 cpuset_t cpuset;
567#endif
568#if defined(__linux__) || defined(__FreeBSD__) || defined(__GNU_kFreeBSD__)
569 int coreCount = idealThreadCount();
570
571 int workerThreads = 1;
572 if (wsgi->threads().compare(u"auto") == 0) {
573 workerThreads = coreCount;
574 } else if (wsgi->threads().toInt() > 1) {
575 workerThreads = wsgi->threads().toInt();
576 }
577
578 int base_cpu;
579 if (workerThreads > 1) {
580 base_cpu = (workerId * workerThreads) + workerCore * cpu_affinity;
581 } else {
582 base_cpu = workerId * cpu_affinity;
583 }
584
585 if (base_cpu >= coreCount) {
586 base_cpu = base_cpu % coreCount;
587 }
588
589 CPU_ZERO(&cpuset);
590 for (int i = 0; i < cpu_affinity; i++) {
591 if (base_cpu >= coreCount)
592 base_cpu = 0;
593 CPU_SET(base_cpu, &cpuset);
594 int ret = snprintf(buf + pos, 4096 - pos, " %d", base_cpu + 1);
595 if (ret < 2 || ret >= 4096) {
596 qCCritical(C_SERVER_UNIX) << "unable to initialize cpu affinity !!!";
597 exit(1);
598 }
599 pos += ret;
600 base_cpu++;
601 }
602#endif
603#if defined(__linux__) || defined(__GNU_kFreeBSD__)
604 if (sched_setaffinity(0, sizeof(cpu_set_t), &cpuset)) {
605 qFatal("failed to sched_setaffinity()");
606 }
607#elif defined(__FreeBSD__)
608 if (cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, sizeof(cpuset), &cpuset)) {
609 qFatal("cpuset_setaffinity");
610 }
611#endif
612 std::cout << buf << std::endl;
613 }
614}
615
616int UnixFork::setupUnixSignalHandlers()
617{
618 setupSocketPair(false, true);
619
620 // struct sigaction hup;
621 // hup.sa_handler = UnixFork::signalHandler;
622 // sigemptyset(&hup.sa_mask);
623 // hup.sa_flags = 0;
624 // hup.sa_flags |= SA_RESTART;
625
626 // if (sigaction(SIGHUP, &hup, 0) > 0)
627 // return 1;
628
629 // struct sigaction term;
630 // term.sa_handler = UnixFork::signalHandler;
631 // sigemptyset(&term.sa_mask);
632 // term.sa_flags |= SA_RESTART;
633
634 // if (sigaction(SIGTERM, &term, 0) > 0)
635 // return 2;
636
637 struct sigaction action;
638
639 // qDebug() << Q_FUNC_INFO << QCoreApplication::applicationPid();
640
641 memset(&action, 0, sizeof(struct sigaction));
642 action.sa_handler = UnixFork::signalHandler;
643 sigemptyset(&action.sa_mask);
644 action.sa_flags |= SA_RESTART;
645 if (sigaction(SIGINT, &action, nullptr) > 0)
646 return SIGINT;
647
648 memset(&action, 0, sizeof(struct sigaction));
649 action.sa_handler = UnixFork::signalHandler;
650 sigemptyset(&action.sa_mask);
651 action.sa_flags |= SA_RESTART;
652 if (sigaction(SIGQUIT, &action, nullptr) > 0)
653 return SIGQUIT;
654
655 memset(&action, 0, sizeof(struct sigaction));
656 action.sa_handler = UnixFork::signalHandler;
657 sigemptyset(&action.sa_mask);
658 action.sa_flags |= SA_RESTART;
659
660 if (sigaction(SIGCHLD, &action, nullptr) > 0)
661 return SIGCHLD;
662
663 return 0;
664}
665
666void UnixFork::setupSocketPair(bool closeSignalsFD, bool createPair)
667{
668 if (closeSignalsFD) {
669 close(signalsFd[0]);
670 close(signalsFd[1]);
671 }
672
673 if (createPair && ::socketpair(AF_UNIX, SOCK_STREAM, 0, signalsFd)) {
674 qFatal("Couldn't create SIGNALS socketpair");
675 }
676 delete m_signalNotifier;
677
678 m_signalNotifier = new QSocketNotifier(signalsFd[1], QSocketNotifier::Read, this);
679 connect(m_signalNotifier, &QSocketNotifier::activated, this, [this]() {
680 char signal;
681 read(signalsFd[1], &signal, sizeof(signal));
682
683 // qCDebug(C_SERVER_UNIX) << "Got signal:" << static_cast<int>(signal) << "pid:" <<
684 // QCoreApplication::applicationPid();
685 switch (signal) {
686 case SIGCHLD:
687 QTimer::singleShot(std::chrono::seconds{0}, this, &UnixFork::handleSigChld);
688 break;
689 case SIGINT:
690 case SIGQUIT:
691 handleSigInt();
692 break;
693 default:
694 break;
695 }
696 });
697}
698
699bool UnixFork::createChild(const Worker &worker, bool respawn)
700{
701 if (m_child) {
702 return false;
703 }
704
705 delete m_signalNotifier;
706 m_signalNotifier = nullptr;
707
708 qint64 childPID = fork();
709
710 if (childPID >= 0) {
711 if (childPID == 0) {
712 if (worker.respawn >= 5) {
713 std::cout << "WSGI worker " << worker.id << " respawned too much, sleeping a bit"
714 << std::endl;
715 sleep(2);
716 }
717
718#if defined(HAS_EventLoopEPoll)
719 auto epoll = qobject_cast<EventDispatcherEPoll *>(QAbstractEventDispatcher::instance());
720 if (epoll) {
721 epoll->reinstall();
722 }
723#endif
724
725 setupSocketPair(true, true);
726
727 m_child = true;
728 postFork(worker.id);
729
730 int ret = qApp->exec();
731 _exit(ret);
732 } else {
733 setupSocketPair(false, false);
734
735 if (respawn) {
736 std::cout << "Respawned WSGI worker " << worker.id << " (new pid: " << childPID
737 << ", cores: " << m_threads << ")" << std::endl;
738 } else {
739 if (m_processes == 1) {
740 std::cout << "spawned WSGI worker (and the only) (pid: " << childPID
741 << ", cores: " << m_threads << ")" << std::endl;
742 } else {
743 std::cout << "spawned WSGI worker " << worker.id << " (pid: " << childPID
744 << ", cores: " << m_threads << ")" << std::endl;
745 }
746 }
747 m_childs.insert(childPID, worker);
748 return true;
749 }
750 } else {
751 qFatal("Fork failed, quitting!!!!!!");
752 }
753
754 return false;
755}
756
757#include "moc_unixfork.cpp"
Implements a web server.
Definition server.h:60
QString threads
Definition server.h:150
virtual void terminateChild() override
Definition unixfork.cpp:187
virtual bool continueMaster(int *exit=nullptr) override
Definition unixfork.cpp:66
virtual void restart() override
Definition unixfork.cpp:99
virtual int exec(bool lazy, bool master) override
Definition unixfork.cpp:72
virtual void killChild() override
Definition unixfork.cpp:173
QAbstractEventDispatcher * instance(QThread *thread)
const char * constData() const const
QByteArray mid(qsizetype pos, qsizetype len) const const
QByteArray simplified() const const
qsizetype size() const const
qlonglong toLongLong(bool *ok, int base) const const
QByteArray trimmed() const const
qint64 applicationPid()
T & value() const const
QHash::iterator begin()
bool empty() const const
QHash::iterator end()
QHash::iterator erase(QHash::const_iterator pos)
QHash::iterator find(const Key &key)
QHash::iterator insert(const Key &key, const T &value)
bool isEmpty() const const
QList< Key > keys() const const
bool contains(const AT &value) const const
void push_back(QList::parameter_type value)
qsizetype size() const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
void activated(QSocketDescriptor socket, QSocketNotifier::Type type)
int compare(QLatin1StringView s1, const QString &s2, Qt::CaseSensitivity cs)
bool isEmpty() const const
QString section(QChar sep, qsizetype start, qsizetype end, QString::SectionFlags flags) const const
int toInt(bool *ok, int base) const const
uint toUInt(bool *ok, int base) const const
QByteArray toUtf8() const const
int idealThreadCount()
void start()
void timeout()