/**************************************************************************** ** ** This file is part of the KD Soap project. ** ** SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company ** ** SPDX-License-Identifier: MIT ** ****************************************************************************/ #include "KDSoapThreadPool.h" #include "KDSoapServerThread_p.h" #include class KDSoapThreadPool::Private { public: Private() : m_maxThreadCount(QThread::idealThreadCount()) { } KDSoapServerThread *chooseNextThread(); int m_maxThreadCount; typedef QList ThreadCollection; ThreadCollection m_threads; }; KDSoapThreadPool::KDSoapThreadPool(QObject *parent) : QObject(parent) , d(new Private) { } KDSoapThreadPool::~KDSoapThreadPool() { // ask all threads to finish, then delete them all for (KDSoapServerThread *thread : std::as_const(d->m_threads)) { thread->quitThread(); } for (KDSoapServerThread *thread : std::as_const(d->m_threads)) { thread->wait(); delete thread; } delete d; } void KDSoapThreadPool::setMaxThreadCount(int maxThreadCount) { d->m_maxThreadCount = maxThreadCount; } int KDSoapThreadPool::maxThreadCount() const { return d->m_maxThreadCount; } KDSoapServerThread *KDSoapThreadPool::Private::chooseNextThread() { KDSoapServerThread *chosenThread = nullptr; // Try to pick an existing thread int minSocketCount = 0; KDSoapServerThread *bestThread = nullptr; for (KDSoapServerThread *thr : std::as_const(m_threads)) { // We look at the amount of sockets connected to each thread, and pick the less busy one. // Note that this isn't fully accurate, due to Keep-Alive: it's possible for long-term // idling clients to be all on one thread, and active clients on another one, and this // wouldn't use CPU fully. But this is probably still better than no keep-alive (much more // connection overhead). Maybe we have to look at sockets who made a request in the last // N seconds... but the past is no indication of the future. const int sc = thr->socketCount(); if (sc == 0) { // Perfect, an idling thread // qDebug() << "Picked" << thr << "since it was idling"; chosenThread = thr; break; } if (!bestThread || sc < minSocketCount) { minSocketCount = sc; bestThread = thr; } } // Use an existing non-idling thread, if we reached maxThreads if (!chosenThread && bestThread && m_threads.count() == m_maxThreadCount) { chosenThread = bestThread; } // Create new thread if (!chosenThread) { chosenThread = new KDSoapServerThread(nullptr); // qDebug() << "Creating KDSoapServerThread" << chosenThread; m_threads.append(chosenThread); chosenThread->startThread(); } return chosenThread; } void KDSoapThreadPool::handleIncomingConnection(int socketDescriptor, KDSoapServer *server) { // First, pick or create a thread. KDSoapServerThread *chosenThread = d->chooseNextThread(); // Then create the socket, and register it in the corresponding socket-pool, and move it to the thread. chosenThread->handleIncomingConnection(socketDescriptor, server); } int KDSoapThreadPool::numConnectedSockets(const KDSoapServer *server) const { int sc = 0; for (KDSoapServerThread *thread : std::as_const(d->m_threads)) { sc += thread->socketCountForServer(server); } return sc; } void KDSoapThreadPool::disconnectSockets(KDSoapServer *server) { QSemaphore readyThreads; for (KDSoapServerThread *thread : std::as_const(d->m_threads)) { thread->disconnectSocketsForServer(server, readyThreads); } // Wait for all threads to have disconnected their sockets readyThreads.acquire(d->m_threads.count()); } int KDSoapThreadPool::totalConnectionCount(const KDSoapServer *server) const { int sc = 0; for (KDSoapServerThread *thread : std::as_const(d->m_threads)) { sc += thread->totalConnectionCountForServer(server); } return sc; } void KDSoapThreadPool::resetTotalConnectionCount(const KDSoapServer *server) { for (KDSoapServerThread *thread : std::as_const(d->m_threads)) { thread->resetTotalConnectionCountForServer(server); } } #include "moc_KDSoapThreadPool.cpp"