diff --git a/include/TLockQueue b/include/TLockQueue new file mode 100644 index 000000000..a9dfe2174 --- /dev/null +++ b/include/TLockQueue @@ -0,0 +1 @@ +#include "tlockqueue.h" diff --git a/include/TLockStack b/include/TLockStack new file mode 100644 index 000000000..aa42fdff9 --- /dev/null +++ b/include/TLockStack @@ -0,0 +1 @@ +#include "tlockstack.h" diff --git a/include/TSqlDatabase b/include/TSqlDatabase new file mode 100644 index 000000000..9533339be --- /dev/null +++ b/include/TSqlDatabase @@ -0,0 +1 @@ +#include "tsqldatabase.h" diff --git a/include/headers.pri b/include/headers.pri index be620c106..cd7b87683 100644 --- a/include/headers.pri +++ b/include/headers.pri @@ -46,6 +46,7 @@ HEADER_CLASSES += ../include/TSqlORMapperIterator HEADER_CLASSES += ../include/TSqlObject HEADER_CLASSES += ../include/TSqlQuery HEADER_CLASSES += ../include/TSqlQueryORMapper +HEADER_CLASSES += ../include/TSqlDatabase HEADER_CLASSES += ../include/TSystemGlobal HEADER_CLASSES += ../include/TTemporaryFile HEADER_CLASSES += ../include/TViewHelper @@ -99,6 +100,9 @@ HEADER_CLASSES += ../include/TMemcached HEADER_CLASSES += ../include/TMemcachedDriver HEADER_CLASSES += ../include/TStdoutSystemLogger HEADER_CLASSES += ../include/TStdErrSystemLogger +HEADER_CLASSES += ../include/TStack +HEADER_CLASSES += ../include/TLockStack +HEADER_CLASSES += ../include/TLockQueue HEADER_FILES = tabstractactioncontext.h HEADER_FILES += tabstractmodel.h @@ -151,6 +155,7 @@ HEADER_FILES += tsqlormapper.h HEADER_FILES += tsqlormapperiterator.h HEADER_FILES += tsqlquery.h HEADER_FILES += tsqlqueryormapper.h +HEADER_FILES += tsqldatabase.h HEADER_FILES += tsystemglobal.h HEADER_FILES += ttemporaryfile.h HEADER_FILES += tviewhelper.h @@ -202,6 +207,11 @@ HEADER_FILES += tmemcacheddriver.h HEADER_FILES += tsystemlogger.h HEADER_FILES += tstdoutsystemlogger.h HEADER_FILES += tstderrsystemlogger.h +HEADER_FILES += tstack.h +HEADER_FILES += tlockstack.h +HEADER_FILES += tlockqueue.h +HEADER_FILES += turingserver.h +HEADER_FILES += turingcoroutine.h HEADER_FILES += tsqldatabasepool.h HEADER_FILES += tkvsdatabasepool.h diff --git a/include/tlockqueue.h b/include/tlockqueue.h new file mode 100644 index 000000000..145d8ae13 --- /dev/null +++ b/include/tlockqueue.h @@ -0,0 +1 @@ +#include "../src/tlockqueue.h" diff --git a/include/tlockstack.h b/include/tlockstack.h new file mode 100644 index 000000000..4aaa2332b --- /dev/null +++ b/include/tlockstack.h @@ -0,0 +1 @@ +#include "../src/tlockstack.h" diff --git a/include/tsqldatabase.h b/include/tsqldatabase.h new file mode 100644 index 000000000..da628c895 --- /dev/null +++ b/include/tsqldatabase.h @@ -0,0 +1 @@ +#include "../src/tsqldatabase.h" diff --git a/src/corelib.pro b/src/corelib.pro index 2f2fbf594..b6f78004e 100644 --- a/src/corelib.pro +++ b/src/corelib.pro @@ -350,6 +350,12 @@ HEADERS += tsharedmemorykvs.h SOURCES += tsharedmemorykvs.cpp HEADERS += tfilesystemlogger.h SOURCES += tfilesystemlogger.cpp +HEADERS += tactioncontextroutine.h +SOURCES += tactioncontextroutine.cpp +# HEADERS += tthreadpool.h +# SOURCES += tthreadpool.cpp +HEADERS += tthreadpoolawaiter.h +SOURCES += tthreadpoolawaiter.cpp !wasm { HEADERS += tsmtpmailer.h @@ -397,8 +403,14 @@ windows { linux-* { HEADERS += tmultiplexingserver.h SOURCES += tmultiplexingserver_linux.cpp + HEADERS += turingserver.h + SOURCES += turingserver_linux.cpp + HEADERS += turingcoroutine.h + SOURCES += turingcoroutine_linux.cpp HEADERS += tactionworker.h SOURCES += tactionworker.cpp +# HEADERS += tactionroutine.h +# SOURCES += tactionroutine.cpp HEADERS += tepoll.h SOURCES += tepoll.cpp HEADERS += tepollsocket.h diff --git a/src/tabstractwebsocket.cpp b/src/tabstractwebsocket.cpp index e2c3d17cb..15f2f18cb 100644 --- a/src/tabstractwebsocket.cpp +++ b/src/tabstractwebsocket.cpp @@ -172,7 +172,7 @@ int TAbstractWebSocket::parse(QByteArray &recvData) websocketFrames().append(TWebSocketFrame()); } else { const TWebSocketFrame &f = websocketFrames().last(); - if (f.state() == TWebSocketFrame::Completed) { + if (f.state() == TWebSocketFrame::ProcessingState::Completed) { websocketFrames().append(TWebSocketFrame()); } } @@ -190,7 +190,7 @@ int TAbstractWebSocket::parse(QByteArray &recvData) while (!ds.atEnd()) { switch (pfrm->state()) { - case TWebSocketFrame::Empty: { + case TWebSocketFrame::ProcessingState::Empty: { hdr = dev->peek(14); QDataStream dshdr(hdr); dshdr.setByteOrder(QDataStream::BigEndian); @@ -247,9 +247,9 @@ int TAbstractWebSocket::parse(QByteArray &recvData) } if (pfrm->payloadLength() == 0) { - pfrm->setState(TWebSocketFrame::Completed); + pfrm->setState(TWebSocketFrame::ProcessingState::Completed); } else { - pfrm->setState(TWebSocketFrame::HeaderParsed); + pfrm->setState(TWebSocketFrame::ProcessingState::HeaderParsed); if (pfrm->payloadLength() >= 2 * 1024 * 1024 * 1024ULL) { tSystemError("Too big frame [{}:{}]", __FILE__, __LINE__); pfrm->clear(); @@ -266,8 +266,8 @@ int TAbstractWebSocket::parse(QByteArray &recvData) break; } - case TWebSocketFrame::HeaderParsed: // fall through - case TWebSocketFrame::MoreData: { + case TWebSocketFrame::ProcessingState::HeaderParsed: // fall through + case TWebSocketFrame::ProcessingState::MoreData: { tSystemDebug("WebSocket reading payload: available length:{}", dev->bytesAvailable()); tSystemDebug("WebSocket parsing length to read:{} current buf len:{}", (quint64)pfrm->payloadLength(), (qint64)pfrm->payload().size()); uint64_t size = std::min((uint64_t)(pfrm->payloadLength() - pfrm->payload().size()), (uint64_t)dev->bytesAvailable()); @@ -296,22 +296,22 @@ int TAbstractWebSocket::parse(QByteArray &recvData) tSystemDebug("WebSocket payload curent buf len: {}", (qint64)pfrm->payload().length()); if ((uint64_t)pfrm->payload().size() == pfrm->payloadLength()) { - pfrm->setState(TWebSocketFrame::Completed); + pfrm->setState(TWebSocketFrame::ProcessingState::Completed); tSystemDebug("Parse Completed payload len: {}", (qint64)pfrm->payload().size()); } else { - pfrm->setState(TWebSocketFrame::MoreData); + pfrm->setState(TWebSocketFrame::ProcessingState::MoreData); tSystemDebug("Parse MoreData payload len: {}", (qint64)pfrm->payload().size()); } break; } - case TWebSocketFrame::Completed: // fall through + case TWebSocketFrame::ProcessingState::Completed: // fall through default: Q_ASSERT(0); break; } - if (pfrm->state() == TWebSocketFrame::Completed) { + if (pfrm->state() == TWebSocketFrame::ProcessingState::Completed) { if (Q_UNLIKELY(!pfrm->validate())) { pfrm->clear(); continue; @@ -365,7 +365,7 @@ int TAbstractWebSocket::parse(QByteArray &recvData) void TAbstractWebSocket::sendHandshakeResponse() { THttpResponseHeader response; - response.setStatusLine(Tf::SwitchingProtocols, THttpUtility::getResponseReasonPhrase(Tf::SwitchingProtocols)); + response.setStatusLine(Tf::StatusCode::SwitchingProtocols, THttpUtility::getResponseReasonPhrase(Tf::StatusCode::SwitchingProtocols)); response.setRawHeader("Upgrade", "websocket"); response.setRawHeader("Connection", "Upgrade"); @@ -382,18 +382,26 @@ TAbstractWebSocket *TAbstractWebSocket::searchWebSocket(int sid) TAbstractWebSocket *sock = nullptr; switch (Tf::app()->multiProcessingModule()) { - case TWebApplication::Thread: + case TWebApplication::MultiProcessingModule::Thread: sock = TWebSocket::searchSocket(sid); break; - case TWebApplication::Epoll: { + case TWebApplication::MultiProcessingModule::Epoll: #ifdef Q_OS_LINUX sock = TEpollWebSocket::searchSocket(sid); #else tFatal("Unsupported MPM: epoll"); #endif break; - } + + case TWebApplication::MultiProcessingModule::Uring: +#ifdef Q_OS_LINUX + tFatal("Todo implementation"); + // TODO TODO +#else + tFatal("Unsupported MPM: uring"); +#endif + break; default: break; diff --git a/src/taccesslog.cpp b/src/taccesslog.cpp index a60675ad9..565011cf7 100644 --- a/src/taccesslog.cpp +++ b/src/taccesslog.cpp @@ -114,42 +114,12 @@ QByteArray TAccessLog::toByteArray(const QByteArray &layout, const QByteArray &d } -TAccessLogger::TAccessLogger() +TAccessLogger::TAccessLogger() : + _accessLog(std::make_unique()) { } -TAccessLogger::TAccessLogger(TAccessLogger &&other) -{ - _accessLog = std::move(other._accessLog); - _timer = std::move(other._timer); - other._accessLog = nullptr; -} - - -TAccessLogger::~TAccessLogger() -{ - close(); -} - - -TAccessLogger &TAccessLogger::operator=(TAccessLogger &&other) -{ - _accessLog = std::move(other._accessLog); - _timer = std::move(other._timer); - other._accessLog = nullptr; - return *this; -} - - -void TAccessLogger::open() -{ - if (!_accessLog) { - _accessLog = new TAccessLog(); - } -} - - void TAccessLogger::write() { if (_accessLog) { @@ -157,10 +127,3 @@ void TAccessLogger::write() Tf::writeAccessLog(*_accessLog); } } - - -void TAccessLogger::close() -{ - delete _accessLog; - _accessLog = nullptr; -} diff --git a/src/taccesslog.h b/src/taccesslog.h index d67ca01f9..c68449700 100644 --- a/src/taccesslog.h +++ b/src/taccesslog.h @@ -3,6 +3,7 @@ #include #include #include +#include class T_CORE_EXPORT TAccessLog { @@ -23,15 +24,10 @@ class T_CORE_EXPORT TAccessLog { class T_CORE_EXPORT TAccessLogger { public: TAccessLogger(); - TAccessLogger(const TAccessLogger &other) = delete; - TAccessLogger(TAccessLogger &&other); - ~TAccessLogger(); - TAccessLogger &operator=(const TAccessLogger &other) = delete; - TAccessLogger &operator=(TAccessLogger &&other); + TAccessLogger(TAccessLogger &&other) = default; + TAccessLogger &operator=(TAccessLogger &&other) = default; - void open(); void write(); - void close(); void setTimestamp(const QDateTime ×tamp) { @@ -54,7 +50,12 @@ class T_CORE_EXPORT TAccessLogger { } } - int statusCode() const { return (_accessLog) ? _accessLog->statusCode : -1; } + inline int statusCode() const { return (_accessLog) ? _accessLog->statusCode : -1; } + + inline void setStatusCode(Tf::StatusCode statusCode) + { + setStatusCode((int)statusCode); + } void setStatusCode(int statusCode) { @@ -63,7 +64,7 @@ class T_CORE_EXPORT TAccessLogger { } } - int responseBytes() const { return (_accessLog) ? _accessLog->responseBytes : -1; } + inline int responseBytes() const { return (_accessLog) ? _accessLog->responseBytes : -1; } void setResponseBytes(int bytes) { @@ -78,6 +79,8 @@ class T_CORE_EXPORT TAccessLogger { } private: - TAccessLog *_accessLog {nullptr}; + std::unique_ptr _accessLog; QElapsedTimer _timer; + + T_DISABLE_COPY(TAccessLogger) }; diff --git a/src/tactioncontext.cpp b/src/tactioncontext.cpp index 303c4a295..3a361e181 100644 --- a/src/tactioncontext.cpp +++ b/src/tactioncontext.cpp @@ -24,6 +24,14 @@ #include #include #include +#include + + +namespace { +// Stores a pointer to current action context into TLS +thread_local TActionContext *actionContextPtrTls = nullptr; + +} /*! \class TActionContext @@ -31,15 +39,18 @@ action controllers. */ + TActionContext::TActionContext() : TDatabaseContext() -{ } +{ + tSystemDebug("TActionContext::TActionContext ptr:{}", (uint64_t)this); +} TActionContext::~TActionContext() { release(); - accessLogger.close(); + tSystemDebug("TActionContext::~TActionContext ptr:{}", (uint64_t)this); } @@ -66,7 +77,6 @@ void TActionContext::execute(THttpRequest &request) // Access log if (Tf::isAccessLoggerAvailable()) { - accessLogger.open(); QByteArray firstLine; firstLine.reserve(200); firstLine += reqHeader.method(); @@ -87,7 +97,7 @@ void TActionContext::execute(THttpRequest &request) QString path = THttpUtility::fromUrlEncoding(reqHeader.path().mid(0, reqHeader.path().indexOf('?'))); if (LimitRequestBodyBytes > 0 && reqHeader.contentLength() > (uint)LimitRequestBodyBytes) { - throw ClientErrorException(Tf::RequestEntityTooLarge, __FILE__, __LINE__); // Request Entity Too Large + throw ClientErrorException((int)Tf::StatusCode::RequestEntityTooLarge, __FILE__, __LINE__); // Request Entity Too Large } // Routing info exists? @@ -140,7 +150,7 @@ void TActionContext::execute(THttpRequest &request) // Verify authenticity token if (EnableCsrfProtectionModuleFlag && _currController->csrfProtectionEnabled() && !_currController->exceptionActionsOfCsrfProtection().contains(route.action)) { - if (method == Tf::Post || method == Tf::Put || method == Tf::Patch || method == Tf::Delete) { + if (method == Tf::HttpMethod::Post || method == Tf::HttpMethod::Put || method == Tf::HttpMethod::Patch || method == Tf::HttpMethod::Delete) { if (!_currController->verifyRequest(*_httpRequest)) { throw SecurityException("Invalid authenticity token", __FILE__, __LINE__); } @@ -169,7 +179,7 @@ void TActionContext::execute(THttpRequest &request) // Dispatches bool inv = ctlrDispatcher.invoke(route.action, route.params); if (!inv) { - _currController->setStatusCode(Tf::NotFound); + _currController->setStatusCode(Tf::StatusCode::NotFound); } } @@ -193,7 +203,7 @@ void TActionContext::execute(THttpRequest &request) path = route.controller; } - if (Q_LIKELY(method == Tf::Get)) { // GET Method + if (Q_LIKELY(method == Tf::HttpMethod::Get)) { // GET Method QString canonicalPath = QUrl(QStringLiteral(".")).resolved(QUrl(path)).toString().mid(1); QFile reqPath(Tf::app()->publicPath() + canonicalPath); QFileInfo fi(reqPath); @@ -215,26 +225,26 @@ void TActionContext::execute(THttpRequest &request) // Sends a request file responseHeader.setRawHeader(QByteArrayLiteral("Last-Modified"), THttpUtility::toHttpDateTimeString(fi.lastModified())); QByteArray type = Tf::app()->internetMediaType(fi.suffix()); - responseBytes = writeResponse(Tf::OK, responseHeader, type, &reqPath, reqPath.size()); + responseBytes = writeResponse(Tf::StatusCode::OK, responseHeader, type, &reqPath, reqPath.size()); } else { // Not send the data - responseBytes = writeResponse(Tf::NotModified, responseHeader); + responseBytes = writeResponse(Tf::StatusCode::NotModified, responseHeader); } } else { if (!route.exists) { - responseBytes = writeResponse(Tf::NotFound, responseHeader); + responseBytes = writeResponse(Tf::StatusCode::NotFound, responseHeader); } else { // Routing not empty, redirect. responseHeader.setRawHeader(QByteArrayLiteral("Location"), QUrl(path).toEncoded()); responseHeader.setContentType(QByteArrayLiteral("text/html")); - responseBytes = writeResponse(Tf::Found, responseHeader); + responseBytes = writeResponse(Tf::StatusCode::Found, responseHeader); } } - } else if (method == Tf::Post) { - responseBytes = writeResponse(Tf::BadRequest, responseHeader); + } else if (method == Tf::HttpMethod::Post) { + responseBytes = writeResponse(Tf::StatusCode::BadRequest, responseHeader); } else { - responseBytes = writeResponse(Tf::BadRequest, responseHeader); + responseBytes = writeResponse(Tf::StatusCode::BadRequest, responseHeader); } accessLogger.setResponseBytes(responseBytes); @@ -244,7 +254,7 @@ void TActionContext::execute(THttpRequest &request) } catch (ClientErrorException &e) { Tf::warn("Caught {}: status code:{}", e.className(), e.statusCode()); tSystemWarn("Caught {}: status code:{}", e.className(), e.statusCode()); - int responseBytes = writeResponse(e.statusCode(), responseHeader); + int responseBytes = writeResponse(static_cast(e.statusCode()), responseHeader); accessLogger.setResponseBytes(responseBytes); accessLogger.setStatusCode(e.statusCode()); } catch (TfException &e) { @@ -252,13 +262,13 @@ void TActionContext::execute(THttpRequest &request) tSystemError("Caught {}: {} [{}:{}]", e.className(), e.message(), e.fileName(), e.lineNumber()); closeSocket(); accessLogger.setResponseBytes(0); - accessLogger.setStatusCode(Tf::InternalServerError); + accessLogger.setStatusCode(Tf::StatusCode::InternalServerError); } catch (std::exception &e) { Tf::error("Caught Exception: {}", e.what()); tSystemError("Caught Exception: {}", e.what()); closeSocket(); accessLogger.setResponseBytes(0); - accessLogger.setStatusCode(Tf::InternalServerError); + accessLogger.setStatusCode(Tf::StatusCode::InternalServerError); } accessLogger.write(); // Writes access log @@ -391,7 +401,7 @@ void TActionContext::flushResponse(TActionController *controller, bool immediate int responseBytes = 0; if (Q_UNLIKELY(controller->_response.isBodyNull())) { THttpResponseHeader header; - responseBytes = writeResponse(Tf::NotFound, header); + responseBytes = writeResponse(Tf::StatusCode::NotFound, header); accessLogger.setStatusCode(header.statusCode()); } else { @@ -407,21 +417,20 @@ void TActionContext::flushResponse(TActionController *controller, bool immediate if (immediate) { flushSocket(); accessLogger.write(); // Writes access log - accessLogger.close(); closeSocket(); } } -int64_t TActionContext::writeResponse(int statusCode, THttpResponseHeader &header) +int64_t TActionContext::writeResponse(Tf::StatusCode statusCode, THttpResponseHeader &header) { QByteArray body; - if (statusCode == Tf::NotModified) { + if (statusCode == Tf::StatusCode::NotModified) { return writeResponse(statusCode, header, QByteArray(), nullptr, 0); } - if (statusCode >= 400) { - QFile html(Tf::app()->publicPath() + QString::number(statusCode) + QLatin1String(".html")); + if ((int)statusCode >= 400) { + QFile html(Tf::app()->publicPath() + QString::number((int)statusCode) + QLatin1String(".html")); if (html.exists() && html.open(QIODevice::ReadOnly)) { body = html.readAll(); html.close(); @@ -432,7 +441,7 @@ int64_t TActionContext::writeResponse(int statusCode, THttpResponseHeader &heade body += ""; body += THttpUtility::getResponseReasonPhrase(statusCode); body += " ("; - body += QByteArray::number(statusCode); + body += QByteArray::number((int)statusCode); body += ")"; } @@ -441,7 +450,7 @@ int64_t TActionContext::writeResponse(int statusCode, THttpResponseHeader &heade } -int64_t TActionContext::writeResponse(int statusCode, THttpResponseHeader &header, const QByteArray &contentType, QIODevice *body, int64_t length) +int64_t TActionContext::writeResponse(Tf::StatusCode statusCode, THttpResponseHeader &header, const QByteArray &contentType, QIODevice *body, int64_t length) { header.setStatusLine(statusCode, THttpUtility::getResponseReasonPhrase(statusCode)); @@ -501,3 +510,18 @@ int TActionContext::keepAliveTimeout() }(); return keepAliveTimeout; } + + +TActionContext *TActionContext::currentActionContext() +{ + return actionContextPtrTls; +} + + +void TActionContext::setCurrentActionContext(TActionContext *context) +{ + if (context && actionContextPtrTls) { + tSystemWarn("Duplicate set : setCurrentActionContext()"); + } + actionContextPtrTls = context; +} diff --git a/src/tactioncontext.h b/src/tactioncontext.h index 78e908682..00c4e1c00 100644 --- a/src/tactioncontext.h +++ b/src/tactioncontext.h @@ -33,13 +33,15 @@ class T_CORE_EXPORT TActionContext : public TDatabaseContext, public TAbstractAc THttpRequest &httpRequest() override { return *_httpRequest; } void flushResponse(TActionController *controller, bool immediate); static int keepAliveTimeout(); + static TActionContext *currentActionContext(); + static void setCurrentActionContext(TActionContext *context); protected: void execute(THttpRequest &request); void release(); qintptr socketDescriptor() const { return socketDesc; } - int64_t writeResponse(int statusCode, THttpResponseHeader &header); - int64_t writeResponse(int statusCode, THttpResponseHeader &header, const QByteArray &contentType, QIODevice *body, int64_t length); + int64_t writeResponse(Tf::StatusCode statusCode, THttpResponseHeader &header); + int64_t writeResponse(Tf::StatusCode statusCode, THttpResponseHeader &header, const QByteArray &contentType, QIODevice *body, int64_t length); int64_t writeResponse(THttpResponseHeader &header, QIODevice *body, int64_t length); virtual int64_t writeResponse(THttpResponseHeader &, QIODevice *) { return 0; } diff --git a/src/tactioncontextroutine.cpp b/src/tactioncontextroutine.cpp new file mode 100644 index 000000000..3f048cd4a --- /dev/null +++ b/src/tactioncontextroutine.cpp @@ -0,0 +1,47 @@ +/* Copyright (c) 2025, AOYAMA Kazuharu + * All rights reserved. + * + * This software may be used and distributed according to the terms of + * the New BSD License, which is incorporated herein by reference. + */ + +#include "tactioncontextroutine.h" +#include "THttpRequest" +#include + + +// TActionContextRoutine::TActionContextRoutine(QWaitCondition &cv, QMutex &mutex, TQueue> &tasks, bool &stop) : +// _cv(cv), _mutex(mutex), _tasks(tasks), _stop(stop) +// { } + + +void TActionContextRoutine::start(QByteArray &readBuffer) +{ + TActionContext::setCurrentActionContext(this); + + THttpRequest request = THttpRequest::generate(readBuffer, QHostAddress("localhost"), this); + execute(request); + + TActionContext::setCurrentActionContext(nullptr); +} + + +int64_t TActionContextRoutine::writeResponse(THttpResponseHeader &header, QIODevice *body) +{ + if (keepAliveTimeout() > 0) { + header.setRawHeader(QByteArrayLiteral("Connection"), QByteArrayLiteral("Keep-Alive")); + } + // Writes HTTP header + result.response = header.toByteArray(); + + if (body) { + if (auto *buf = dynamic_cast(body); buf) { // dynamic_cast is faster for QBuffer + result.response += buf->buffer(); + } else if (auto *file = qobject_cast(body); file) { + result.fileName = file->fileName(); + } else { + tSystemError("Invalid body [{}:{}]", __FILE__, __LINE__); + } + } + return 0; +} diff --git a/src/tactioncontextroutine.h b/src/tactioncontextroutine.h new file mode 100644 index 000000000..28adbe5c1 --- /dev/null +++ b/src/tactioncontextroutine.h @@ -0,0 +1,22 @@ +#pragma once +#include + + +class T_CORE_EXPORT TActionContextRoutine : public TActionContext { +public: + TActionContextRoutine() = default; + ~TActionContextRoutine() = default; + void start(QByteArray &readBuffer); + + class Result { + public: + QByteArray response; + QString fileName; + } result; + +protected: + virtual int64_t writeResponse(THttpResponseHeader &, QIODevice *) override; + + T_DISABLE_COPY(TActionContextRoutine) + T_DISABLE_MOVE(TActionContextRoutine) +}; diff --git a/src/tactioncontroller.cpp b/src/tactioncontroller.cpp index d01e983a4..3f94ac837 100644 --- a/src/tactioncontroller.cpp +++ b/src/tactioncontroller.cpp @@ -225,7 +225,9 @@ const QStringList &TActionController::availableControllers() QStringList controllers; for (auto it = Tf::objectFactories()->cbegin(); it != Tf::objectFactories()->cend(); ++it) { if (it.key().endsWith("controller")) { + //if (it->first.endsWith("controller")) { controllers << QString::fromLatin1(it.key()); + //controllers << QString::fromLatin1(it->first); } } std::sort(controllers.begin(), controllers.end()); @@ -550,7 +552,7 @@ QByteArray TActionController::renderView(TActionView *view) Renders a static error page with the status code, which page is [statusCode].html in the \a public directory. */ -bool TActionController::renderErrorResponse(int statusCode) +bool TActionController::renderErrorResponse(Tf::StatusCode statusCode) { bool ret = false; @@ -559,7 +561,7 @@ bool TActionController::renderErrorResponse(int statusCode) return ret; } - QString file = Tf::app()->publicPath() + QString::number(statusCode) + QLatin1String(".html"); + QString file = Tf::app()->publicPath() + QString::number((int)statusCode) + QLatin1String(".html"); if (QFileInfo(file).exists()) { ret = sendFile(file, "text/html", "", false); } else { @@ -589,7 +591,7 @@ QString TActionController::partialViewClassName(const QString &partial) /*! Redirects to the URL \a url. */ -void TActionController::redirect(const QUrl &url, int statusCode) +void TActionController::redirect(const QUrl &url, Tf::StatusCode statusCode) { if ((int)_rendered > 0) { Tf::error("Unable to redirect. Has rendered already: {}", (className() + '#' + activeAction())); @@ -874,7 +876,7 @@ void TActionController::reset() _ctrlName.clear(); _actionName.clear(); _args.clear(); - _statCode = Tf::OK; // 200 OK + _statCode = (int)Tf::StatusCode::OK; // 200 OK _rendered = RenderState::NotRendered; _layoutEnable = true; _layoutName.clear(); diff --git a/src/tactioncontroller.h b/src/tactioncontroller.h index 65fcc5a9c..3efed3d3c 100644 --- a/src/tactioncontroller.h +++ b/src/tactioncontroller.h @@ -61,8 +61,8 @@ class T_CORE_EXPORT TActionController : public TAbstractController, public TActi bool layoutEnabled() const; void setLayout(const QString &layout); QString layout() const; - void setStatusCode(int code); - int statusCode() const { return _statCode; } + void setStatusCode(Tf::StatusCode code); + Tf::StatusCode statusCode() const { return static_cast(_statCode); } void setFlashValidationErrors(const TFormValidator &validator, const QString &prefix = QString("err_")); TSession &session() override { return _sessionStore; } void setSession(const TSession &session); @@ -93,8 +93,8 @@ class T_CORE_EXPORT TActionController : public TAbstractController, public TActi bool renderCbor(const QCborValue &value, QCborValue::EncodingOptions opt = QCborValue::NoTransformation); bool renderCbor(const QCborMap &map, QCborValue::EncodingOptions opt = QCborValue::NoTransformation); bool renderCbor(const QCborArray &array, QCborValue::EncodingOptions opt = QCborValue::NoTransformation); - bool renderErrorResponse(int statusCode); - void redirect(const QUrl &url, int statusCode = Tf::Found); + bool renderErrorResponse(Tf::StatusCode statusCode); + void redirect(const QUrl &url, Tf::StatusCode statusCode = Tf::StatusCode::Found); bool sendFile(const QString &filePath, const QByteArray &contentType, const QString &name = QString(), bool autoRemove = false); bool sendData(const QByteArray &data, const QByteArray &contentType, const QString &name = QString()); void rollbackTransaction() { _rollback = true; } @@ -144,7 +144,7 @@ class T_CORE_EXPORT TActionController : public TAbstractController, public TActi mutable QString _ctrlName; QString _actionName; QStringList _args; - int _statCode {Tf::OK}; // 200 OK + int _statCode {(int)Tf::StatusCode::OK}; // 200 OK RenderState _rendered {RenderState::NotRendered}; bool _layoutEnable {true}; QString _layoutName; @@ -198,9 +198,9 @@ inline QString TActionController::layout() const return _layoutName; } -inline void TActionController::setStatusCode(int code) +inline void TActionController::setStatusCode(Tf::StatusCode code) { - _statCode = code; + _statCode = (int)code; } // inline QString TActionController::flash(const QString &name) const diff --git a/src/tactionthread.cpp b/src/tactionthread.cpp index 7b5ee9df0..884d5b5a2 100644 --- a/src/tactionthread.cpp +++ b/src/tactionthread.cpp @@ -111,30 +111,28 @@ void TActionThread::run() try { for (;;) { - QList requests = readRequest(_httpSocket); - tSystemDebug("HTTP request count: {}", (qint64)requests.count()); + THttpRequest request = readRequest(_httpSocket); + //tSystemDebug("HTTP request count: {}", (qint64)requests.count()); - if (requests.isEmpty()) { + if (request.isEmpty()) { break; } // WebSocket? - QByteArray connectionHeader = requests[0].header().rawHeader(QByteArrayLiteral("Connection")).toLower(); + QByteArray connectionHeader = request.header().rawHeader(QByteArrayLiteral("Connection")).toLower(); if (Q_UNLIKELY(connectionHeader.contains("upgrade"))) { - QByteArray upgradeHeader = requests[0].header().rawHeader(QByteArrayLiteral("Upgrade")).toLower(); + QByteArray upgradeHeader = request.header().rawHeader(QByteArrayLiteral("Upgrade")).toLower(); tSystemDebug("Upgrade: {}", (const char*)upgradeHeader.data()); if (upgradeHeader == "websocket") { // Switch to WebSocket - if (!handshakeForWebSocket(requests[0].header())) { + if (!handshakeForWebSocket(request.header())) { goto socket_error; } } goto socket_cleanup; } - for (auto &req : requests) { - TActionContext::execute(req); - } + TActionContext::execute(request); if (keepAliveTimeout() == 0) { break; @@ -154,12 +152,12 @@ void TActionThread::run() Tf::warn("Caught ClientErrorException: status code:{}", e.statusCode()); tSystemWarn("Caught ClientErrorException: status code:{}", e.statusCode()); THttpResponseHeader header; - TActionContext::writeResponse(e.statusCode(), header); + TActionContext::writeResponse((Tf::StatusCode)e.statusCode(), header); } catch (std::exception &e) { Tf::error("Caught Exception: {}", e.what()); tSystemError("Caught Exception: {}", e.what()); THttpResponseHeader header; - TActionContext::writeResponse(Tf::InternalServerError, header); + TActionContext::writeResponse(Tf::StatusCode::InternalServerError, header); } receive_end: @@ -185,18 +183,11 @@ void TActionThread::emitError(int socketError) } -QList TActionThread::readRequest(THttpSocket *socket) +THttpRequest TActionThread::readRequest(THttpSocket *socket) { - QList reqs; - for (;;) { if (socket->waitForReadyReadRequest(500)) { - reqs = socket->read(); - if (!reqs.isEmpty()) { - return reqs; - } else { - break; - } + return socket->read(); } if (Q_UNLIKELY(socket->state() != QAbstractSocket::ConnectedState)) { @@ -211,7 +202,7 @@ QList TActionThread::readRequest(THttpSocket *socket) } socket->abort(); - return QList(); + return THttpRequest(); } diff --git a/src/tactionthread.h b/src/tactionthread.h index e73cfb149..73ace8679 100644 --- a/src/tactionthread.h +++ b/src/tactionthread.h @@ -18,7 +18,7 @@ class T_CORE_EXPORT TActionThread : public QThread, public TActionContext { static int threadCount(); static bool waitForAllDone(int msec); - static QList readRequest(THttpSocket *socket); + static THttpRequest readRequest(THttpSocket *socket); protected: void run() override; diff --git a/src/tactionworker.cpp b/src/tactionworker.cpp index 31f78e959..112467647 100644 --- a/src/tactionworker.cpp +++ b/src/tactionworker.cpp @@ -32,7 +32,7 @@ int64_t TActionWorker::writeResponse(THttpResponseHeader &header, QIODevice *bod // Check auto-remove bool autoRemove = false; - QFile *f = dynamic_cast(body); + QFile *f = qobject_cast(body); if (f) { QString filePath = f->fileName(); if (TActionContext::autoRemoveFiles.contains(filePath)) { @@ -44,7 +44,6 @@ int64_t TActionWorker::writeResponse(THttpResponseHeader &header, QIODevice *bod if (!TActionContext::stopped.load()) { _socket->sendData(header.toByteArray(), body, autoRemove, std::move(accessLogger)); } - accessLogger.close(); return 0; } @@ -68,9 +67,10 @@ void TActionWorker::start(TEpollHttpSocket *sock) _socket = sock; _httpRequest += _socket->readRequest(); _clientAddr = _socket->peerAddress(); - QList requests = THttpRequest::generate(_httpRequest, _clientAddr, this); + THttpRequest request = THttpRequest::generate(_httpRequest, _clientAddr, this); // Loop for HTTP-pipeline requests + /* for (THttpRequest &req : requests) { // Executes a action context TActionContext::execute(req); @@ -79,6 +79,8 @@ void TActionWorker::start(TEpollHttpSocket *sock) break; } } + */ + TActionContext::execute(request); TActionContext::release(); _httpRequest.clear(); diff --git a/src/tapplicationserverbase.h b/src/tapplicationserverbase.h index 1097f051b..ef9c12153 100644 --- a/src/tapplicationserverbase.h +++ b/src/tapplicationserverbase.h @@ -35,6 +35,7 @@ class T_CORE_EXPORT TApplicationServerBase { friend class TThreadApplicationServer; friend class TMultiplexingServer; + friend class TUringServer; T_DISABLE_COPY(TApplicationServerBase) T_DISABLE_MOVE(TApplicationServerBase) }; diff --git a/src/tatomic.h b/src/tatomic.h index 30d0dd6ef..aded05355 100644 --- a/src/tatomic.h +++ b/src/tatomic.h @@ -8,7 +8,11 @@ class TAtomic : public std::atomic { public: TAtomic() = default; ~TAtomic() = default; - TAtomic(T item) : + TAtomic(const TAtomic &) = delete; + TAtomic &operator=(const TAtomic &) = delete; + TAtomic(TAtomic &&) = delete; + TAtomic &operator=(TAtomic &&) = delete; + explicit TAtomic(T item) : std::atomic(item) { } operator T() const { return load(); } @@ -37,7 +41,4 @@ class TAtomic : public std::atomic { { return std::atomic::compare_exchange_strong(expected, newValue, std::memory_order_acq_rel); } - - T_DISABLE_COPY(TAtomic) - T_DISABLE_MOVE(TAtomic) }; diff --git a/src/tatomicptr.h b/src/tatomicptr.h index 7b4f85ea6..fcd4edcf9 100644 --- a/src/tatomicptr.h +++ b/src/tatomicptr.h @@ -14,9 +14,9 @@ inline void threadFence() template class TAtomicPtr { public: - TAtomicPtr(T *value = nullptr); + explicit TAtomicPtr(T *value = nullptr); TAtomicPtr(const TAtomicPtr &other); - ~TAtomicPtr() { } + ~TAtomicPtr() = default; operator T *() const; T *load(bool *mark = nullptr) const; @@ -43,15 +43,13 @@ class TAtomicPtr { template inline TAtomicPtr::TAtomicPtr(T *value) : atomicPtr((quintptr)value) -{ -} +{} template inline TAtomicPtr::TAtomicPtr(const TAtomicPtr &other) : atomicPtr(other.atomicPtr.load()) -{ -} +{} template diff --git a/src/tcachesharedmemorystore.cpp b/src/tcachesharedmemorystore.cpp index 3ba732544..fb68b05e9 100644 --- a/src/tcachesharedmemorystore.cpp +++ b/src/tcachesharedmemorystore.cpp @@ -1,6 +1,6 @@ #include "tcachesharedmemorystore.h" #include "tsharedmemorykvs.h" -#include "tkvsdatabasepool.h" +#include "TWebApplication" TCacheSharedMemoryStore::TCacheSharedMemoryStore() @@ -13,8 +13,10 @@ TCacheSharedMemoryStore::~TCacheSharedMemoryStore() void TCacheSharedMemoryStore::init() { - auto settings = TKvsDatabasePool::instance()->getDatabaseSettings(Tf::KvsEngine::CacheKvs); - TSharedMemoryKvs::initialize(settings.databaseName, settings.connectOptions); + const QVariantMap &settings = Tf::app()->kvsSettings(Tf::KvsEngine::CacheKvs); + auto databaseName = settings.value("DatabaseName").toString().trimmed(); + auto connectOptions = settings.value("ConnectOptions").toString().trimmed(); + TSharedMemoryKvs::initialize(databaseName, connectOptions); } diff --git a/src/tcachesqlitestore.cpp b/src/tcachesqlitestore.cpp index 90ada1ca9..4f341b9e2 100644 --- a/src/tcachesqlitestore.cpp +++ b/src/tcachesqlitestore.cpp @@ -25,7 +25,7 @@ static int sqliteMinorVersion; inline QSqlError lastError() { - return Tf::currentSqlDatabase(Tf::app()->databaseIdForCache()).lastError(); + return Tf::currentSqlDatabase(Tf::app()->databaseIdForCache()).sqlDatabase().lastError(); } @@ -64,10 +64,10 @@ bool TCacheSQLiteStore::createTable(const QString &table) { int id = Tf::app()->databaseIdForCache(); auto db = TSqlDatabasePool::instance()->database(id); - bool ret = queryNonTrx(db, QStringLiteral("CREATE TABLE IF NOT EXISTS %1 (%2 TEXT PRIMARY KEY, %3 INTEGER, %4 BLOB)").arg(table, KEY_COLUMN, TIMESTAMP_COLUMN, BLOB_COLUMN)); - queryNonTrx(db, QStringLiteral("VACUUM")); + bool ret = queryNonTrx(db->sqlDatabase(), QStringLiteral("CREATE TABLE IF NOT EXISTS %1 (%2 TEXT PRIMARY KEY, %3 INTEGER, %4 BLOB)").arg(table, KEY_COLUMN, TIMESTAMP_COLUMN, BLOB_COLUMN)); + queryNonTrx(db->sqlDatabase(), QStringLiteral("VACUUM")); - TSqlDatabasePool::instance()->pool(db); + //TSqlDatabasePool::instance()->pool(db); return ret; } diff --git a/src/tdatabasecontext.cpp b/src/tdatabasecontext.cpp index 860392d83..d3c9305cb 100644 --- a/src/tdatabasecontext.cpp +++ b/src/tdatabasecontext.cpp @@ -9,19 +9,19 @@ #include "tkvsdatabasepool.h" #include "tsqldatabasepool.h" #include "tsystemglobal.h" -#include -#include -#include #include #include +#include #include +#include +#include #include +#include namespace { // Stores a pointer to current database context into TLS -// - qulonglong type to prevent qThreadStorage_deleteData() function to work -QThreadStorage databaseContextPtrTls; -QSqlDatabase invalidDb; +thread_local TDatabaseContext *databaseContextPtrTls = nullptr; + } /*! @@ -30,10 +30,16 @@ QSqlDatabase invalidDb; database access. */ -TDatabaseContext::TDatabaseContext() : - sqlDatabases(), - kvsDatabases() +TDatabaseContext::TDatabaseContext() { + const int count = Tf::app()->sqlDatabaseSettingsCount(); + if (sqlTransactions.size() < (size_t)count) { + sqlTransactions.resize(count); + } + + if (kvsDatabases.size() < (size_t)Tf::KvsEngine::Num) { + kvsDatabases.resize((size_t)Tf::KvsEngine::Num); + } } @@ -44,65 +50,72 @@ TDatabaseContext::~TDatabaseContext() } -QSqlDatabase &TDatabaseContext::getSqlDatabase(int id) +TSqlDatabase &TDatabaseContext::getSqlDatabase(int id) { if (id < 0) { - return invalidDb; // invalid database + throw RuntimeException("error database id", __FILE__, __LINE__); } if (id >= Tf::app()->sqlDatabaseSettingsCount()) { throw RuntimeException("error database id", __FILE__, __LINE__); } - TSqlTransaction &tx = sqlDatabases[id]; - QSqlDatabase &db = tx.database(); + TSqlTransaction &tx = sqlTransactions[id]; + TSqlDatabase::Handle &handle = tx.database(); - if (db.isValid() && tx.isActive()) { - return db; + if (handle && handle->isValid()) { + return *handle; } int n = 0; do { - if (!db.isValid()) { - db = TSqlDatabasePool::instance()->database(id); + if (!handle || !handle->isValid()) { + handle = std::move(TSqlDatabasePool::instance()->database(id)); } if (tx.begin()) { break; } - TSqlDatabasePool::instance()->pool(db, true); + + handle = std::move(TSqlDatabase::Handle{}); } while (++n < 2); // try two times idleElapsed = (uint)std::time(nullptr); - return db; + return *handle; } void TDatabaseContext::releaseSqlDatabases() { rollbackTransactions(); - sqlDatabases.clear(); + + for (auto &tx : sqlTransactions) { + if (tx.database()) { + tx.database() = TSqlDatabase::Handle{}; + } + } } -TKvsDatabase &TDatabaseContext::getKvsDatabase(Tf::KvsEngine engine) +TKvsDatabase::Handle &TDatabaseContext::getKvsDatabase(Tf::KvsEngine engine) { - TKvsDatabase &db = kvsDatabases[(int)engine]; - if (!db.isValid()) { - db = TKvsDatabasePool::instance()->database(engine); + auto &handle = kvsDatabases[(int)engine]; + if (!handle || !handle->isValid()) { + handle = std::move(TKvsDatabasePool::instance()->database(engine)); } idleElapsed = (uint)std::time(nullptr); - return db; + return handle; } void TDatabaseContext::releaseKvsDatabases() { - for (QMap::iterator it = kvsDatabases.begin(); it != kvsDatabases.end(); ++it) { - TKvsDatabasePool::instance()->pool(it.value()); + for (auto &handle : kvsDatabases) { + if (handle) { + handle = TKvsDatabase::Handle{}; + } } - kvsDatabases.clear(); } @@ -124,16 +137,14 @@ void TDatabaseContext::setTransactionEnabled(bool enable, int id) Tf::error("Invalid database ID: {}", id); return; } - return sqlDatabases[id].setEnabled(enable); + return sqlTransactions[id].setEnabled(enable); } void TDatabaseContext::commitTransactions() { - for (QMap::iterator it = sqlDatabases.begin(); it != sqlDatabases.end(); ++it) { - TSqlTransaction &tx = it.value(); - tx.commit(); - TSqlDatabasePool::instance()->pool(tx.database()); + for (auto it = sqlTransactions.begin(); it != sqlTransactions.end(); ++it) { + it->commit(); } } @@ -142,24 +153,21 @@ bool TDatabaseContext::commitTransaction(int id) { bool res = false; - if (id < 0 || id >= sqlDatabases.count()) { + if (id < 0 || id >= (int)sqlTransactions.size()) { Tf::error("Failed to commit transaction. Invalid database ID: {}", id); return res; } - TSqlTransaction &tx = sqlDatabases[id]; + TSqlTransaction &tx = sqlTransactions[id]; res = tx.commit(); - TSqlDatabasePool::instance()->pool(sqlDatabases[id].database()); return res; } void TDatabaseContext::rollbackTransactions() { - for (QMap::iterator it = sqlDatabases.begin(); it != sqlDatabases.end(); ++it) { - TSqlTransaction &tx = it.value(); - tx.rollback(); - TSqlDatabasePool::instance()->pool(tx.database(), true); + for (auto it = sqlTransactions.begin(); it != sqlTransactions.end(); ++it) { + it->rollback(); } } @@ -168,12 +176,11 @@ bool TDatabaseContext::rollbackTransaction(int id) { bool res = false; - if (id < 0 || id >= sqlDatabases.count()) { + if (id < 0 || id >= (int)sqlTransactions.size()) { Tf::error("Failed to rollback transaction. Invalid database ID: {}", id); return res; } - res = sqlDatabases[id].rollback(); - TSqlDatabasePool::instance()->pool(sqlDatabases[id].database(), true); + res = sqlTransactions[id].rollback(); return res; } @@ -186,16 +193,16 @@ int TDatabaseContext::idleTime() const TDatabaseContext *TDatabaseContext::currentDatabaseContext() { - return reinterpret_cast(databaseContextPtrTls.localData()); + return databaseContextPtrTls; } void TDatabaseContext::setCurrentDatabaseContext(TDatabaseContext *context) { - if (context && databaseContextPtrTls.localData()) { + if (context && databaseContextPtrTls) { tSystemWarn("Duplicate set : setCurrentDatabaseContext()"); } - databaseContextPtrTls.setLocalData((qulonglong)context); + databaseContextPtrTls = context; } diff --git a/src/tdatabasecontext.h b/src/tdatabasecontext.h index 4baecbf50..79977fb41 100644 --- a/src/tdatabasecontext.h +++ b/src/tdatabasecontext.h @@ -1,10 +1,12 @@ #pragma once -#include +#include #include +#include #include +#include "tsqldatabasepool.h" +#include class QSqlDatabase; -class TKvsDatabase; class TCache; @@ -12,9 +14,11 @@ class T_CORE_EXPORT TDatabaseContext { public: TDatabaseContext(); virtual ~TDatabaseContext(); + TDatabaseContext(TDatabaseContext &&) = default; + TDatabaseContext &operator=(TDatabaseContext &&) = default; - QSqlDatabase &getSqlDatabase(int id = 0); - TKvsDatabase &getKvsDatabase(Tf::KvsEngine engine); + TSqlDatabase &getSqlDatabase(int id = 0); + TKvsDatabase::Handle &getKvsDatabase(Tf::KvsEngine engine); void setTransactionEnabled(bool enable, int id = 0); void release(); @@ -31,14 +35,13 @@ class T_CORE_EXPORT TDatabaseContext { void releaseKvsDatabases(); void releaseSqlDatabases(); - QMap sqlDatabases; - QMap kvsDatabases; + std::vector sqlTransactions; + std::vector kvsDatabases; private: uint idleElapsed {0}; TCache *cachep {nullptr}; T_DISABLE_COPY(TDatabaseContext) - T_DISABLE_MOVE(TDatabaseContext) }; diff --git a/src/tepoll.cpp b/src/tepoll.cpp index eed89fcc1..3b5b9e42e 100644 --- a/src/tepoll.cpp +++ b/src/tepoll.cpp @@ -197,15 +197,16 @@ bool TEpoll::deletePoll(TEpollSocket *socket) void TEpoll::dispatchEvents() { - TSendData *sd; - while (_sendRequests.dequeue(sd)) { - TEpollSocket *sock = sd->socket; + std::optional sd; + + while ((sd = _sendRequests.dequeue())) { + TEpollSocket *sock = (*sd)->socket; if (Q_UNLIKELY(sock->socketDescriptor() <= 0)) { continue; } - switch (sd->method) { + switch ((*sd)->method) { case TSendData::Disconnect: deletePoll(sock); sock->dispose(); @@ -213,14 +214,14 @@ void TEpoll::dispatchEvents() case TSendData::SwitchToWebSocket: { tSystemDebug("Switch to WebSocket"); - Q_ASSERT(sd->buffer == nullptr); + Q_ASSERT((*sd)->buffer == nullptr); - QByteArray secKey = sd->header.rawHeader("Sec-WebSocket-Key"); + QByteArray secKey = (*sd)->header.rawHeader("Sec-WebSocket-Key"); tSystemDebug("secKey: {}", secKey); int newsocket = TApplicationServerBase::duplicateSocket(sock->socketDescriptor()); // Switch to WebSocket - TEpollWebSocket *ws = new TEpollWebSocket(newsocket, sock->peerAddress(), sd->header); + TEpollWebSocket *ws = new TEpollWebSocket(newsocket, sock->peerAddress(), (*sd)->header); ws->moveToThread(Tf::app()->thread()); bool res = ws->watch(); if (!res) { @@ -233,7 +234,7 @@ void TEpoll::dispatchEvents() // WebSocket opening TSession session; - QByteArray sessionId = sd->header.cookie(TSession::sessionName()); + QByteArray sessionId = (*sd)->header.cookie(TSession::sessionName()); if (!sessionId.isEmpty()) { // Finds a session session = TSessionManager::instance().findSession(sessionId); @@ -244,11 +245,11 @@ void TEpoll::dispatchEvents() default: tSystemError("Logic error [{}:{}]", __FILE__, __LINE__); - delete sd->buffer; + delete (*sd)->buffer; break; } - delete sd; + delete *sd; } } diff --git a/src/tepollhttpsocket.cpp b/src/tepollhttpsocket.cpp index 682aebaba..934f77353 100644 --- a/src/tepollhttpsocket.cpp +++ b/src/tepollhttpsocket.cpp @@ -125,7 +125,7 @@ bool TEpollHttpSocket::seekRecvBuffer(int pos) } else { if (systemLimitBodyBytes > 0 && _recvBuffer.length() > systemLimitBodyBytes) { _recvBuffer.resize(0); - throw ClientErrorException(Tf::RequestEntityTooLarge); // Request Entity Too Large + throw ClientErrorException((int)Tf::StatusCode::RequestEntityTooLarge); // Request Entity Too Large } _lengthToRead = std::max(_lengthToRead - pos, (int64_t)0); @@ -192,7 +192,7 @@ void TEpollHttpSocket::parse() if (systemLimitBodyBytes > 0 && header.contentLength() > systemLimitBodyBytes) { _recvBuffer.resize(0); - throw ClientErrorException(Tf::RequestEntityTooLarge); // Request EhttpBuffery Too Large + throw ClientErrorException((int)Tf::StatusCode::RequestEntityTooLarge); // Request EhttpBuffery Too Large } _lengthToRead = std::max(idx + 4 + (int64_t)header.contentLength() - (int64_t)_recvBuffer.length(), (int64_t)0); diff --git a/src/tepollwebsocket.cpp b/src/tepollwebsocket.cpp index 326227479..432c29505 100644 --- a/src/tepollwebsocket.cpp +++ b/src/tepollwebsocket.cpp @@ -47,7 +47,7 @@ TEpollWebSocket::~TEpollWebSocket() bool TEpollWebSocket::canReadRequest() { for (auto &frm : (const QList &)_frames) { - if (frm.isFinalFrame() && frm.state() == TWebSocketFrame::Completed) { + if (frm.isFinalFrame() && frm.state() == TWebSocketFrame::ProcessingState::Completed) { return true; } } @@ -113,7 +113,7 @@ QList> TEpollWebSocket::readAllBinaryRequest() while (!_frames.isEmpty()) { TWebSocketFrame frm = _frames.takeFirst(); payload += frm.payload(); - if (frm.isFinalFrame() && frm.state() == TWebSocketFrame::Completed) { + if (frm.isFinalFrame() && frm.state() == TWebSocketFrame::ProcessingState::Completed) { ret << qMakePair(opcode, payload); break; } @@ -151,7 +151,7 @@ void TEpollWebSocket::process() auto payloads = readAllBinaryRequest(); if (!payloads.isEmpty()) { - TWebSocketWorker *_worker = new TWebSocketWorker(TWebSocketWorker::Receiving, this, reqHeader.path()); + TWebSocketWorker *_worker = new TWebSocketWorker(TWebSocketWorker::RunMode::Receiving, this, reqHeader.path()); _worker->setPayloads(payloads); startWorker(_worker); delete _worker; @@ -182,7 +182,7 @@ void TEpollWebSocket::releaseWorker() void TEpollWebSocket::startWorkerForOpening(const TSession &session) { - TWebSocketWorker *worker = new TWebSocketWorker(TWebSocketWorker::Opening, this, reqHeader.path()); + TWebSocketWorker *worker = new TWebSocketWorker(TWebSocketWorker::RunMode::Opening, this, reqHeader.path()); worker->setSession(session); startWorker(worker); @@ -194,7 +194,7 @@ void TEpollWebSocket::startWorkerForOpening(const TSession &session) void TEpollWebSocket::startWorkerForClosing() { if (!closing.load()) { - TWebSocketWorker *worker = new TWebSocketWorker(TWebSocketWorker::Closing, this, reqHeader.path()); + TWebSocketWorker *worker = new TWebSocketWorker(TWebSocketWorker::RunMode::Closing, this, reqHeader.path()); startWorker(worker); releaseWorker(); diff --git a/src/test/container/container.pro b/src/test/container/container.pro new file mode 100644 index 000000000..4490d80b7 --- /dev/null +++ b/src/test/container/container.pro @@ -0,0 +1,4 @@ +include(../test.pri) +TARGET = container +SOURCES = main.cpp +DEFINES += GLOG_USE_GLOG_EXPORT diff --git a/src/test/container/main.cpp b/src/test/container/main.cpp new file mode 100644 index 000000000..b635c25ce --- /dev/null +++ b/src/test/container/main.cpp @@ -0,0 +1,391 @@ +#include +#include "tstack.h" +#include "tlockstack.h" +#include "tlockqueue.h" +#include +#include +#include +#include +#include +#include +#include + + +template +class FCQueue { +public: + enum class State : uint8_t { Empty, Pending, Applied }; + enum class Op : uint8_t { Push, Pop }; + + struct Request { + std::atomic state {FCQueue::State::Empty}; + Op op {FCQueue::Op::Push}; + std::optional value; + }; + + explicit FCQueue() // : reqs_{128} + { + // while (reqs_.size() < 128) { + // reqs_.push_back(new Request{}); + // } + } + + ~FCQueue() + { + // for (auto p : reqs_) { + // delete p; + // } + } + + void push(T v) + { + if (try_push(v)) { + return; + } + + size_t id = acquireSlot(); + auto& r = reqs_[id]; + + // State s = r.state.load(std::memory_order_acquire); + // if (s != State::Empty) { + // throw std::runtime_error("FCQueue: push logic error"); + // } + + r.value = std::move(v); + r.op = Op::Push; + r.state.store(State::Pending, std::memory_order_release); + combineCounter.fetch_add(1, std::memory_order_relaxed); + + while (r.state.load(std::memory_order_acquire) != State::Applied) { + std::this_thread::yield(); + combine(); + } + r.state.store(State::Empty, std::memory_order_release); + } + + std::optional pop() + { + std::optional val; + + if (try_pop(val)) { + return val; + } + + size_t id = acquireSlot(); + auto& r = reqs_[id]; + + r.op = Op::Pop; + r.state.store(State::Pending, std::memory_order_release); + combineCounter.fetch_add(1, std::memory_order_relaxed); + + while (r.state.load(std::memory_order_acquire) != State::Applied) { + std::this_thread::yield(); + combine(); + } + + val = std::move(r.value); + r.state.store(State::Empty, std::memory_order_release); + return val; + } + +private: + bool try_push(T v) + { + if (!combineLock_.try_lock()) { + return false; + } + + q_.push(std::move(v)); + combineLock_.unlock(); + return true; + } + + bool try_pop(std::optional &v) + { + if (!combineLock_.try_lock()) { + return false; + } + + if (q_.empty()) { + v = std::nullopt; + } else { + v = std::move(q_.front()); + q_.pop(); + } + + combineLock_.unlock(); + return true; + } + + size_t acquireSlot() + { + static thread_local size_t mySlot = SIZE_MAX; + + if (mySlot != SIZE_MAX) [[likely]] { + return mySlot; + } + + mySlot = slotCounter_.load(std::memory_order_acquire) + 1; + if (mySlot >= reqs_.size()) { + ensure_slot(mySlot); + } + mySlot = slotCounter_.fetch_add(1, std::memory_order_relaxed); + //std::print("mySlot: {}\n", mySlot); + return mySlot; + } + + void ensure_slot(size_t idx) + { + throw std::runtime_error("FCQueue: ensure_slot error"); + // std::lock_guard lock(combineLock_); + + // size_t extend = reqs_.size(); + // while (extend <= idx) extend *= 2; + // while (reqs_.size() < extend) { + // reqs_.push_back(new Request{}); + // } + + } + + void combine() + { + if (!combineLock_.try_lock()) { + return; + } + + static size_t idx = 0; + size_t combcnt = combineCounter.exchange(0, std::memory_order_acq_rel); + size_t cnt = 0; + size_t maxslot = slotCounter_.load(std::memory_order_acquire); + + while (cnt < combcnt) [[likely]] { + if (++idx >= maxslot) [[unlikely]] { + idx = 0; + maxslot = slotCounter_.load(std::memory_order_relaxed); + } + + auto &r = reqs_[idx]; + State s = r.state.load(std::memory_order_acquire); + + if (s != State::Pending) [[unlikely]] { + continue; // Empty or Applied は combiner が触らない + } + + switch (r.op) { + case Op::Push: + q_.push(std::move(r.value.value())); + break; + + case Op::Pop: + if (q_.empty()) { + r.value = std::nullopt; + } else { + r.value = std::move(q_.front()); + q_.pop(); + } + break; + + default: + break; + } + r.state.store(State::Applied, std::memory_order_release); + cnt++; + } + + combineLock_.unlock(); + } + +private: + std::mutex combineLock_; + std::atomic combineCounter {0}; + //std::deque reqs_; + std::array reqs_; + //std::vector reqs_; + std::queue q_; + std::atomic slotCounter_ {0}; +}; + + +static const int THREADS = std::max((int)(std::thread::hardware_concurrency() * 0.8), 1); +static constexpr int OPS_PER_THREAD = 200000; +static const int TOTAL = THREADS * OPS_PER_THREAD; + + +class TestQueue : public QObject { + Q_OBJECT +private: + template + void correctnessTest() + { + for (int i = 0; i < 20; i++) { + Qu q; + + // Duplicate check flag + std::vector> seen(TOTAL); + for (auto &v : seen) v.store(false); + + std::atomic consumedCount{0}; + + // -------- Producer -------- + auto producer = [&](int tid) { + std::mt19937_64 rng(std::random_device{}()); + int base = tid * OPS_PER_THREAD; + for (int i = 0; i < OPS_PER_THREAD; i++) { + (q.*Enqueue)(base + i); // unique value + if ((rng() & 0xF) == 0) { + std::this_thread::yield(); + QThread::usleep(10); + } + //std::cout << "tid:" << tid << " i:" << i << std::endl; + } + }; + + // -------- Consumer -------- + auto consumer = [&]() { + //int get_counter = 0; + std::mt19937_64 rng(std::random_device{}()); + while (consumedCount.load() < TOTAL) { + //std::print("consumedCount remain:{}\n", TOTAL - consumedCount.load()); + + auto r = (q.*Dequeue)(); + if (!r) { + continue; + } else { + //std::print("consumer get_counter:{} \n", ++get_counter); + if ((rng() & 0xF) == 0) { + std::this_thread::yield(); + QThread::usleep(10); + } + } + + int x = *r; + + if (x < 0 || x >= TOTAL) { + qFatal("Out-of-range value detected: %d", x); + } + + bool expected = false; + if (!seen[x].compare_exchange_strong(expected, true)) { + qFatal("Duplicate detected: %d", x); + } + + consumedCount++; + } + }; + + // ---- Launch threads ---- + std::vector producers, consumers; + + for (int i = 0; i < THREADS; i++) { + producers.emplace_back(producer, i); + consumers.emplace_back(consumer); + } + + for (auto &t : producers) { + t.join(); + } + + for (auto &t : consumers) { + t.join(); + } + + // ---- Verify all flags true ---- + for (int i = 0; i < TOTAL; i++) { + QVERIFY2(seen[i].load(), "Missing value detected"); + } + + qDebug("correctness test %d OK", i + 1); + } + } + + template + void stressTest() + { + Qu q; + + std::atomic stop{false}; + std::atomic counter{0}; + + auto producer = [&]() { + std::mt19937_64 rng(std::random_device{}()); + while (!stop.load(std::memory_order_acquire)) { + int v = rng(); + (q.*Enqueue)(v); + counter++; + if ((rng() & 0xF) == 0) { + std::this_thread::yield(); + QThread::usleep(10); + } + } + }; + + auto consumer = [&]() { + std::mt19937_64 rng(std::random_device{}()); + while (!stop.load(std::memory_order_acquire)) { + auto r = (q.*Dequeue)(); + if (r) counter++; + + if ((rng() & 0xF) == 0) { + std::this_thread::yield(); + QThread::usleep(10); + } + } + }; + + std::vector threads; + + // 16 producer + 16 consumer + for (int i = 0; i < THREADS; i++) { + threads.emplace_back(producer); + threads.emplace_back(consumer); + } + + constexpr int msecs = 30000; + QTest::qSleep(msecs); // 30 seconds + stop.store(true); + + for (auto &t : threads) { + t.join(); + } + + qDebug("ops %d (%d ops/msec)", counter.load(), counter.load() / msecs); + QVERIFY(counter.load() > msecs * 20); + } + +private slots: + void correctnessTest_FCQueue() + { + correctnessTest, &FCQueue::push, &FCQueue::pop>(); + } + + // void correctnessTest_TLockQueue() + // { + // correctnessTest, &TLockQueue::push, &TLockQueue::pop>(); + // } + + // void correctnessTest_TLockStack() + // { + // correctnessTest, &TLockStack::push, &TLockStack::pop>(); + // } + + void stressTest_FCQueue() + { + stressTest, &FCQueue::push, &FCQueue::pop>(); + } + + void stressTest_TLockQueue() + { + using PushFunc = void (TLockQueue::*)(const int64_t&); + stressTest, static_cast(&TLockQueue::push), &TLockQueue::pop>(); + } + + void stressTest_TLockStack() + { + using PushFunc = void (TLockStack::*)(const int64_t&); + stressTest, static_cast(&TLockStack::push), &TLockStack::pop>(); + } +}; + + +TF_TEST_SQLLESS_MAIN(TestQueue) +#include "main.moc" diff --git a/src/test/queue/main.cpp b/src/test/queue/main.cpp index 015f0d8f1..2489a23dd 100644 --- a/src/test/queue/main.cpp +++ b/src/test/queue/main.cpp @@ -31,10 +31,10 @@ class PopThread : public QThread try { uint64_t lastNum = 0; for (;;) { - uint64_t num; - if (intQueue.dequeue(num)) { + auto num = intQueue.dequeue(); + if (num) { //std::cout << "pop:" << intQueue.count() << std::endl; - QVERIFY(num == lastNum); + QVERIFY(num.value() == lastNum); lastNum++; std::this_thread::yield(); } diff --git a/src/test/test.pri b/src/test/test.pri index 94cd41d76..d7c437fd2 100644 --- a/src/test/test.pri +++ b/src/test/test.pri @@ -6,7 +6,7 @@ QT -= gui DEFINES += TF_DLL # C++ Standards Support -CONFIG += c++20 +CONFIG += c++23 windows:QMAKE_CXXFLAGS += /Zc:__cplusplus /std:c++20 /permissive- include(../../tfbase.pri) @@ -30,5 +30,5 @@ windows { # shared link '-lglog' LIBS += $$system("pkg-config --libs libglog 2>/dev/null") } - linux-*:LIBS += -lrt $$system("pkg-config --libs libunwind 2>/dev/null") + linux-*:LIBS += -lrt -luring $$system("pkg-config --libs libunwind 2>/dev/null") } diff --git a/src/tfcore.h b/src/tfcore.h index 15bba7656..d503cd1d6 100644 --- a/src/tfcore.h +++ b/src/tfcore.h @@ -9,21 +9,21 @@ #include #include -#define TF_EINTR_LOOP(func) \ +#define TF_EINTR_LOOP(func) { \ int ret; \ do { \ errno = 0; \ ret = (func); \ } while (ret < 0 && errno == EINTR); \ - return ret; + return ret; } -#define TF_EAGAIN_LOOP(func) \ +#define TF_EAGAIN_LOOP(func) { \ int ret; \ do { \ errno = 0; \ ret = (func); \ } while (ret < 0 && (errno == EINTR || errno == EAGAIN)); \ - return ret; + return ret; } #ifdef Q_OS_UNIX diff --git a/src/tfcore_unix.h b/src/tfcore_unix.h index 1a03ea7bc..7e341ef1e 100644 --- a/src/tfcore_unix.h +++ b/src/tfcore_unix.h @@ -38,7 +38,7 @@ inline int tf_ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *ts) inline int tf_poll(struct pollfd *fds, nfds_t nfds, int timeout) { - struct timespec ts = {timeout / 1000, (timeout % 1000) * 1000000L}; + struct timespec ts{timeout / 1000, (timeout % 1000) * 1000000L}; return tf_ppoll(fds, nfds, &ts); } diff --git a/src/tfnamespace.h b/src/tfnamespace.h index c8fe259b5..050a625a7 100644 --- a/src/tfnamespace.h +++ b/src/tfnamespace.h @@ -8,12 +8,12 @@ class TWebApplication; */ namespace Tf { -enum QuotedStrSplitBehavior { +enum class QuotedStrSplitBehavior { SplitWhereverSep = 0, SplitSkipQuotedString, }; -enum CaseSensitivity { +enum class CaseSensitivity { CaseInsensitive = Qt::CaseInsensitive, CaseSensitive = Qt::CaseSensitive, }; @@ -31,7 +31,7 @@ enum HttpMethod { Patch, }; -enum HttpStatusCode { +enum StatusCode { // Informational 1xx Continue = 100, SwitchingProtocols = 101, @@ -80,7 +80,7 @@ enum HttpStatusCode { }; // Common options for AJAX -enum AjaxOption { +enum class AjaxOption { Asynchronous = 0, // true or false, default:true ContentType, // default:"application/x-www-form-urlencoded" Encoding, // default:"UTF-8" @@ -93,7 +93,7 @@ enum AjaxOption { SanitizeJSON, // true or false, See Prototype API docs }; -enum AjaxEvent { +enum class AjaxEvent { Create = 100, // Before request is initiated Uninitialized, // Immediately after request is initiated and before loading Loading, // When the remote response is being loaded by the browser @@ -104,7 +104,7 @@ enum AjaxEvent { Complete, // When the XMLHttpRequest is complete (fires after success or failure, if they are present) }; -enum ValidationRule { +enum class ValidationRule { Required = 0, // This value is required. MaxLength, // This value is too long. MinLength, // This value is too short. diff --git a/src/tformvalidator.cpp b/src/tformvalidator.cpp index 573ebb038..33c588daf 100644 --- a/src/tformvalidator.cpp +++ b/src/tformvalidator.cpp @@ -126,23 +126,25 @@ QString TFormValidator::dateTimeFormat() const void TFormValidator::setRule(const QString &key, Tf::ValidationRule rule, bool enable, const QString &errorMessage) { // arg check - switch ((int)rule) { - case Tf::MaxLength: - case Tf::MinLength: - case Tf::IntMax: - case Tf::IntMin: - case Tf::DoubleMax: - case Tf::DoubleMin: + switch (rule) { + case Tf::ValidationRule::MaxLength: + case Tf::ValidationRule::MinLength: + case Tf::ValidationRule::IntMax: + case Tf::ValidationRule::IntMin: + case Tf::ValidationRule::DoubleMax: + case Tf::ValidationRule::DoubleMin: Tf::warn("Validation: Bad rule spedified [key:{} rule:{}]. Use another setRule method.", key, (int)rule); return; - case Tf::Pattern: + case Tf::ValidationRule::Pattern: Tf::warn("Validation: Bad rule spedified [key:{} rule:{}]. Use setPatternRule method.", key, (int)rule); return; + default: + break; } removeRule(key, rule); - rules.prepend(RuleEntry(key, (int)rule, enable, (errorMessage.isEmpty() ? Tf::app()->validationErrorMessage(rule) : errorMessage))); + rules.prepend(RuleEntry(key, (int)rule, enable, (errorMessage.isEmpty() ? Tf::app()->validationErrorMessage((int)rule) : errorMessage))); } /*! @@ -161,23 +163,25 @@ void TFormValidator::setRule(const QString &key, Tf::ValidationRule rule, int va void TFormValidator::setRule(const QString &key, Tf::ValidationRule rule, int64_t val, const QString &errorMessage) { // arg check - switch ((int)rule) { - case Tf::Required: - case Tf::EmailAddress: - case Tf::Url: - case Tf::Date: - case Tf::Time: - case Tf::DateTime: + switch (rule) { + case Tf::ValidationRule::Required: + case Tf::ValidationRule::EmailAddress: + case Tf::ValidationRule::Url: + case Tf::ValidationRule::Date: + case Tf::ValidationRule::Time: + case Tf::ValidationRule::DateTime: Tf::warn("Validation: Bad rule spedified [key:{} rule:{}]. Use another setRule method.", key, (int)rule); return; - case Tf::Pattern: + case Tf::ValidationRule::Pattern: Tf::warn("Validation: Bad rule spedified [key:{} rule:{}]. Use setPatternRule method.", key, (int)rule); return; + default: + break; } removeRule(key, rule); - rules.prepend(RuleEntry(key, (int)rule, val, (errorMessage.isEmpty() ? Tf::app()->validationErrorMessage(rule) : errorMessage))); + rules.prepend(RuleEntry(key, (int)rule, val, (errorMessage.isEmpty() ? Tf::app()->validationErrorMessage((int)rule) : errorMessage))); } /*! @@ -196,27 +200,29 @@ void TFormValidator::setRule(const QString &key, Tf::ValidationRule rule, float void TFormValidator::setRule(const QString &key, Tf::ValidationRule rule, double val, const QString &errorMessage) { // arg check - switch ((int)rule) { - case Tf::Required: - case Tf::MaxLength: - case Tf::MinLength: - case Tf::IntMax: - case Tf::IntMin: - case Tf::EmailAddress: - case Tf::Url: - case Tf::Date: - case Tf::Time: - case Tf::DateTime: + switch (rule) { + case Tf::ValidationRule::Required: + case Tf::ValidationRule::MaxLength: + case Tf::ValidationRule::MinLength: + case Tf::ValidationRule::IntMax: + case Tf::ValidationRule::IntMin: + case Tf::ValidationRule::EmailAddress: + case Tf::ValidationRule::Url: + case Tf::ValidationRule::Date: + case Tf::ValidationRule::Time: + case Tf::ValidationRule::DateTime: Tf::warn("Validation: Bad rule spedified [key:{} rule:{}]. Use another setRule method.", key, (int)rule); return; - case Tf::Pattern: + case Tf::ValidationRule::Pattern: Tf::warn("Validation: Bad rule spedified [key:{} rule:{}]. Use setPatternRule method.", key, (int)rule); return; + default: + break; } removeRule(key, rule); - rules.prepend(RuleEntry(key, (int)rule, val, (errorMessage.isEmpty() ? Tf::app()->validationErrorMessage(rule) : errorMessage))); + rules.prepend(RuleEntry(key, (int)rule, val, (errorMessage.isEmpty() ? Tf::app()->validationErrorMessage((int)rule) : errorMessage))); } /*! @@ -243,8 +249,8 @@ void TFormValidator::setRule(const QString &key, Tf::ValidationRule rule, const */ void TFormValidator::setPatternRule(const QString &key, const QRegularExpression &rx, const QString &errorMessage) { - removeRule(key, Tf::Pattern); - rules.prepend(RuleEntry(key, Tf::Pattern, rx, (errorMessage.isEmpty() ? Tf::app()->validationErrorMessage(Tf::Pattern) : errorMessage))); + removeRule(key, Tf::ValidationRule::Pattern); + rules.prepend(RuleEntry(key, (int)Tf::ValidationRule::Pattern, rx, (errorMessage.isEmpty() ? Tf::app()->validationErrorMessage((int)Tf::ValidationRule::Pattern) : errorMessage))); } /*! @@ -260,10 +266,10 @@ bool TFormValidator::validate(const QVariantMap &map) errors.clear(); // Add default rules, Tf::Required. - QString msg = Tf::app()->validationErrorMessage(Tf::Required); + QString msg = Tf::app()->validationErrorMessage((int)Tf::ValidationRule::Required); for (auto &k : (const QStringList &)map.keys()) { - if (!containsRule(k, Tf::Required)) { - rules.append(RuleEntry(k, (int)Tf::Required, true, msg)); + if (!containsRule(k, Tf::ValidationRule::Required)) { + rules.append(RuleEntry(k, (int)Tf::ValidationRule::Required, true, msg)); } } @@ -272,7 +278,7 @@ bool TFormValidator::validate(const QVariantMap &map) if (str.isEmpty()) { bool req = r.value.toBool(); - if (r.rule == Tf::Required && req) { + if (r.rule == (int)Tf::ValidationRule::Required && req) { tSystemDebug("validation error: required parameter is empty, key:{}", r.key); errors << qMakePair(r.key, r.rule); } @@ -280,10 +286,10 @@ bool TFormValidator::validate(const QVariantMap &map) bool ok1, ok2; tSystemDebug("validating key:{} value: {}", r.key, str); switch (r.rule) { - case Tf::Required: + case (int)Tf::ValidationRule::Required: break; - case Tf::MaxLength: { + case (int)Tf::ValidationRule::MaxLength: { int max = r.value.toInt(&ok2); if (!ok2 || str.length() > max) { errors << qMakePair(r.key, r.rule); @@ -291,7 +297,7 @@ bool TFormValidator::validate(const QVariantMap &map) break; } - case Tf::MinLength: { + case (int)Tf::ValidationRule::MinLength: { int min = r.value.toInt(&ok2); if (!ok2 || str.length() < min) { errors << qMakePair(r.key, r.rule); @@ -299,7 +305,7 @@ bool TFormValidator::validate(const QVariantMap &map) break; } - case Tf::IntMax: { + case (int)Tf::ValidationRule::IntMax: { int64_t n = str.toLongLong(&ok1); int64_t max = r.value.toLongLong(&ok2); if (!ok1 || !ok2 || n > max) { @@ -308,7 +314,7 @@ bool TFormValidator::validate(const QVariantMap &map) break; } - case Tf::IntMin: { + case (int)Tf::ValidationRule::IntMin: { int64_t n = str.toLongLong(&ok1); int64_t min = r.value.toLongLong(&ok2); if (!ok1 || !ok2 || n < min) { @@ -317,7 +323,7 @@ bool TFormValidator::validate(const QVariantMap &map) break; } - case Tf::DoubleMax: { + case (int)Tf::ValidationRule::DoubleMax: { double n = str.toDouble(&ok1); double max = r.value.toDouble(&ok2); if (!ok1 || !ok2 || n > max) { @@ -326,7 +332,7 @@ bool TFormValidator::validate(const QVariantMap &map) break; } - case Tf::DoubleMin: { + case (int)Tf::ValidationRule::DoubleMin: { double n = str.toDouble(&ok1); double min = r.value.toDouble(&ok2); if (!ok1 || !ok2 || n < min) { @@ -335,7 +341,7 @@ bool TFormValidator::validate(const QVariantMap &map) break; } - case Tf::EmailAddress: { // refer to RFC5321 + case (int)Tf::ValidationRule::EmailAddress: { // refer to RFC5321 if (r.value.toBool()) { QRegularExpression re("^" ADDR_SPEC "$"); auto match = re.match(str); @@ -346,7 +352,7 @@ bool TFormValidator::validate(const QVariantMap &map) break; } - case Tf::Url: { + case (int)Tf::ValidationRule::Url: { if (r.value.toBool()) { QUrl url(str, QUrl::StrictMode); if (!url.isValid()) { @@ -356,7 +362,7 @@ bool TFormValidator::validate(const QVariantMap &map) break; } - case Tf::Date: { + case (int)Tf::ValidationRule::Date: { if (r.value.toBool()) { QDate date = QLocale().toDate(str, dateFormat()); if (!date.isValid()) { @@ -367,7 +373,7 @@ bool TFormValidator::validate(const QVariantMap &map) break; } - case Tf::Time: { + case (int)Tf::ValidationRule::Time: { if (r.value.toBool()) { QTime time = QLocale().toTime(str, timeFormat()); if (!time.isValid()) { @@ -378,7 +384,7 @@ bool TFormValidator::validate(const QVariantMap &map) break; } - case Tf::DateTime: { + case (int)Tf::ValidationRule::DateTime: { if (r.value.toBool()) { QDateTime dt = QLocale().toDateTime(str, dateTimeFormat()); if (!dt.isValid()) { @@ -389,7 +395,7 @@ bool TFormValidator::validate(const QVariantMap &map) break; } - case Tf::Pattern: { + case (int)Tf::ValidationRule::Pattern: { QRegularExpression rx = r.value.toRegularExpression(); auto match = rx.match(str); if (!rx.isValid() || !(match.hasMatch() && !match.hasPartialMatch())) { @@ -453,7 +459,7 @@ Tf::ValidationRule TFormValidator::errorRule(const QString &key) const return (Tf::ValidationRule)err.second; } } - return Tf::Required; + return Tf::ValidationRule::Required; } /*! @@ -462,7 +468,7 @@ Tf::ValidationRule TFormValidator::errorRule(const QString &key) const QString TFormValidator::message(const QString &key, Tf::ValidationRule rule) const { for (auto &r : rules) { - if (r.key == key && r.rule == rule) { + if (r.key == key && r.rule == (int)rule) { return r.message; } } @@ -508,7 +514,7 @@ QString TFormValidator::value(const QString &key, const QString &defaultValue) c bool TFormValidator::containsRule(const QString &key, Tf::ValidationRule rule) const { for (auto &r : rules) { - if (r.key == key && r.rule == rule) { + if (r.key == key && r.rule == (int)rule) { return true; } } @@ -523,7 +529,7 @@ void TFormValidator::removeRule(const QString &key, Tf::ValidationRule rule) { for (QMutableListIterator i(rules); i.hasNext();) { const RuleEntry &r = i.next(); - if (r.key == key && r.rule == rule) { + if (r.key == key && r.rule == (int)rule) { i.remove(); } } @@ -535,8 +541,8 @@ void TFormValidator::removeRule(const QString &key, Tf::ValidationRule rule) */ void TFormValidator::setValidationError(const QString &key, const QString &errorMessage) { - errors << qMakePair(key, (int)Tf::Custom); - rules.append(RuleEntry(key, Tf::Custom, "dummy", errorMessage)); + errors << qMakePair(key, (int)Tf::ValidationRule::Custom); + rules.append(RuleEntry(key, (int)Tf::ValidationRule::Custom, "dummy", errorMessage)); } /*! diff --git a/src/tglobal.cpp b/src/tglobal.cpp index 366fc4b50..7e8a44f7d 100644 --- a/src/tglobal.cpp +++ b/src/tglobal.cpp @@ -21,6 +21,8 @@ #ifdef Q_OS_LINUX #include #include +#include "turingserver.h" +#include "tactioncontextroutine.h" #endif #ifdef Q_OS_WIN #define NOMINMAX @@ -121,19 +123,34 @@ TCache *Tf::cache() noexcept TAbstractController *Tf::currentController() { - if (Tf::app()->multiProcessingModule() == TWebApplication::Thread) { + switch (Tf::app()->multiProcessingModule()) { + case TWebApplication::MultiProcessingModule::Thread: { TActionThread *context = dynamic_cast(QThread::currentThread()); if (Q_LIKELY(context)) { return context->currentController(); } - } + break; } - if (Tf::app()->multiProcessingModule() == TWebApplication::Epoll) { + case TWebApplication::MultiProcessingModule::Epoll: #ifdef Q_OS_LINUX return TMultiplexingServer::instance()->currentController(); #else tFatal("Unsupported MPM: epoll"); #endif + break; + + case TWebApplication::MultiProcessingModule::Uring: { +#ifdef Q_OS_LINUX + if (auto context = TActionContext::currentActionContext(); context) { + return context->currentController(); + } +#else + tFatal("Unsupported MPM: uring"); +#endif + break; } + + default: + break; } throw RuntimeException("Can not cast the current thread", __FILE__, __LINE__); @@ -142,31 +159,47 @@ TAbstractController *Tf::currentController() TDatabaseContext *Tf::currentDatabaseContext() { - TDatabaseContext *context; + switch (Tf::app()->multiProcessingModule()) { + case TWebApplication::MultiProcessingModule::Thread: + if (auto context = TDatabaseContext::currentDatabaseContext(); context) { + return context; + } - if (Tf::app()->multiProcessingModule() == TWebApplication::Epoll) { -#ifdef Q_OS_LINUX - context = TMultiplexingServer::instance()->currentWorker(); - if (context) { + if (auto context = dynamic_cast(QThread::currentThread()); context) { return context; } + break; - context = Tf::app()->mainDatabaseContext(); - if (context) { + case TWebApplication::MultiProcessingModule::Epoll: +#ifdef Q_OS_LINUX + if (auto context = TMultiplexingServer::instance()->currentWorker(); context) { return context; } #else tFatal("Unsupported MPM: epoll"); #endif - } + break; - context = TDatabaseContext::currentDatabaseContext(); - if (context) { - return context; + case TWebApplication::MultiProcessingModule::Uring: +#ifdef Q_OS_LINUX + if (auto context = dynamic_cast(TActionContext::currentActionContext()); context) { + return context; + } + + if (auto context = TDatabaseContext::currentDatabaseContext(); context) { + return context; + } +#else + tFatal("Unsupported MPM: uring"); +#endif + break; + + default: + tFatal("Unsupported MPM"); + break; } - context = dynamic_cast(QThread::currentThread()); - if (context) { + if (auto context = Tf::app()->mainDatabaseContext(); context) { return context; } @@ -174,7 +207,7 @@ TDatabaseContext *Tf::currentDatabaseContext() } -QSqlDatabase &Tf::currentSqlDatabase(int id) noexcept +TSqlDatabase &Tf::currentSqlDatabase(int id) noexcept { return currentDatabaseContext()->getSqlDatabase(id); } diff --git a/src/tglobal.h b/src/tglobal.h index b0fc94f43..7734fc2f0 100644 --- a/src/tglobal.h +++ b/src/tglobal.h @@ -216,7 +216,7 @@ class TAbstractActionContext; class TAppSettings; class TDatabaseContext; class TCache; -class QSqlDatabase; +class TSqlDatabase; namespace Tf { T_CORE_EXPORT TWebApplication *app() noexcept; @@ -235,8 +235,9 @@ T_CORE_EXPORT TCache *cache() noexcept; T_CORE_EXPORT TAbstractController *currentController(); inline const TAbstractController *constCurrentController() { return currentController(); } T_CORE_EXPORT TDatabaseContext *currentDatabaseContext(); -T_CORE_EXPORT QSqlDatabase ¤tSqlDatabase(int id) noexcept; +T_CORE_EXPORT TSqlDatabase ¤tSqlDatabase(int id) noexcept; T_CORE_EXPORT QMap> *objectFactories() noexcept; +//T_CORE_EXPORT std::map> *objectFactories() noexcept; // LZ4 lossless compression algorithm T_CORE_EXPORT QByteArray lz4Compress(const char *data, int nbytes, int compressionLevel = 1) noexcept; @@ -249,6 +250,16 @@ inline bool strcmp(const QByteArray &str1, const QByteArray &str2) return str1.length() == str2.length() && !std::strncmp(str1.data(), str2.data(), str1.length()); } +// Scope-Exit Function Class +template +class ScopeExitFunction { +public: + explicit ScopeExitFunction(Func&& func) : _func(std::move(func)) {} + ~ScopeExitFunction() noexcept { _func(); } +private: + Func _func; +}; + #ifdef TF_HAVE_STD_FORMAT // std::format // Logging for developer diff --git a/src/thazardobject.h b/src/thazardobject.h index 9f315723c..54f3a2733 100644 --- a/src/thazardobject.h +++ b/src/thazardobject.h @@ -7,7 +7,7 @@ class T_CORE_EXPORT THazardObject { public: THazardObject(); THazardObject(const THazardObject &); - THazardObject(THazardObject &&) { } + THazardObject(THazardObject &&) = default; virtual ~THazardObject() { } void deleteLater(); @@ -21,4 +21,3 @@ class T_CORE_EXPORT THazardObject { friend class THazardPtrManager; friend class THazardRemoverThread; }; - diff --git a/src/thazardptr.cpp b/src/thazardptr.cpp index c188737a3..d4b2957dd 100644 --- a/src/thazardptr.cpp +++ b/src/thazardptr.cpp @@ -3,7 +3,7 @@ THazardPtr::THazardPtr() : - rec(new THazardPtrRecord()) + rec(new THazardPtrRecord{}) { THazardPtrManager::instance().push(rec); THazardPtrManager::instance().gc(); diff --git a/src/thazardptrmanager.cpp b/src/thazardptrmanager.cpp index eb2725520..dd06ef527 100644 --- a/src/thazardptrmanager.cpp +++ b/src/thazardptrmanager.cpp @@ -68,7 +68,7 @@ void THazardRemoverThread::run() THazardPtrManager::THazardPtrManager() : - removerThread(new THazardRemoverThread()) + removerThread(new THazardRemoverThread{}) { } diff --git a/src/thttpheader.cpp b/src/thttpheader.cpp index af47bb7d7..54f7d0156 100644 --- a/src/thttpheader.cpp +++ b/src/thttpheader.cpp @@ -24,12 +24,12 @@ THttpHeader::THttpHeader() : /*! Copy constructor. */ -THttpHeader::THttpHeader(const THttpHeader &other) : - TInternetMessageHeader(*static_cast(&other)), - _majorVersion(other._majorVersion), - _minorVersion(other._minorVersion) -{ -} +// THttpHeader::THttpHeader(const THttpHeader &other) : +// TInternetMessageHeader(*static_cast(&other)), +// _majorVersion(other._majorVersion), +// _minorVersion(other._minorVersion) +// { +// } /*! Constructs an HTTP header by parsing \a str. @@ -44,13 +44,13 @@ THttpHeader::THttpHeader(const QByteArray &str) : Assigns \a other to this HTTP header and returns a reference to this HTTP header. */ -THttpHeader &THttpHeader::operator=(const THttpHeader &other) -{ - TInternetMessageHeader::operator=(*static_cast(&other)); - _majorVersion = other._majorVersion; - _minorVersion = other._minorVersion; - return *this; -} +// THttpHeader &THttpHeader::operator=(const THttpHeader &other) +// { +// TInternetMessageHeader::operator=(*static_cast(&other)); +// _majorVersion = other._majorVersion; +// _minorVersion = other._minorVersion; +// return *this; +// } /*! Returns a byte array representation of the HTTP header. @@ -85,20 +85,20 @@ QByteArray THttpHeader::toByteArray() const /*! Constructor. */ -THttpRequestHeader::THttpRequestHeader() : - THttpHeader() -{ -} +// THttpRequestHeader::THttpRequestHeader() : +// THttpHeader() +// { +// } /*! Copy constructor. */ -THttpRequestHeader::THttpRequestHeader(const THttpRequestHeader &other) : - THttpHeader(*static_cast(&other)), - _reqMethod(other._reqMethod), - _reqUri(other._reqUri) -{ -} +// THttpRequestHeader::THttpRequestHeader(const THttpRequestHeader &other) : +// THttpHeader(*static_cast(&other)), +// _reqMethod(other._reqMethod), +// _reqUri(other._reqUri) +// { +// } /*! Constructs an HTTP request header by parsing \a str. @@ -197,13 +197,13 @@ QByteArray THttpRequestHeader::toByteArray() const Assigns \a other to this HTTP request header and returns a reference to this header. */ -THttpRequestHeader &THttpRequestHeader::operator=(const THttpRequestHeader &other) -{ - THttpHeader::operator=(*static_cast(&other)); - _reqMethod = other._reqMethod; - _reqUri = other._reqUri; - return *this; -} +// THttpRequestHeader &THttpRequestHeader::operator=(const THttpRequestHeader &other) +// { +// THttpHeader::operator=(*static_cast(&other)); +// _reqMethod = other._reqMethod; +// _reqUri = other._reqUri; +// return *this; +// } /*! \fn const QByteArray &THttpRequestHeader::method() const @@ -225,20 +225,20 @@ THttpRequestHeader &THttpRequestHeader::operator=(const THttpRequestHeader &othe /*! Constructor. */ -THttpResponseHeader::THttpResponseHeader() : - THttpHeader() -{ -} +// THttpResponseHeader::THttpResponseHeader() : +// THttpHeader() +// { +// } /*! Copy constructor. */ -THttpResponseHeader::THttpResponseHeader(const THttpResponseHeader &other) : - THttpHeader(*static_cast(&other)), - _statusCode(other._statusCode), - _reasonPhrase(other._reasonPhrase) -{ -} +// THttpResponseHeader::THttpResponseHeader(const THttpResponseHeader &other) : +// THttpHeader(*static_cast(&other)), +// _statusCode(other._statusCode), +// _reasonPhrase(other._reasonPhrase) +// { +// } /*! Constructs an HTTP response header by parsing \a str. @@ -271,9 +271,9 @@ THttpResponseHeader::THttpResponseHeader(const QByteArray &str) : Sets the status code to \a code, the reason phrase to \a text and the protocol-version to \a majorVer and \a minorVer. */ -void THttpResponseHeader::setStatusLine(int code, const QByteArray &text, int majorVer, int minorVer) +void THttpResponseHeader::setStatusLine(Tf::StatusCode code, const QByteArray &text, int majorVer, int minorVer) { - _statusCode = code; + _statusCode = (int)code; _reasonPhrase = text; THttpHeader::_majorVersion = majorVer; THttpHeader::_minorVersion = minorVer; @@ -305,13 +305,13 @@ QByteArray THttpResponseHeader::toByteArray() const Assigns \a other to this HTTP response header and returns a reference to this header. */ -THttpResponseHeader &THttpResponseHeader::operator=(const THttpResponseHeader &other) -{ - THttpHeader::operator=(*static_cast(&other)); - _statusCode = other._statusCode; - _reasonPhrase = other._reasonPhrase; - return *this; -} +// THttpResponseHeader &THttpResponseHeader::operator=(const THttpResponseHeader &other) +// { +// THttpHeader::operator=(*static_cast(&other)); +// _statusCode = other._statusCode; +// _reasonPhrase = other._reasonPhrase; +// return *this; +// } void THttpResponseHeader::clear() diff --git a/src/thttpheader.h b/src/thttpheader.h index cafb75ca4..6aed90159 100644 --- a/src/thttpheader.h +++ b/src/thttpheader.h @@ -1,16 +1,19 @@ #pragma once #include #include +#include class T_CORE_EXPORT THttpHeader : public TInternetMessageHeader { public: THttpHeader(); - THttpHeader(const THttpHeader &other); - THttpHeader(const QByteArray &str); - virtual ~THttpHeader() { } + explicit THttpHeader(const QByteArray &str); + THttpHeader(const THttpHeader &other) = default; + THttpHeader &operator=(const THttpHeader &other) = default; + THttpHeader(THttpHeader &&) = default; + THttpHeader &operator=(THttpHeader &&) = default; + virtual ~THttpHeader() = default; - THttpHeader &operator=(const THttpHeader &other); virtual QByteArray toByteArray() const; virtual int majorVersion() const { return _majorVersion; } virtual int minorVersion() const { return _minorVersion; } @@ -23,10 +26,12 @@ class T_CORE_EXPORT THttpHeader : public TInternetMessageHeader { class T_CORE_EXPORT THttpRequestHeader : public THttpHeader { public: - THttpRequestHeader(); - THttpRequestHeader(const THttpRequestHeader &other); - THttpRequestHeader(const QByteArray &str); - THttpRequestHeader &operator=(const THttpRequestHeader &other); + THttpRequestHeader() = default; + explicit THttpRequestHeader(const QByteArray &str); + THttpRequestHeader(const THttpRequestHeader &other) = default; + THttpRequestHeader &operator=(const THttpRequestHeader &other) = default; + THttpRequestHeader(THttpRequestHeader &&) = default; + THttpRequestHeader &operator=(THttpRequestHeader &&) = default; const QByteArray &method() const { return _reqMethod; } const QByteArray &path() const { return _reqUri; } @@ -34,6 +39,7 @@ class T_CORE_EXPORT THttpRequestHeader : public THttpHeader { QByteArray cookie(const QString &name) const; QList cookies() const; virtual QByteArray toByteArray() const; + inline bool isEmpty() const { return THttpHeader::isEmpty() && _reqUri.isEmpty(); } private: QByteArray _reqMethod; @@ -43,14 +49,16 @@ class T_CORE_EXPORT THttpRequestHeader : public THttpHeader { class T_CORE_EXPORT THttpResponseHeader : public THttpHeader { public: - THttpResponseHeader(); - THttpResponseHeader(const THttpResponseHeader &other); - THttpResponseHeader(const QByteArray &str); + THttpResponseHeader() = default; + explicit THttpResponseHeader(const QByteArray &str); + THttpResponseHeader(const THttpResponseHeader &) = default; + THttpResponseHeader &operator=(const THttpResponseHeader &) = default; + THttpResponseHeader(THttpResponseHeader &&) = default; + THttpResponseHeader &operator=(THttpResponseHeader &&) = default; - int statusCode() const { return _statusCode; } - void setStatusLine(int code, const QByteArray &text = QByteArray(), int majorVer = 1, int minorVer = 1); + Tf::StatusCode statusCode() const { return static_cast(_statusCode); } + void setStatusLine(Tf::StatusCode code, const QByteArray &text = QByteArray(), int majorVer = 1, int minorVer = 1); virtual QByteArray toByteArray() const; - THttpResponseHeader &operator=(const THttpResponseHeader &other); void clear(); private: diff --git a/src/thttprequest.cpp b/src/thttprequest.cpp index 780ed49a9..1f1a949c6 100644 --- a/src/thttprequest.cpp +++ b/src/thttprequest.cpp @@ -18,15 +18,15 @@ const QMap methodHash = { - {"get", Tf::Get}, - {"head", Tf::Head}, - {"post", Tf::Post}, - {"options", Tf::Options}, - {"put", Tf::Put}, - {"delete", Tf::Delete}, - {"trace", Tf::Trace}, - {"connect", Tf::Connect}, - {"patch", Tf::Patch}, + {"get", Tf::HttpMethod::Get}, + {"head", Tf::HttpMethod::Head}, + {"post", Tf::HttpMethod::Post}, + {"options", Tf::HttpMethod::Options}, + {"put", Tf::HttpMethod::Put}, + {"delete", Tf::HttpMethod::Delete}, + {"trace", Tf::HttpMethod::Trace}, + {"connect", Tf::HttpMethod::Connect}, + {"patch", Tf::HttpMethod::Patch}, }; @@ -42,17 +42,16 @@ static bool httpMethodOverride() \brief The THttpRequestData class is for shared THttpRequest data objects. */ -THttpRequestData::THttpRequestData(const THttpRequestData &other) : - QSharedData(other), - header(other.header), - bodyArray(other.bodyArray), - queryItems(other.queryItems), - formItems(other.formItems), - multipartFormData(other.multipartFormData), - jsonData(other.jsonData), - clientAddress(other.clientAddress) -{ -} +// THttpRequestData::THttpRequestData(const THttpRequestData &other) : +// header(other.header), +// bodyArray(other.bodyArray), +// queryItems(other.queryItems), +// formItems(other.formItems), +// multipartFormData(other.multipartFormData), +// jsonData(other.jsonData), +// clientAddress(other.clientAddress) +// { +// } /*! \class THttpRequest @@ -64,29 +63,21 @@ THttpRequestData::THttpRequestData(const THttpRequestData &other) : Constructor. */ THttpRequest::THttpRequest() : - d(new THttpRequestData) -{ -} - -/*! - \fn THttpRequest::THttpRequest(const THttpRequest &other) - Copy constructor. -*/ -THttpRequest::THttpRequest(const THttpRequest &other) : - d(other.d) -{ -} + d(std::make_unique()) +{} /*! Constructor with the header \a header and the body \a body. */ THttpRequest::THttpRequest(const THttpRequestHeader &header, const QByteArray &body, const QHostAddress &clientAddress, TActionContext *context) : - d(new THttpRequestData) + d(std::make_unique()) { d->header = header; d->clientAddress = clientAddress; - d->bodyArray = body; - parseBody(d->bodyArray, d->header, context); + if (!body.isEmpty()) { + bodyArray() = body; + } + parseBody(body, d->header, context); } /*! @@ -94,49 +85,23 @@ THttpRequest::THttpRequest(const THttpRequestHeader &header, const QByteArray &b reading the file \a filePath. */ THttpRequest::THttpRequest(const QByteArray &header, const QString &filePath, const QHostAddress &clientAddress, TActionContext *context) : - d(new THttpRequestData) + d(std::make_unique()) { - d->header = THttpRequestHeader(header); + d->header = std::move(THttpRequestHeader{header}); d->clientAddress = clientAddress; if (d->header.contentType().trimmed().toLower().startsWith(QByteArrayLiteral("multipart/form-data"))) { - d->multipartFormData = TMultipartFormData(filePath, boundary(), context); - d->formItems = d->multipartFormData.postParameters; + multipartFormData() = TMultipartFormData(filePath, boundary(), context); + formItemList() = multipartFormData().postParameters; } else { QFile file(filePath); if (file.open(QIODevice::ReadOnly)) { - d->bodyArray = file.readAll(); - parseBody(d->bodyArray, d->header, context); + bodyArray() = file.readAll(); + parseBody(bodyArray(), d->header, context); } } } -/*! - Destructor. -*/ -THttpRequest::~THttpRequest() -{ - if (bodyDevice) { - bodyDevice->close(); - delete bodyDevice; - } -} - -/*! - Assignment operator. -*/ -THttpRequest &THttpRequest::operator=(const THttpRequest &other) -{ - if (bodyDevice) { - bodyDevice->close(); - delete bodyDevice; - bodyDevice = nullptr; - } - - d = other.d; - return *this; -} - /*! Returns the method of an HTTP request, which can be overridden by another value, a query parameter named '_method' or @@ -146,15 +111,15 @@ THttpRequest &THttpRequest::operator=(const THttpRequest &other) */ Tf::HttpMethod THttpRequest::method() const { - Tf::HttpMethod method = Tf::Invalid; + Tf::HttpMethod method = Tf::HttpMethod::Invalid; if (httpMethodOverride()) { method = queryItemMethod(); // query parameter named '_method' - if (method == Tf::Invalid) { + if (method == Tf::HttpMethod::Invalid) { method = getHttpMethodOverride(); // X-HTTP-* methods override } } - if (method == Tf::Invalid) { + if (method == Tf::HttpMethod::Invalid) { method = realMethod(); } return method; @@ -167,7 +132,7 @@ Tf::HttpMethod THttpRequest::method() const Tf::HttpMethod THttpRequest::realMethod() const { QString s = d->header.method().toLower(); - return methodHash.value(s, Tf::Invalid); + return methodHash.value(s, Tf::HttpMethod::Invalid); } /*! @@ -177,19 +142,19 @@ Tf::HttpMethod THttpRequest::getHttpMethodOverride() const { Tf::HttpMethod method; QString str = d->header.rawHeader(QByteArrayLiteral("X-HTTP-Method-Override")).toLower(); - method = methodHash.value(str, Tf::Invalid); - if (method != Tf::Invalid) { + method = methodHash.value(str, Tf::HttpMethod::Invalid); + if (method != Tf::HttpMethod::Invalid) { return method; } str = d->header.rawHeader(QByteArrayLiteral("X-HTTP-Method")).toLower(); - method = methodHash.value(str, Tf::Invalid); - if (method != Tf::Invalid) { + if (method != Tf::HttpMethod::Invalid) { + method = methodHash.value(str, Tf::HttpMethod::Invalid); return method; } str = d->header.rawHeader(QByteArrayLiteral("X-METHOD-OVERRIDE")).toLower(); - method = methodHash.value(str, Tf::Invalid); + method = methodHash.value(str, Tf::HttpMethod::Invalid); return method; } @@ -200,7 +165,7 @@ Tf::HttpMethod THttpRequest::getHttpMethodOverride() const Tf::HttpMethod THttpRequest::queryItemMethod() const { QString queryMethod = queryItemValue(QStringLiteral("_method")); - return methodHash.value(queryMethod, Tf::Invalid); + return methodHash.value(queryMethod, Tf::HttpMethod::Invalid); } @@ -233,7 +198,7 @@ bool THttpRequest::hasItem(const QString &name, const QListqueryItems); + return hasItem(name, queryItemList()); } /*! @@ -263,7 +228,7 @@ QString THttpRequest::itemValue(const QString &name, const QString &defaultValue */ QString THttpRequest::queryItemValue(const QString &name, const QString &defaultValue) const { - return itemValue(name, defaultValue, d->queryItems); + return itemValue(name, defaultValue, queryItemList()); } @@ -285,7 +250,7 @@ QStringList THttpRequest::allItemValues(const QString &name, const QListqueryItems); + return allItemValues(name, queryItemList()); } /*! @@ -308,7 +273,7 @@ QStringList THttpRequest::queryItemList(const QString &key) const */ QVariantList THttpRequest::queryItemVariantList(const QString &key) const { - return itemVariantList(key, d->queryItems); + return itemVariantList(key, queryItemList()); } /*! @@ -317,7 +282,7 @@ QVariantList THttpRequest::queryItemVariantList(const QString &key) const */ QVariantMap THttpRequest::queryItems(const QString &key) const { - return itemMap(key, d->queryItems); + return itemMap(key, queryItemList()); } @@ -336,7 +301,7 @@ QVariantMap THttpRequest::itemMap(const QList> &items) */ QVariantMap THttpRequest::queryItems() const { - return itemMap(d->queryItems); + return itemMap(queryItemList()); } /*! @@ -352,7 +317,7 @@ QVariantMap THttpRequest::queryItems() const */ bool THttpRequest::hasFormItem(const QString &name) const { - return hasItem(name, d->formItems); + return hasItem(name, formItemList()); } /*! @@ -371,7 +336,7 @@ QString THttpRequest::formItemValue(const QString &name) const */ QString THttpRequest::formItemValue(const QString &name, const QString &defaultValue) const { - return itemValue(name, defaultValue, d->formItems); + return itemValue(name, defaultValue, formItemList()); } /*! @@ -381,7 +346,7 @@ QString THttpRequest::formItemValue(const QString &name, const QString &defaultV */ QStringList THttpRequest::allFormItemValues(const QString &name) const { - return allItemValues(name, d->formItems); + return allItemValues(name, formItemList()); } /*! @@ -425,7 +390,7 @@ QVariantList THttpRequest::itemVariantList(const QString &key, const QListformItems); + return itemVariantList(key, formItemList()); } @@ -473,7 +438,7 @@ QVariantMap THttpRequest::itemMap(const QString &key, const QListformItems); + return itemMap(key, formItemList()); } /*! @@ -481,44 +446,44 @@ QVariantMap THttpRequest::formItems(const QString &key) const */ QVariantMap THttpRequest::formItems() const { - return itemMap(d->formItems); + return itemMap(formItemList()); } void THttpRequest::parseBody(const QByteArray &body, const THttpRequestHeader &header, TActionContext *context) { switch (method()) { - case Tf::Post: - case Tf::Put: - case Tf::Patch: { + case Tf::HttpMethod::Post: + case Tf::HttpMethod::Put: + case Tf::HttpMethod::Patch: { QString ctype = QString::fromLatin1(header.contentType().trimmed()); if (ctype.startsWith(QLatin1String("application/x-www-form-urlencoded"), Qt::CaseInsensitive)) { if (!body.isEmpty()) { - d->formItems = THttpUtility::fromFormUrlEncoded(body); + formItemList() = THttpUtility::fromFormUrlEncoded(body); } } else if (ctype.startsWith(QLatin1String("application/json"), Qt::CaseInsensitive)) { QJsonParseError error; - d->jsonData = QJsonDocument::fromJson(body, &error); + jsonData() = QJsonDocument::fromJson(body, &error); if (error.error != QJsonParseError::NoError) { tSystemWarn("Json data: {}\n error: {}\n at: {}", body.data(), error.errorString(), error.offset); } } else if (ctype.startsWith(QLatin1String("multipart/form-data"), Qt::CaseInsensitive)) { // multipart/form-data - d->multipartFormData = TMultipartFormData(body, boundary(), context); - d->formItems = d->multipartFormData.postParameters; + multipartFormData() = TMultipartFormData(body, boundary(), context); + formItemList() = multipartFormData().postParameters; } else { tSystemWarn("unsupported content-type: {}", ctype); } } /* FALLTHRU */ - case Tf::Get: { + case Tf::HttpMethod::Get: { // query parameter QByteArrayList data = header.path().split('?'); QString query = QString::fromLatin1(data.value(1)); if (!query.isEmpty()) { - d->queryItems = THttpRequest::fromQuery(query); + queryItemList() = THttpRequest::fromQuery(query); } break; } @@ -583,29 +548,30 @@ QList THttpRequest::cookies() const */ QVariantMap THttpRequest::allParameters() const { - auto params = d->queryItems; - params << d->formItems; + auto params = queryItemList(); + params << formItemList(); return itemMap(params); } -QList THttpRequest::generate(QByteArray &byteArray, const QHostAddress &address, TActionContext *context) +THttpRequest THttpRequest::generate(QByteArray &byteArray, const QHostAddress &address, TActionContext *context) { - QList reqList; + THttpRequest request; int from = 0; - int headidx; - while ((headidx = byteArray.indexOf(Tf::CRLFCRLF, from)) > 0) { + if (int headidx = byteArray.indexOf(Tf::CRLFCRLF, from); headidx > 0) { headidx += 4; THttpRequestHeader header(byteArray.mid(from)); int contlen = header.contentLength(); if (contlen <= 0) { - reqList << THttpRequest(header, QByteArray(), address, context); + request = THttpRequest(header, QByteArray(), address, context); } else { - reqList << THttpRequest(header, byteArray.mid(headidx, contlen), address, context); + request = THttpRequest(header, byteArray.mid(headidx, contlen), address, context); } from = headidx + contlen; + } else { + return request; } if (from >= byteArray.length()) { @@ -613,7 +579,7 @@ QList THttpRequest::generate(QByteArray &byteArray, const QHostAdd } else { byteArray.remove(0, from); } - return reqList; + return request; } /*! @@ -668,13 +634,102 @@ QHostAddress THttpRequest::originatingClientAddress() const QIODevice *THttpRequest::rawBody() { if (!bodyDevice) { - if (!d->multipartFormData.bodyFile.isEmpty()) { - bodyDevice = new QFile(d->multipartFormData.bodyFile); + if (d->multipartFormData && !multipartFormData().bodyFile.isEmpty()) { + auto p = new QFile(multipartFormData().bodyFile); + bodyDevice.reset(p); } else { - bodyDevice = new QBuffer(&d->bodyArray); + auto p = new QBuffer(d->bodyArray.get()); + bodyDevice.reset(p); } } - return bodyDevice; + return bodyDevice.get(); +} + + +QJsonDocument &THttpRequest::jsonData() +{ + if (!d->jsonData) { + d->jsonData.reset(new QJsonDocument{}); + } + return *(d->jsonData); +} + + +const QJsonDocument &THttpRequest::jsonData() const +{ + if (!d->jsonData) { + d->jsonData.reset(new QJsonDocument{}); + } + return *(d->jsonData); +} + + +TMultipartFormData &THttpRequest::multipartFormData() +{ + if (!d->multipartFormData) { + d->multipartFormData.reset(new TMultipartFormData{}); + } + return *(d->multipartFormData); +} + + +const QByteArray &THttpRequest::bodyArray() const +{ + static const QByteArray dummy; + + if (!d->bodyArray) { + return dummy; + } + return *(d->bodyArray); +} + + +QByteArray &THttpRequest::bodyArray() +{ + if (!d->bodyArray) { + d->bodyArray.reset(new QByteArray{}); + } + return *(d->bodyArray); +} + + +const QList> &THttpRequest::queryItemList() const +{ + static const QList> dummy; + + if (!d->queryItemList) { + return dummy; + } + return *(d->queryItemList); +} + + +QList> &THttpRequest::queryItemList() +{ + if (!d->queryItemList) { + d->queryItemList.reset(new QList>{}); + } + return *(d->queryItemList); +} + + +const QList> &THttpRequest::formItemList() const +{ + static const QList> dummy; + + if (!d->formItemList) { + return dummy; + } + return *(d->formItemList); +} + + +QList> &THttpRequest::formItemList() +{ + if (!d->formItemList) { + d->formItemList.reset(new QList>{}); + } + return *(d->formItemList); } diff --git a/src/thttprequest.h b/src/thttprequest.h index 65e2a13ee..73a011ab6 100644 --- a/src/thttprequest.h +++ b/src/thttprequest.h @@ -1,4 +1,8 @@ #pragma once +#include +#include +#include +#include #include #include #include @@ -6,39 +10,41 @@ #include #include #include -#include -#include -#include -#include +#include class TActionContext; class QIODevice; -class T_CORE_EXPORT THttpRequestData : public QSharedData { +class T_CORE_EXPORT THttpRequestData { public: - THttpRequestData() { } - THttpRequestData(const THttpRequestData &other); - ~THttpRequestData() { } + THttpRequestData() = default; + ~THttpRequestData() = default; + THttpRequestData(THttpRequestData &&) = default; + THttpRequestData &operator=(THttpRequestData &&) = default; THttpRequestHeader header; - QByteArray bodyArray; - QList> queryItems; - QList> formItems; - TMultipartFormData multipartFormData; - QJsonDocument jsonData; + std::unique_ptr bodyArray; + std::unique_ptr>> queryItemList; + std::unique_ptr>> formItemList; + std::unique_ptr multipartFormData; + std::unique_ptr jsonData; QHostAddress clientAddress; + + T_DISABLE_COPY(THttpRequestData) }; class T_CORE_EXPORT THttpRequest { public: THttpRequest(); - THttpRequest(const THttpRequest &other); THttpRequest(const THttpRequestHeader &header, const QByteArray &body, const QHostAddress &clientAddress, TActionContext *context); THttpRequest(const QByteArray &header, const QString &filePath, const QHostAddress &clientAddress, TActionContext *context); - virtual ~THttpRequest(); - THttpRequest &operator=(const THttpRequest &other); + THttpRequest(const THttpRequest &) = delete; + THttpRequest &operator=(const THttpRequest &) = delete; + THttpRequest(THttpRequest &&) = default; + THttpRequest &operator=(THttpRequest &&) = default; + virtual ~THttpRequest() = default; const THttpRequestHeader &header() const { return d->header; } Tf::HttpMethod method() const; @@ -48,7 +54,8 @@ class T_CORE_EXPORT THttpRequest { QString parameter(const QString &name) const; QVariantMap allParameters() const; - bool hasQuery() const { return !d->queryItems.isEmpty(); } + bool isEmpty() const { return d->header.isEmpty(); } + bool hasQuery() const { return d->queryItemList && !d->queryItemList->isEmpty(); } bool hasQueryItem(const QString &name) const; QString queryItemValue(const QString &name) const; QString queryItemValue(const QString &name, const QString &defaultValue) const; @@ -57,7 +64,7 @@ class T_CORE_EXPORT THttpRequest { QVariantList queryItemVariantList(const QString &key) const; QVariantMap queryItems(const QString &key) const; QVariantMap queryItems() const; - bool hasForm() const { return !d->formItems.isEmpty(); } + bool hasForm() const { return d->formItemList && !d->formItemList->isEmpty(); } bool hasFormItem(const QString &name) const; QString formItemValue(const QString &name) const; QString formItemValue(const QString &name, const QString &defaultValue) const; @@ -66,20 +73,27 @@ class T_CORE_EXPORT THttpRequest { QVariantList formItemVariantList(const QString &key) const; QVariantMap formItems(const QString &key) const; QVariantMap formItems() const; - TMultipartFormData &multipartFormData() { return d->multipartFormData; } + TMultipartFormData &multipartFormData(); QByteArray cookie(const QString &name) const; QList cookies() const; QHostAddress clientAddress() const { return d->clientAddress; } QHostAddress originatingClientAddress() const; QIODevice *rawBody(); - bool hasJson() const { return !d->jsonData.isNull(); } - const QJsonDocument &jsonData() const { return d->jsonData; } + bool hasJson() const { return d->jsonData && !d->jsonData->isNull(); } + const QJsonDocument &jsonData() const; - static QList generate(QByteArray &byteArray, const QHostAddress &address, TActionContext *context); + static THttpRequest generate(QByteArray &byteArray, const QHostAddress &address, TActionContext *context); static QList> fromQuery(const QString &query); protected: QByteArray boundary() const; + const QByteArray &bodyArray() const; + QByteArray &bodyArray(); + const QList> &queryItemList() const; + QList> &queryItemList(); + const QList> &formItemList() const; + QList> &formItemList(); + QJsonDocument &jsonData(); static bool hasItem(const QString &name, const QList> &items); static QString itemValue(const QString &name, const QString &defaultValue, const QList> &items); @@ -91,11 +105,10 @@ class T_CORE_EXPORT THttpRequest { private: void parseBody(const QByteArray &body, const THttpRequestHeader &header, TActionContext *context); - QSharedDataPointer d; - QIODevice *bodyDevice {nullptr}; + std::unique_ptr d; + std::unique_ptr bodyDevice; friend class TMultipartFormData; }; Q_DECLARE_METATYPE(THttpRequest) - diff --git a/src/thttpsocket.cpp b/src/thttpsocket.cpp index 5f26a1d13..b244d00fc 100644 --- a/src/thttpsocket.cpp +++ b/src/thttpsocket.cpp @@ -48,22 +48,22 @@ THttpSocket::~THttpSocket() } -QList THttpSocket::read() +THttpRequest THttpSocket::read() { - QList reqList; + THttpRequest request; if (canReadRequest()) { if (_fileBuffer.isOpen()) { _fileBuffer.close(); - reqList << THttpRequest(_headerBuffer, _fileBuffer.fileName(), peerAddress(), _context); + request = THttpRequest(_headerBuffer, _fileBuffer.fileName(), peerAddress(), _context); _headerBuffer.resize(0); } else { - reqList = THttpRequest::generate(_readBuffer, peerAddress(), _context); + request = THttpRequest::generate(_readBuffer, peerAddress(), _context); } _lengthToRead = -1; } - return reqList; + return request; } @@ -221,7 +221,7 @@ bool THttpSocket::waitForReadyReadRequest(int msecs) THttpRequestHeader header(_readBuffer); if (Q_UNLIKELY(systemLimitBodyBytes > 0 && header.contentLength() > systemLimitBodyBytes)) { - throw ClientErrorException(Tf::RequestEntityTooLarge); // Request Entity Too Large + throw ClientErrorException((int)Tf::StatusCode::RequestEntityTooLarge); // Request Entity Too Large } _lengthToRead = std::max(idx + 4 + header.contentLength() - (int64_t)_readBuffer.length(), (int64_t)0); diff --git a/src/thttpsocket.h b/src/thttpsocket.h index f2e7544b5..fc6509a42 100644 --- a/src/thttpsocket.h +++ b/src/thttpsocket.h @@ -16,7 +16,7 @@ class T_CORE_EXPORT THttpSocket : public QObject { THttpSocket(QByteArray &readBuffer, TActionContext *context, QObject *parent = 0); virtual ~THttpSocket(); - QList read(); + THttpRequest read(); bool waitForReadyReadRequest(int msecs = 5000); bool canReadRequest() const; int64_t write(const THttpHeader *header, QIODevice *body); diff --git a/src/thttputility.cpp b/src/thttputility.cpp index 21d383c70..0ba72e747 100644 --- a/src/thttputility.cpp +++ b/src/thttputility.cpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include #if defined(Q_OS_WIN) #include @@ -22,50 +24,50 @@ constexpr auto HTTP_DATE_TIME_FORMAT = "ddd, d MMM yyyy hh:mm:ss"; const QMap reasonPhrase = { // Informational 1xx - {Tf::Continue, "Continue"}, - {Tf::SwitchingProtocols, "Switching Protocols"}, + {(int)Tf::StatusCode::Continue, "Continue"}, + {(int)Tf::StatusCode::SwitchingProtocols, "Switching Protocols"}, // Successful 2xx - {Tf::OK, "OK"}, - {Tf::Created, "Created"}, - {Tf::Accepted, "Accepted"}, - {Tf::NonAuthoritativeInformation, "Non-Authoritative Information"}, - {Tf::NoContent, "No Content"}, - {Tf::ResetContent, "Reset Content"}, - {Tf::PartialContent, "Partial Content"}, + {(int)Tf::StatusCode::OK, "OK"}, + {(int)Tf::StatusCode::Created, "Created"}, + {(int)Tf::StatusCode::Accepted, "Accepted"}, + {(int)Tf::StatusCode::NonAuthoritativeInformation, "Non-Authoritative Information"}, + {(int)Tf::StatusCode::NoContent, "No Content"}, + {(int)Tf::StatusCode::ResetContent, "Reset Content"}, + {(int)Tf::StatusCode::PartialContent, "Partial Content"}, // Redirection 3xx - {Tf::MultipleChoices, "Multiple Choices"}, - {Tf::MovedPermanently, "Moved Permanently"}, - {Tf::Found, "Found"}, - {Tf::SeeOther, "See Other"}, - {Tf::NotModified, "Not Modified"}, - {Tf::UseProxy, "Use Proxy"}, - {Tf::TemporaryRedirect, "Temporary Redirect"}, + {(int)Tf::StatusCode::MultipleChoices, "Multiple Choices"}, + {(int)Tf::StatusCode::MovedPermanently, "Moved Permanently"}, + {(int)Tf::StatusCode::Found, "Found"}, + {(int)Tf::StatusCode::SeeOther, "See Other"}, + {(int)Tf::StatusCode::NotModified, "Not Modified"}, + {(int)Tf::StatusCode::UseProxy, "Use Proxy"}, + {(int)Tf::StatusCode::TemporaryRedirect, "Temporary Redirect"}, // Client Error 4xx - {Tf::BadRequest, "Bad Request"}, - {Tf::Unauthorized, "Unauthorized"}, - {Tf::PaymentRequired, "Payment Required"}, - {Tf::Forbidden, "Forbidden"}, - {Tf::NotFound, "Not Found"}, - {Tf::MethodNotAllowed, "Method Not Allowed"}, - {Tf::NotAcceptable, "Not Acceptable"}, - {Tf::ProxyAuthenticationRequired, "Proxy Authentication Required"}, - {Tf::RequestTimeout, "Request Timeout"}, - {Tf::Conflict, "Conflict"}, - {Tf::Gone, "Gone"}, - {Tf::LengthRequired, "Length Required"}, - {Tf::PreconditionFailed, "Precondition Failed"}, - {Tf::RequestEntityTooLarge, "Request Entity Too Large"}, - {Tf::RequestURITooLong, "Request-URI Too Long"}, - {Tf::UnsupportedMediaType, "Unsupported Media Type"}, - {Tf::RequestedRangeNotSatisfiable, "Requested Range Not Satisfiable"}, - {Tf::ExpectationFailed, "Expectation Failed"}, + {(int)Tf::StatusCode::BadRequest, "Bad Request"}, + {(int)Tf::StatusCode::Unauthorized, "Unauthorized"}, + {(int)Tf::StatusCode::PaymentRequired, "Payment Required"}, + {(int)Tf::StatusCode::Forbidden, "Forbidden"}, + {(int)Tf::StatusCode::NotFound, "Not Found"}, + {(int)Tf::StatusCode::MethodNotAllowed, "Method Not Allowed"}, + {(int)Tf::StatusCode::NotAcceptable, "Not Acceptable"}, + {(int)Tf::StatusCode::ProxyAuthenticationRequired, "Proxy Authentication Required"}, + {(int)Tf::StatusCode::RequestTimeout, "Request Timeout"}, + {(int)Tf::StatusCode::Conflict, "Conflict"}, + {(int)Tf::StatusCode::Gone, "Gone"}, + {(int)Tf::StatusCode::LengthRequired, "Length Required"}, + {(int)Tf::StatusCode::PreconditionFailed, "Precondition Failed"}, + {(int)Tf::StatusCode::RequestEntityTooLarge, "Request Entity Too Large"}, + {(int)Tf::StatusCode::RequestURITooLong, "Request-URI Too Long"}, + {(int)Tf::StatusCode::UnsupportedMediaType, "Unsupported Media Type"}, + {(int)Tf::StatusCode::RequestedRangeNotSatisfiable, "Requested Range Not Satisfiable"}, + {(int)Tf::StatusCode::ExpectationFailed, "Expectation Failed"}, // Server Error 5xx - {Tf::InternalServerError, "Internal Server Error"}, - {Tf::NotImplemented, "Not Implemented"}, - {Tf::BadGateway, "Bad Gateway"}, - {Tf::ServiceUnavailable, "Service Unavailable"}, - {Tf::GatewayTimeout, "Gateway Timeout"}, - {Tf::HTTPVersionNotSupported, "HTTP Version Not Supported"}, + {(int)Tf::StatusCode::InternalServerError, "Internal Server Error"}, + {(int)Tf::StatusCode::NotImplemented, "Not Implemented"}, + {(int)Tf::StatusCode::BadGateway, "Bad Gateway"}, + {(int)Tf::StatusCode::ServiceUnavailable, "Service Unavailable"}, + {(int)Tf::StatusCode::GatewayTimeout, "Gateway Timeout"}, + {(int)Tf::StatusCode::HTTPVersionNotSupported, "HTTP Version Not Supported"}, }; @@ -320,9 +322,9 @@ QString THttpUtility::fromMimeEncoded(const QByteArray &mime) /*! Returns a reason phrase of the HTTP status code \a statusCode. */ -QByteArray THttpUtility::getResponseReasonPhrase(int statusCode) +QByteArray THttpUtility::getResponseReasonPhrase(Tf::StatusCode statusCode) { - return reasonPhrase.value(statusCode); + return reasonPhrase.value((int)statusCode); } /*! @@ -424,6 +426,7 @@ QDateTime THttpUtility::fromHttpDateTimeUTCString(const QByteArray &utc) QByteArray THttpUtility::getUTCTimeString() { +#if 0 static const char *DAY[] = {"Sun, ", "Mon, ", "Tue, ", "Wed, ", "Thu, ", "Fri, ", "Sat, "}; static const char *MONTH[] = {"Jan ", "Feb ", "Mar ", "Apr ", "May ", "Jun ", "Jul ", "Aug ", "Sep ", "Oct ", "Nov ", "Dec "}; @@ -472,4 +475,20 @@ QByteArray THttpUtility::getUTCTimeString() #endif // Q_OS_UNIX return utcTime; +#else + auto now = std::chrono::system_clock::now(); + std::time_t t = std::chrono::system_clock::to_time_t(now); + + std::tm tm{}; +#if defined(Q_OS_WIN) + gmtime_s(&tm, &t); +#else + gmtime_r(&t, &tm); +#endif + QByteArray buf; + buf.reserve(31); + int len = std::strftime(buf.data(), 31, "%a, %d %b %Y %H:%M:%S GMT", &tm); + buf.resize(len); + return buf; +#endif } diff --git a/src/thttputility.h b/src/thttputility.h index b7562a583..35a764633 100644 --- a/src/thttputility.h +++ b/src/thttputility.h @@ -30,7 +30,7 @@ class T_CORE_EXPORT THttpUtility { static QByteArray toMimeEncoded(const QString &input, const QByteArray &encoding = "UTF-8"); static QByteArray toMimeEncoded(const QString &input, QStringConverter::Encoding encoding); static QString fromMimeEncoded(const QByteArray &mime); - static QByteArray getResponseReasonPhrase(int statusCode); + static QByteArray getResponseReasonPhrase(Tf::StatusCode statusCode); static QString trimmedQuotes(const QString &string); static QByteArray timeZone(); static QByteArray toHttpDateTimeString(const QDateTime &dateTime); diff --git a/src/tinternetmessageheader.h b/src/tinternetmessageheader.h index c22a281f4..b4cf647f5 100644 --- a/src/tinternetmessageheader.h +++ b/src/tinternetmessageheader.h @@ -8,10 +8,13 @@ class T_CORE_EXPORT TInternetMessageHeader { public: - TInternetMessageHeader() { } + TInternetMessageHeader() = default; + explicit TInternetMessageHeader(const QByteArray &str); TInternetMessageHeader(const TInternetMessageHeader &other); - TInternetMessageHeader(const QByteArray &str); - virtual ~TInternetMessageHeader() { } + TInternetMessageHeader &operator=(const TInternetMessageHeader &other); + TInternetMessageHeader(TInternetMessageHeader &&) = default; + TInternetMessageHeader &operator=(TInternetMessageHeader &&) = default; + virtual ~TInternetMessageHeader() = default; bool hasRawHeader(const QByteArray &key) const; QByteArray rawHeader(const QByteArray &key) const; @@ -32,7 +35,6 @@ class T_CORE_EXPORT TInternetMessageHeader { void setDate(const QDateTime &dateTime); void setCurrentDate(); virtual QByteArray toByteArray() const; - TInternetMessageHeader &operator=(const TInternetMessageHeader &other); protected: void parse(const QByteArray &header); @@ -42,4 +44,3 @@ class T_CORE_EXPORT TInternetMessageHeader { RawHeaderPairList _headerPairList; mutable int64_t _contentLength {-1}; }; - diff --git a/src/tkvsdatabase.cpp b/src/tkvsdatabase.cpp index d4bb87f3c..86ff9d0be 100644 --- a/src/tkvsdatabase.cpp +++ b/src/tkvsdatabase.cpp @@ -5,6 +5,8 @@ * the New BSD License, which is incorporated herein by reference. */ +#include "tkvsdatabase.h" +#include "tkvsdatabasepool.h" #include "tmongodriver.h" #include "tredisdriver.h" #include "tmemcacheddriver.h" @@ -12,7 +14,6 @@ #include #include #include -#include #include #include @@ -26,7 +27,7 @@ const char *const TKvsDatabase::defaultConnection = "tf_default_connection"; // Map of connection name and database data -class TKvsDatabaseDict : public QMap { +class TKvsDatabaseDict : public QMap { public: mutable QReadWriteLock lock; }; @@ -34,8 +35,8 @@ class TKvsDatabaseDict : public QMap { static TKvsDatabaseDict *databaseDict() { - static TKvsDatabaseDict *dict = new TKvsDatabaseDict; - return dict; + static std::unique_ptr dict{new TKvsDatabaseDict{}}; + return dict.get(); } @@ -68,17 +69,22 @@ static TKvsDriver *createDriver(const QString &driverName) } -TKvsDatabase TKvsDatabase::database(const QString &connectionName) +std::unique_ptr TKvsDatabase::database(const QString &connectionName) { auto *dict = databaseDict(); QReadLocker locker(&dict->lock); - const TKvsDatabaseData &d = (*dict)[connectionName]; - return TKvsDatabase(d.connectionName, d.driver); + if (!dict->contains(connectionName)) { + tSystemWarn("No such KVS database: {}", connectionName); + return std::unique_ptr{new TKvsDatabase{}}; + } + + const TKvsDatabaseSettings &d = (*dict)[connectionName]; + return std::unique_ptr{new TKvsDatabase(d.connectionName, d.driver)}; } -TKvsDatabase TKvsDatabase::addDatabase(const QString &driver, const QString &connectionName) +bool TKvsDatabase::addDatabase(const QString &driver, const QString &connectionName) { auto *dict = databaseDict(); QWriteLocker locker(&dict->lock); @@ -89,11 +95,11 @@ TKvsDatabase TKvsDatabase::addDatabase(const QString &driver, const QString &con delete data.driver; } - TKvsDatabaseData data; + TKvsDatabaseSettings data; data.connectionName = connectionName; data.driver = createDriver(driver); // creates a driver dict->insert(connectionName, data); - return TKvsDatabase(data); + return true; } @@ -105,11 +111,11 @@ void TKvsDatabase::removeDatabase(const QString &connectionName) TKvsDatabase db(dict->take(connectionName)); db.close(); - delete db.drv; + delete db._driver; } -TKvsDatabaseData TKvsDatabase::settings(const QString &connectionName) +TKvsDatabaseSettings TKvsDatabase::settings(const QString &connectionName) { auto *dict = databaseDict(); QReadLocker locker(&dict->lock); @@ -117,32 +123,17 @@ TKvsDatabaseData TKvsDatabase::settings(const QString &connectionName) } -TKvsDatabase::TKvsDatabase(const TKvsDatabase &other) : - connectName(other.connectName), - drv(other.drv) -{ -} - - TKvsDatabase::TKvsDatabase(const QString &connectionName, TKvsDriver *driver) : - connectName(connectionName), - drv(driver) -{ -} - - -TKvsDatabase::TKvsDatabase(const TKvsDatabaseData &data) : - connectName(data.connectionName), - drv(data.driver) + _connectName(connectionName), + _driver(driver) { } -TKvsDatabase &TKvsDatabase::operator=(const TKvsDatabase &other) +TKvsDatabase::TKvsDatabase(const TKvsDatabaseSettings &data) : + _connectName(data.connectionName), + _driver(data.driver) { - connectName = other.connectName; - drv = other.drv; - return *this; } @@ -156,7 +147,7 @@ bool TKvsDatabase::open() { auto *dict = databaseDict(); QReadLocker locker(&dict->lock); - const TKvsDatabaseData &data = (*dict)[connectName]; + const TKvsDatabaseSettings &data = (*dict)[_connectName]; return (driver()) ? driver()->open(data.databaseName, data.userName, data.password, data.hostName, data.port, data.connectOptions) : false; } @@ -191,16 +182,16 @@ QString TKvsDatabase::databaseName() const { auto *dict = databaseDict(); QReadLocker locker(&dict->lock); - return (*dict)[connectName].databaseName; + return (*dict)[_connectName].databaseName; } void TKvsDatabase::setDatabaseName(const QString &name) { - if (!connectName.isEmpty()) { + if (!_connectName.isEmpty()) { auto *dict = databaseDict(); QWriteLocker locker(&dict->lock); - (*dict)[connectName].databaseName = name; + (*dict)[_connectName].databaseName = name; } } @@ -209,16 +200,16 @@ QString TKvsDatabase::hostName() const { auto *dict = databaseDict(); QReadLocker locker(&dict->lock); - return (*dict)[connectName].hostName; + return (*dict)[_connectName].hostName; } void TKvsDatabase::setHostName(const QString &hostName) { - if (!connectName.isEmpty()) { + if (!_connectName.isEmpty()) { auto *dict = databaseDict(); QWriteLocker locker(&dict->lock); - (*dict)[connectName].hostName = hostName; + (*dict)[_connectName].hostName = hostName; } } @@ -227,16 +218,16 @@ int TKvsDatabase::port() const { auto *dict = databaseDict(); QReadLocker locker(&dict->lock); - return (*dict)[connectName].port; + return (*dict)[_connectName].port; } void TKvsDatabase::setPort(int port) { - if (!connectName.isEmpty()) { + if (!_connectName.isEmpty()) { auto *dict = databaseDict(); QWriteLocker locker(&dict->lock); - (*dict)[connectName].port = port; + (*dict)[_connectName].port = port; } } @@ -245,16 +236,16 @@ QString TKvsDatabase::userName() const { auto *dict = databaseDict(); QReadLocker locker(&dict->lock); - return (*dict)[connectName].userName; + return (*dict)[_connectName].userName; } void TKvsDatabase::setUserName(const QString &userName) { - if (!connectName.isEmpty()) { + if (!_connectName.isEmpty()) { auto *dict = databaseDict(); QWriteLocker locker(&dict->lock); - (*dict)[connectName].userName = userName; + (*dict)[_connectName].userName = userName; } } @@ -263,16 +254,16 @@ QString TKvsDatabase::password() const { auto *dict = databaseDict(); QReadLocker locker(&dict->lock); - return (*dict)[connectName].password; + return (*dict)[_connectName].password; } void TKvsDatabase::setPassword(const QString &password) { - if (!connectName.isEmpty()) { + if (!_connectName.isEmpty()) { auto *dict = databaseDict(); QWriteLocker locker(&dict->lock); - (*dict)[connectName].password = password; + (*dict)[_connectName].password = password; } } @@ -281,16 +272,16 @@ QString TKvsDatabase::connectOptions() const { auto *dict = databaseDict(); QReadLocker locker(&dict->lock); - return (*dict)[connectName].connectOptions; + return (*dict)[_connectName].connectOptions; } void TKvsDatabase::setConnectOptions(const QString &options) { - if (!connectName.isEmpty()) { + if (!_connectName.isEmpty()) { auto *dict = databaseDict(); QWriteLocker locker(&dict->lock); - (*dict)[connectName].connectOptions = options; + (*dict)[_connectName].connectOptions = options; } } @@ -299,16 +290,16 @@ QStringList TKvsDatabase::postOpenStatements() const { auto *dict = databaseDict(); QReadLocker locker(&dict->lock); - return (*dict)[connectName].postOpenStatements; + return (*dict)[_connectName].postOpenStatements; } void TKvsDatabase::setPostOpenStatements(const QStringList &statements) { - if (!connectName.isEmpty()) { + if (!_connectName.isEmpty()) { auto *dict = databaseDict(); QWriteLocker locker(&dict->lock); - (*dict)[connectName].postOpenStatements = statements; + (*dict)[_connectName].postOpenStatements = statements; } } @@ -319,3 +310,34 @@ void TKvsDatabase::moveToThread(QThread *targetThread) driver()->moveToThread(targetThread); } } + + +TKvsDatabase &TKvsDatabase::operator=(TKvsDatabase &&other) +{ + if (this != &other) { + _connectName = std::forward(other._connectName); + _driver = other._driver; + other._driver = nullptr; + } + return *this; +} + + +TKvsDatabase::Handle::~Handle() +{ + if (_dbptr) { + TKvsDatabasePool::instance()->pool(std::move(_dbptr)); + } +} + + +TKvsDatabase::Handle &TKvsDatabase::Handle::operator=(TKvsDatabase::Handle &&other) +{ + if (this != &other) { + if (_dbptr) { + TKvsDatabasePool::instance()->pool(std::move(_dbptr)); + } + _dbptr = std::move(other._dbptr); + } + return *this; +} diff --git a/src/tkvsdatabase.h b/src/tkvsdatabase.h index 1e50d2fd5..796380435 100644 --- a/src/tkvsdatabase.h +++ b/src/tkvsdatabase.h @@ -1,20 +1,42 @@ #pragma once #include #include +#include class TKvsDriver; -class TKvsDatabaseData; +class TKvsDatabaseSettings; class T_CORE_EXPORT TKvsDatabase { public: - TKvsDatabase() { } - TKvsDatabase(const TKvsDatabase &other); - ~TKvsDatabase() { } - TKvsDatabase &operator=(const TKvsDatabase &other); + using KvsDbPtr = std::unique_ptr; + + class Handle { + public: + Handle() = default; + explicit Handle(KvsDbPtr ptr) : _dbptr(std::move(ptr)) {} + Handle(const Handle &) = delete; + Handle &operator=(const Handle &) = delete; + Handle(Handle &&other) { *this = std::move(other); } + Handle &operator=(Handle &&other); + ~Handle(); + + TKvsDatabase *operator->() { return _dbptr.get(); } + TKvsDatabase &operator*() { return *(_dbptr.get()); } + explicit operator bool() const noexcept { return (bool)_dbptr; } + + private: + KvsDbPtr _dbptr; + }; + + + TKvsDatabase() = default; + ~TKvsDatabase() { /* do not delete driver pointer */ } + TKvsDatabase(TKvsDatabase &&other) { *this = std::move(other); } + TKvsDatabase &operator=(TKvsDatabase &&); QString driverName() const; - QString connectionName() const { return connectName; } + QString connectionName() const { return _connectName; } QString databaseName() const; void setDatabaseName(const QString &name); QString hostName() const; @@ -35,26 +57,27 @@ class T_CORE_EXPORT TKvsDatabase { bool command(const QString &cmd); bool isOpen() const; bool isValid() const; - TKvsDriver *driver() { return drv; } - const TKvsDriver *driver() const { return drv; } + TKvsDriver *driver() { return _driver; } + const TKvsDriver *driver() const { return _driver; } void moveToThread(QThread *targetThread); static const char *const defaultConnection; - static TKvsDatabase database(const QString &connectionName = defaultConnection); - static TKvsDatabase addDatabase(const QString &driver, const QString &connectionName = defaultConnection); + static std::unique_ptr database(const QString &connectionName = defaultConnection); + static bool addDatabase(const QString &driver, const QString &connectionName = defaultConnection); static void removeDatabase(const QString &connectionName = defaultConnection); - static TKvsDatabaseData settings(const QString &connectionName = defaultConnection); + static TKvsDatabaseSettings settings(const QString &connectionName = defaultConnection); private: - QString connectName; - TKvsDriver *drv {nullptr}; + QString _connectName; + TKvsDriver *_driver {nullptr}; TKvsDatabase(const QString &connectionName, TKvsDriver *driver); - TKvsDatabase(const TKvsDatabaseData &data); + TKvsDatabase(const TKvsDatabaseSettings &data); + T_DISABLE_COPY(TKvsDatabase) }; -class T_CORE_EXPORT TKvsDatabaseData { +class T_CORE_EXPORT TKvsDatabaseSettings { public: QString connectionName; QString databaseName; @@ -66,5 +89,5 @@ class T_CORE_EXPORT TKvsDatabaseData { QStringList postOpenStatements; TKvsDriver *driver {nullptr}; // pointer to a singleton object - TKvsDatabaseData() { } + TKvsDatabaseSettings() { } }; diff --git a/src/tkvsdatabasepool.cpp b/src/tkvsdatabasepool.cpp index eb6e7b21d..dc45b7a90 100644 --- a/src/tkvsdatabasepool.cpp +++ b/src/tkvsdatabasepool.cpp @@ -7,9 +7,7 @@ #include "tkvsdatabasepool.h" #include "tfnamespace.h" -#include "tsqldatabasepool.h" #include "tsystemglobal.h" -#include "tstack.h" #include #include #include @@ -17,6 +15,7 @@ #include #include #include +#include /*! \class TKvsDatabasePool @@ -53,13 +52,13 @@ static KvsEngineHash *kvsEngineHash() TKvsDatabasePool *TKvsDatabasePool::instance() { - static TKvsDatabasePool *databasePool = []() { - auto *pool = new TKvsDatabasePool; + static std::unique_ptr databasePool = []() { + std::unique_ptr pool{new TKvsDatabasePool{}}; pool->maxConnects = Tf::app()->maxNumberOfThreadsPerAppServer(); pool->init(); return pool; }(); - return databasePool; + return databasePool.get(); } @@ -71,7 +70,7 @@ TKvsDatabasePool::TKvsDatabasePool() : TKvsDatabasePool::~TKvsDatabasePool() { - QMutexLocker locker(&_mutex); + //QMutexLocker locker(&_mutex); timer.stop(); for (int eng = 0; eng < (int)Tf::KvsEngine::Num; eng++) { @@ -79,38 +78,42 @@ TKvsDatabasePool::~TKvsDatabasePool() continue; } - auto &cache = cachedDatabase[eng]; + auto &cache = cachedDatabases[eng]; QString name; - while (!cache.isEmpty()) { - name = cache.pop(); - TKvsDatabase::database(name).close(); - TKvsDatabase::removeDatabase(name); + while (!cache.empty()) { + auto db = cache.pop(); + if (db) { + name = (*db)->connectionName(); + (*db)->close(); + TKvsDatabase::removeDatabase(name); + } } - auto &stack = availableNames[eng]; - while (!stack.isEmpty()) { - name = stack.pop(); - TKvsDatabase::removeDatabase(name); + auto &stack = availableDatabases[eng]; + while (!stack.empty()) { + auto db = stack.pop(); + if (db) { + name = (*db)->connectionName(); + TKvsDatabase::removeDatabase(name); + } } } - delete[] cachedDatabase; delete[] lastCachedTime; - delete[] availableNames; } void TKvsDatabasePool::init() { - if (cachedDatabase) { + if (lastCachedTime) { return; } - QMutexLocker locker(&_mutex); + //QMutexLocker locker(&_mutex); - cachedDatabase = new QStack[(int)Tf::KvsEngine::Num]; + cachedDatabases.resize((int)Tf::KvsEngine::Num); + availableDatabases.resize((int)Tf::KvsEngine::Num); lastCachedTime = new TAtomic[(int)Tf::KvsEngine::Num]; - availableNames = new QStack[(int)Tf::KvsEngine::Num]; bool aval = false; // Adds databases previously @@ -126,17 +129,19 @@ void TKvsDatabasePool::init() tSystemDebug("KVS database available. engine:{}", (int)engine); } - auto &stack = availableNames[(int)engine]; + auto &stack = availableDatabases[(int)engine]; for (int i = 0; i < maxConnects; ++i) { - TKvsDatabase db = TKvsDatabase::addDatabase(drv, QString::asprintf(CONN_NAME_FORMAT, (int)engine, i)); - if (!db.isValid()) { + QString name = QString::asprintf(CONN_NAME_FORMAT, (int)engine, i); + TKvsDatabase::addDatabase(drv, name); + auto db = TKvsDatabase::database(name); + if (!db->isValid()) { Tf::warn("KVS init parameter is invalid"); break; } - setDatabaseSettings(db, engine); - stack.push(db.connectionName()); // push onto stack - tSystemDebug("Add KVS successfully. name:{}", db.connectionName()); + setDatabaseSettings(*db, engine); + tSystemDebug("Add KVS successfully. name:{}", db->connectionName()); + stack.push(std::move(db)); // push onto stack } } @@ -147,10 +152,8 @@ void TKvsDatabasePool::init() } -TKvsDatabase TKvsDatabasePool::database(Tf::KvsEngine engine) +TKvsDatabase::Handle TKvsDatabasePool::database(Tf::KvsEngine engine) { - QMutexLocker locker(&_mutex); - if (!Tf::app()->isKvsAvailable(engine)) { switch (engine) { case Tf::KvsEngine::MongoDB: @@ -177,51 +180,49 @@ TKvsDatabase TKvsDatabasePool::database(Tf::KvsEngine engine) throw RuntimeException("No such KVS engine", __FILE__, __LINE__); break; } - return TKvsDatabase(); + return TKvsDatabase::Handle(); } - auto &cache = cachedDatabase[(int)engine]; - auto &stack = availableNames[(int)engine]; + auto &cache = cachedDatabases[(int)engine]; + auto &stack = availableDatabases[(int)engine]; - for (;;) { - QString name; - if (!cache.isEmpty()) { - name = cache.pop(); - auto db = TKvsDatabase::database(name); - if (Q_LIKELY(db.isOpen())) { - tSystemDebug("Gets cached KVS database: {}", db.connectionName()); - db.moveToThread(QThread::currentThread()); // move to thread - return db; + while (true) { + auto dbptr = cache.pop(); + if (dbptr) { + if ((*dbptr)->isOpen()) { + tSystemDebug("Gets cached KVS database: {}", (*dbptr)->connectionName()); + (*dbptr)->moveToThread(QThread::currentThread()); // move to thread + return TKvsDatabase::Handle(std::move(*dbptr)); } else { - tSystemError("Pooled database is not open: {} [{}:{}]", db.connectionName(), __FILE__, __LINE__); - stack.push(name); + tSystemError("Pooled database is not open: {} [{}:{}]", (*dbptr)->connectionName(), __FILE__, __LINE__); + stack.push(std::move(*dbptr)); continue; } } - if (Q_LIKELY(!stack.isEmpty())) { - name = stack.pop(); - auto db = TKvsDatabase::database(name); - db.moveToThread(QThread::currentThread()); // move to thread + dbptr = stack.pop(); + if (!dbptr) { + break; + } + (*dbptr)->moveToThread(QThread::currentThread()); // move to thread - if (Q_UNLIKELY(!db.open())) { - Tf::error("KVS Database open error. Invalid database settings, or maximum number of KVS connection exceeded."); - tSystemError("KVS database open error: {}", db.connectionName()); - return TKvsDatabase(); - } + if (!(*dbptr)->open()) { + Tf::error("KVS Database open error. Invalid database settings, or maximum number of KVS connection exceeded."); + tSystemError("KVS database open error: {}", (*dbptr)->connectionName()); + return TKvsDatabase::Handle(); + } - tSystemDebug("KVS opened successfully env:{} connectname:{} dbname:{}", Tf::app()->databaseEnvironment(), db.connectionName(), db.databaseName()); - tSystemDebug("Gets KVS database: {}", db.connectionName()); - // Executes post-open statements - if (!db.postOpenStatements().isEmpty()) { - for (QString st : db.postOpenStatements()) { - st = st.trimmed(); - db.command(st); - } - } + tSystemDebug("KVS opened successfully env:{} connectname:{} dbname:{}", Tf::app()->databaseEnvironment(), (*dbptr)->connectionName(), (*dbptr)->databaseName()); + tSystemDebug("Gets KVS database: {}", (*dbptr)->connectionName()); - return db; + // Executes post-open statements + if (!(*dbptr)->postOpenStatements().isEmpty()) { + for (QString st : (*dbptr)->postOpenStatements()) { + st = st.trimmed(); + (*dbptr)->command(st); + } } + return TKvsDatabase::Handle(std::move(*dbptr)); } throw RuntimeException("No pooled connection", __FILE__, __LINE__); @@ -279,54 +280,33 @@ bool TKvsDatabasePool::setDatabaseSettings(TKvsDatabase &database, Tf::KvsEngine } -TKvsDatabaseData TKvsDatabasePool::getDatabaseSettings(Tf::KvsEngine engine) const -{ - QMutexLocker locker(&_mutex); - - TKvsDatabaseData settrings; - auto &stack = availableNames[(int)engine]; - - QString name; - for (;;) { - if (Q_LIKELY(!stack.isEmpty())) { - name = stack.pop(); - return TKvsDatabase::settings(name); - stack.push(name); - } - } - - throw RuntimeException("No pooled connection", __FILE__, __LINE__); -} - - -void TKvsDatabasePool::pool(TKvsDatabase &database) +void TKvsDatabasePool::pool(KvsDbPtr dbptr) { - QMutexLocker locker(&_mutex); + //QMutexLocker locker(&_mutex); - if (Q_LIKELY(database.isValid())) { + if (dbptr->isValid()) { bool ok; - int engine = database.connectionName().left(2).toInt(&ok); - if (Q_UNLIKELY(!ok)) { + int engine = dbptr->connectionName().left(2).toInt(&ok); + if (!ok) { throw RuntimeException("No such KVS engine", __FILE__, __LINE__); } - if (database.isOpen()) { - cachedDatabase[engine].push(database.connectionName()); + if (dbptr->isOpen()) { + tSystemDebug("Pooled KVS database: {} count:{}", dbptr->connectionName(), (qint64)cachedDatabases.size()); + cachedDatabases[engine].push(std::move(dbptr)); lastCachedTime[engine].store((uint)std::time(nullptr)); - tSystemDebug("Pooled KVS database: {} count:{}", database.connectionName(), (qint64)cachedDatabase->count()); } else { - tSystemWarn("Closed KVS database connection, name: {}", database.connectionName()); - availableNames[engine].push(database.connectionName()); + tSystemWarn("Closed KVS database connection, name: {}", dbptr->connectionName()); + availableDatabases[engine].push(std::move(dbptr)); } } - database = TKvsDatabase(); // Sets an invalid object } void TKvsDatabasePool::timerEvent(QTimerEvent *event) { if (event->timerId() == timer.timerId()) { - QMutexLocker locker(&_mutex); + //QMutexLocker locker(&_mutex); QString name; // Closes extra-connection @@ -335,13 +315,13 @@ void TKvsDatabasePool::timerEvent(QTimerEvent *event) continue; } - auto &cache = cachedDatabase[e]; + auto &cache = cachedDatabases[e]; while (lastCachedTime[e].load() < (uint)std::time(nullptr) - 30 - && !cache.isEmpty()) { - name = cache.pop(); - TKvsDatabase::database(name).close(); - tSystemDebug("Closed KVS database connection, name: {}", name); - availableNames[e].push(name); + && !cache.empty()) { + auto db = cache.pop(); + (*db)->close(); + tSystemDebug("Closed KVS database connection, name: {}", (*db)->connectionName()); + availableDatabases[e].push(std::move(*db)); } } } else { diff --git a/src/tkvsdatabasepool.h b/src/tkvsdatabasepool.h index f8c116722..a89f17ff6 100644 --- a/src/tkvsdatabasepool.h +++ b/src/tkvsdatabasepool.h @@ -1,25 +1,24 @@ #pragma once #include #include +#include #include #include #include #include #include #include - -class QSettings; -template class TStack; +#include +#include class T_CORE_EXPORT TKvsDatabasePool : public QObject { Q_OBJECT public: - ~TKvsDatabasePool(); - TKvsDatabase database(Tf::KvsEngine engine); - void pool(TKvsDatabase &database); - TKvsDatabaseData getDatabaseSettings(Tf::KvsEngine engine) const; + using KvsDbPtr = std::unique_ptr; + ~TKvsDatabasePool(); + TKvsDatabase::Handle database(Tf::KvsEngine engine); static TKvsDatabasePool *instance(); protected: @@ -31,14 +30,18 @@ class T_CORE_EXPORT TKvsDatabasePool : public QObject { private: TKvsDatabasePool(); + void pool(KvsDbPtr dbptr); - mutable QRecursiveMutex _mutex; - QStack *cachedDatabase {nullptr}; + //mutable QRecursiveMutex _mutex; TAtomic *lastCachedTime {nullptr}; - QStack *availableNames {nullptr}; int maxConnects {0}; QBasicTimer timer; + // std::vector> availableDatabases; + // std::vector> cachedDatabases; + std::vector> availableDatabases; + std::vector> cachedDatabases; T_DISABLE_COPY(TKvsDatabasePool) T_DISABLE_MOVE(TKvsDatabasePool) + friend class TKvsDatabase; }; diff --git a/src/tkvsdriver.cpp b/src/tkvsdriver.cpp index 766da97f6..f83dc4bc1 100644 --- a/src/tkvsdriver.cpp +++ b/src/tkvsdriver.cpp @@ -12,3 +12,9 @@ \brief The TKvsDriver class is an abstract base class for accessing specific KVS databases. This class should not be used directly. */ + +// key(): +// MEMCACHED +// MONGODB +// REDIS +// MEMORY diff --git a/src/tlockqueue.cpp b/src/tlockqueue.cpp new file mode 100644 index 000000000..a9dfe2174 --- /dev/null +++ b/src/tlockqueue.cpp @@ -0,0 +1 @@ +#include "tlockqueue.h" diff --git a/src/tlockqueue.h b/src/tlockqueue.h new file mode 100644 index 000000000..c3bb255c7 --- /dev/null +++ b/src/tlockqueue.h @@ -0,0 +1,55 @@ +#pragma once +#include +#include +#include + + +template +class TLockQueue { +public: + void push(const T &val) + { + std::lock_guard lock(_mutex); + _queue.push(val); + } + + void push(T &&val) + { + std::lock_guard lock(_mutex); + _queue.push(std::move(val)); + } + + std::optional pop() + { + std::lock_guard lock(_mutex); + std::optional val; + + if (!_queue.empty()) { + val = std::forward(_queue.front()); + _queue.pop(); + } + return val; + } + + bool empty() const + { + std::lock_guard lock(_mutex); + return _queue.empty(); + } + + size_t size() const + { + std::lock_guard lock(_mutex); + return _queue.size(); + } + + size_t count() const + { + std::lock_guard lock(_mutex); + return _queue.size(); + } + +private: + std::queue _queue; + mutable std::mutex _mutex; +}; diff --git a/src/tlockstack.cpp b/src/tlockstack.cpp new file mode 100644 index 000000000..aa42fdff9 --- /dev/null +++ b/src/tlockstack.cpp @@ -0,0 +1 @@ +#include "tlockstack.h" diff --git a/src/tlockstack.h b/src/tlockstack.h new file mode 100644 index 000000000..2346cf418 --- /dev/null +++ b/src/tlockstack.h @@ -0,0 +1,64 @@ +#pragma once +#include +#include +#include + + +template +class TLockStack { +public: + TLockStack() = default; + TLockStack(const TLockStack &) = delete; + TLockStack &operator=(const TLockStack &) = delete; + TLockStack(TLockStack &&other) noexcept + { + _stack = std::move(other._stack); + } + TLockStack &operator=(TLockStack &&) = delete; + + void push(const T &val) + { + std::lock_guard lock(_mutex); + _stack.push(val); + } + + void push(T &&val) + { + std::lock_guard lock(_mutex); + _stack.push(std::move(val)); + } + + std::optional pop() + { + std::lock_guard lock(_mutex); + std::optional val; + + if (!_stack.empty()) { + val = std::forward(_stack.top()); + _stack.pop(); + } + return val; + } + + bool empty() const + { + std::lock_guard lock(_mutex); + return _stack.empty(); + } + + size_t size() const + { + std::lock_guard lock(_mutex); + return _stack.size(); + } + + size_t count() const + { + std::lock_guard lock(_mutex); + return _stack.size(); + } + +private: + std::stack _stack; + mutable std::mutex _mutex; +}; diff --git a/src/tmemcached.cpp b/src/tmemcached.cpp index 354df6fc0..0aff36c66 100644 --- a/src/tmemcached.cpp +++ b/src/tmemcached.cpp @@ -283,13 +283,13 @@ QByteArray TMemcached::requestLine(const QByteArray &command, const QByteArray & TMemcachedDriver *TMemcached::driver() { #ifdef TF_NO_DEBUG - return (TMemcachedDriver *)_database.driver(); + return (_database) ? (TMemcachedDriver *)_database->driver() : nullptr; #else - if (!_database.driver()) { + if (!_database || !_database->driver()) { return nullptr; } - TMemcachedDriver *driver = dynamic_cast(_database.driver()); + TMemcachedDriver *driver = dynamic_cast(_database->driver()); if (!driver) { throw RuntimeException("cast error", __FILE__, __LINE__); } @@ -303,13 +303,13 @@ TMemcachedDriver *TMemcached::driver() const TMemcachedDriver *TMemcached::driver() const { #ifdef TF_NO_DEBUG - return (const TMemcachedDriver *)_database.driver(); + return (_database) ? (TMemcachedDriver *)_database->driver() : nullptr; #else - if (!_database.driver()) { + if (!_database || !_database->driver()) { return nullptr; } - const TMemcachedDriver *driver = dynamic_cast(_database.driver()); + const TMemcachedDriver *driver = dynamic_cast(_database->driver()); if (!driver) { throw RuntimeException("cast error", __FILE__, __LINE__); } diff --git a/src/tmemcached.h b/src/tmemcached.h index 04bb69890..69ac41f9f 100644 --- a/src/tmemcached.h +++ b/src/tmemcached.h @@ -38,7 +38,7 @@ class T_CORE_EXPORT TMemcached { TMemcachedDriver *driver(); const TMemcachedDriver *driver() const; - TKvsDatabase _database; + TKvsDatabase::Handle &_database; friend class TCacheMemcachedStore; T_DISABLE_COPY(TMemcached) diff --git a/src/tmongoquery.cpp b/src/tmongoquery.cpp index dcfddfb1a..bc2a54b5d 100644 --- a/src/tmongoquery.cpp +++ b/src/tmongoquery.cpp @@ -36,29 +36,6 @@ TMongoQuery::TMongoQuery(Tf::KvsEngine engine, const QString &collection) : { } -/*! - Copy constructor. -*/ -TMongoQuery::TMongoQuery(const TMongoQuery &other) : - _database(other._database), - _collection(other._collection), - _queryLimit(other._queryLimit), - _queryOffset(other._queryOffset) -{ -} - -/*! - Assignment operator. -*/ -TMongoQuery &TMongoQuery::operator=(const TMongoQuery &other) -{ - _database = other._database; - _collection = other._collection; - _queryLimit = other._queryLimit; - _queryOffset = other._queryOffset; - return *this; -} - /*! Finds documents by the criteria \a criteria in the collection and returns the number of the documents. Use the \a fields parameter to @@ -67,7 +44,7 @@ TMongoQuery &TMongoQuery::operator=(const TMongoQuery &other) */ bool TMongoQuery::find(const QVariantMap &criteria, const QVariantMap &orderBy, const QStringList &fields) { - if (!_database.isValid()) { + if (!_database || !_database->isValid()) { tSystemError("TMongoQuery::find : driver not loaded"); return false; } @@ -81,7 +58,7 @@ bool TMongoQuery::find(const QVariantMap &criteria, const QVariantMap &orderBy, */ bool TMongoQuery::next() { - if (!_database.isValid()) { + if (!_database || !_database->isValid()) { return false; } return driver()->cursor().next(); @@ -92,7 +69,7 @@ bool TMongoQuery::next() */ QVariantMap TMongoQuery::value() const { - if (!_database.isValid()) { + if (!_database || !_database->isValid()) { return QVariantMap(); } return driver()->cursor().value(); @@ -105,7 +82,7 @@ QVariantMap TMongoQuery::value() const */ QVariantMap TMongoQuery::findOne(const QVariantMap &criteria, const QStringList &fields) { - if (!_database.isValid()) { + if (!_database || !_database->isValid()) { tSystemError("TMongoQuery::findOne : driver not loaded"); return QVariantMap(); } @@ -131,7 +108,7 @@ QVariantMap TMongoQuery::findById(const QString &id, const QStringList &fields) */ bool TMongoQuery::insert(QVariantMap &document) { - if (!_database.isValid()) { + if (!_database || !_database->isValid()) { tSystemError("TMongoQuery::insert : driver not loaded"); return false; } @@ -158,7 +135,7 @@ int TMongoQuery::remove(const QVariantMap &criteria) { int deletedCount = -1; - if (!_database.isValid()) { + if (!_database || !_database->isValid()) { tSystemError("TMongoQuery::remove : driver not loaded"); return deletedCount; } @@ -201,7 +178,7 @@ int TMongoQuery::update(const QVariantMap &criteria, const QVariantMap &document QVariantMap doc; QVariantMap tmp = document; - if (!_database.isValid()) { + if (!_database || !_database->isValid()) { tSystemError("TMongoQuery::update : driver not loaded"); return modifiedCount; } @@ -231,7 +208,7 @@ int TMongoQuery::updateMany(const QVariantMap &criteria, const QVariantMap &docu int modifiedCount = -1; QVariantMap doc; - if (!_database.isValid()) { + if (!_database || !_database->isValid()) { tSystemError("TMongoQuery::updateMany : driver not loaded"); return modifiedCount; } @@ -272,7 +249,7 @@ bool TMongoQuery::updateById(const QVariantMap &document) int TMongoQuery::count(const QVariantMap &criteria) { - if (!_database.isValid()) { + if (!_database || !_database->isValid()) { tSystemError("TMongoQuery::count : driver not loaded"); return -1; } @@ -311,13 +288,13 @@ QString TMongoQuery::lastErrorString() const TMongoDriver *TMongoQuery::driver() { #ifdef TF_NO_DEBUG - return (TMongoDriver *)_database.driver(); + return (_database) ? (TMongoDriver *)_database->driver() : nullptr; #else - if (!_database.driver()) { + if (!_database || !_database->driver()) { return nullptr; } - TMongoDriver *driver = dynamic_cast(_database.driver()); + TMongoDriver *driver = dynamic_cast(_database->driver()); if (!driver) { throw RuntimeException("cast error", __FILE__, __LINE__); } @@ -331,13 +308,13 @@ TMongoDriver *TMongoQuery::driver() const TMongoDriver *TMongoQuery::driver() const { #ifdef TF_NO_DEBUG - return (const TMongoDriver *)_database.driver(); + return (_database) ? (const TMongoDriver *)_database->driver() : nullptr; #else - if (!_database.driver()) { + if (!_database || !_database->driver()) { return nullptr; } - const TMongoDriver *driver = dynamic_cast(_database.driver()); + const TMongoDriver *driver = dynamic_cast(_database->driver()); if (!driver) { throw RuntimeException("cast error", __FILE__, __LINE__); } diff --git a/src/tmongoquery.h b/src/tmongoquery.h index a94a25e10..0c1c75e10 100644 --- a/src/tmongoquery.h +++ b/src/tmongoquery.h @@ -10,8 +10,10 @@ class TMongoDriver; class T_CORE_EXPORT TMongoQuery { public: TMongoQuery(const QString &collection); - TMongoQuery(const TMongoQuery &other); + //TMongoQuery(const TMongoQuery &other); + TMongoQuery(TMongoQuery &&) = default; virtual ~TMongoQuery() { } + TMongoQuery &operator=(TMongoQuery &&) = default; int limit() const; void setLimit(int limit); @@ -33,7 +35,7 @@ class T_CORE_EXPORT TMongoQuery { int count(const QVariantMap &criteria = QVariantMap()); QString lastErrorString() const; - TMongoQuery &operator=(const TMongoQuery &other); + //TMongoQuery &operator=(const TMongoQuery &other); private: TMongoDriver *driver(); @@ -42,12 +44,13 @@ class T_CORE_EXPORT TMongoQuery { private: TMongoQuery(Tf::KvsEngine engine, const QString &collection); - TKvsDatabase _database; + TKvsDatabase::Handle &_database; QString _collection; int _queryLimit {0}; int _queryOffset {0}; friend class TCacheMongoStore; + T_DISABLE_COPY(TMongoQuery) }; diff --git a/src/tmultiplexingserver_linux.cpp b/src/tmultiplexingserver_linux.cpp index ae34df3e6..b9865ef54 100644 --- a/src/tmultiplexingserver_linux.cpp +++ b/src/tmultiplexingserver_linux.cpp @@ -22,27 +22,20 @@ #include #include #include +#include constexpr int SEND_BUF_SIZE = 16 * 1024; constexpr int RECV_BUF_SIZE = 128 * 1024; namespace { -TMultiplexingServer *multiplexingServer = nullptr; - - -void cleanup() -{ - delete multiplexingServer; - multiplexingServer = nullptr; -} +std::unique_ptr multiplexingServer; } void TMultiplexingServer::instantiate(int listeningSocket) { if (!multiplexingServer) { - multiplexingServer = new TMultiplexingServer(listeningSocket); - qAddPostRoutine(::cleanup); + multiplexingServer.reset(new TMultiplexingServer(listeningSocket)); } } @@ -52,7 +45,7 @@ TMultiplexingServer *TMultiplexingServer::instance() if (Q_UNLIKELY(!multiplexingServer)) { tFatal("Call TMultiplexingServer::instantiate() function first"); } - return multiplexingServer; + return multiplexingServer.get(); } diff --git a/src/tpublisher.cpp b/src/tpublisher.cpp index 2e7e69327..8bfb43141 100644 --- a/src/tpublisher.cpp +++ b/src/tpublisher.cpp @@ -108,12 +108,12 @@ void Pub::publish(const QByteArray &binary, const QObject *sender) TPublisher *TPublisher::instance() { - static TPublisher *globalInstance = []() { - auto *pub = new TPublisher(); - connect(TSystemBus::instance(), SIGNAL(readyReceive()), pub, SLOT(receiveSystemBus())); + static std::unique_ptr globalInstance = []() { + std::unique_ptr pub {new TPublisher}; + connect(TSystemBus::instance(), SIGNAL(readyReceive()), pub.get(), SLOT(receiveSystemBus())); return pub; }(); - return globalInstance; + return globalInstance.get(); } @@ -177,11 +177,11 @@ QObject *TPublisher::castToObject(TAbstractWebSocket *socket) QObject *obj = nullptr; switch (Tf::app()->multiProcessingModule()) { - case TWebApplication::Thread: + case TWebApplication::MultiProcessingModule::Thread: obj = dynamic_cast(socket); break; - case TWebApplication::Epoll: + case TWebApplication::MultiProcessingModule::Epoll: #ifdef Q_OS_LINUX obj = dynamic_cast(socket); #else @@ -189,6 +189,14 @@ QObject *TPublisher::castToObject(TAbstractWebSocket *socket) #endif break; + case TWebApplication::MultiProcessingModule::Uring: +#ifdef Q_OS_LINUX + tFatal("TODO TODO TODO"); +#else + tFatal("Unsupported MPM: uring"); +#endif + break; + default: tFatal("Unsupported MPM: epoll"); break; diff --git a/src/tqueue.cpp b/src/tqueue.cpp index d0d54a7b7..c937d6b7d 100644 --- a/src/tqueue.cpp +++ b/src/tqueue.cpp @@ -1,12 +1,9 @@ #include "tqueue.h" -#include - -namespace { -QThreadStorage hzptrTls; -} +#include THazardPtr &Tf::hazardPtrForQueue() { - return hzptrTls.localData(); + static thread_local THazardPtr hzptrTls; + return hzptrTls; } diff --git a/src/tqueue.h b/src/tqueue.h index dbf17df41..0d046f957 100644 --- a/src/tqueue.h +++ b/src/tqueue.h @@ -3,7 +3,6 @@ #include "tatomicptr.h" #include "thazardobject.h" #include "thazardptr.h" -#include namespace Tf { @@ -11,14 +10,20 @@ T_CORE_EXPORT THazardPtr &hazardPtrForQueue(); } +// +// Non-blocking move queue +// template class TQueue { private: struct Node : public THazardObject { T value; TAtomicPtr next; - Node(const T &v) : - value(v) { } + + explicit Node(const T &v) : value{v} { } + Node(Node &&) = delete; + Node &operator=(const Node &) = delete; + Node &operator=(Node &&) = delete; }; TAtomicPtr queHead {nullptr}; @@ -27,10 +32,11 @@ class TQueue { public: TQueue(); - void enqueue(const T &val); - bool dequeue(T &val); - bool head(T &val); + void enqueue(T val); + std::optional dequeue(); int count() const { return counter.load(); } + int size() const { return counter.load(); } + bool empty() const { return counter.load() == 0; } T_DISABLE_COPY(TQueue) T_DISABLE_MOVE(TQueue) @@ -47,9 +53,9 @@ inline TQueue::TQueue() template -inline void TQueue::enqueue(const T &val) +inline void TQueue::enqueue(T val) { - auto *newnode = new Node(val); + auto *newnode = new Node(std::move(val)); for (;;) { Node *tail = queTail.load(); Node *next = tail->next.load(); @@ -73,9 +79,11 @@ inline void TQueue::enqueue(const T &val) template -inline bool TQueue::dequeue(T &val) +inline std::optional TQueue::dequeue() { + std::optional val; Node *next; + for (;;) { Node *head = queHead.load(); Node *tail = queTail.load(); @@ -98,7 +106,7 @@ inline bool TQueue::dequeue(T &val) } if (queHead.compareExchange(head, next)) { - val = next->value; + val = std::move(next->value); head->deleteLater(); // gc counter--; break; @@ -106,26 +114,5 @@ inline bool TQueue::dequeue(T &val) } } Tf::hazardPtrForQueue().clear(); - return (bool)next; + return val; } - - -template -inline bool TQueue::head(T &val) -{ - Node *next; - for (;;) { - Node *headp = queHead.load(); - next = Tf::hazardPtrForQueue().guard(&headp->next); - - if (Q_LIKELY(headp == queHead.load())) { - if (next) { - val = next->value; - } - break; - } - } - Tf::hazardPtrForQueue().clear(); - return (bool)next; -} - diff --git a/src/trash/tfileaiowriter_unix.cpp b/src/trash/tfileaiowriter_unix.cpp index 916c27fa1..01d7fd326 100644 --- a/src/trash/tfileaiowriter_unix.cpp +++ b/src/trash/tfileaiowriter_unix.cpp @@ -7,7 +7,7 @@ #include "tfcore_unix.h" #include "tfileaiowriter.h" -#include "tqueue.h" +#include "tlockqueue.h" #include #include #include @@ -25,7 +25,7 @@ class TFileAioWriterData { QString fileName; int fileDescriptor {0}; #if 0 - TQueue syncBuffer; + TLockQueue syncBuffer; #else QQueue syncBuffer; #endif diff --git a/src/tredis.cpp b/src/tredis.cpp index bad652555..1de10bc12 100644 --- a/src/tredis.cpp +++ b/src/tredis.cpp @@ -49,13 +49,13 @@ TRedis::TRedis(Tf::KvsEngine engine) : TRedisDriver *TRedis::driver() { #ifdef TF_NO_DEBUG - return (TRedisDriver *)_database.driver(); + return (_database) ? (TRedisDriver *)_database->driver() : nullptr; #else - if (!_database.driver()) { + if (!_database || !_database->driver()) { return nullptr; } - TRedisDriver *driver = dynamic_cast(_database.driver()); + TRedisDriver *driver = dynamic_cast(_database->driver()); if (!driver) { throw RuntimeException("cast error", __FILE__, __LINE__); } @@ -69,13 +69,13 @@ TRedisDriver *TRedis::driver() const TRedisDriver *TRedis::driver() const { #ifdef TF_NO_DEBUG - return (const TRedisDriver *)_database.driver(); + return (_database) ? (TRedisDriver *)_database->driver() : nullptr; #else - if (!_database.driver()) { + if (!_database || !_database->driver()) { return nullptr; } - const TRedisDriver *driver = dynamic_cast(_database.driver()); + const TRedisDriver *driver = dynamic_cast(_database->driver()); if (!driver) { throw RuntimeException("cast error", __FILE__, __LINE__); } diff --git a/src/tredis.h b/src/tredis.h index f774908bb..2489903d3 100644 --- a/src/tredis.h +++ b/src/tredis.h @@ -70,7 +70,7 @@ class T_CORE_EXPORT TRedis { static QByteArrayList toByteArrayList(const QStringList &values); static QStringList toStringList(const QByteArrayList &values); - TKvsDatabase _database; + TKvsDatabase::Handle &_database; friend class TCacheRedisStore; T_DISABLE_COPY(TRedis) diff --git a/src/tsendbuffer.cpp b/src/tsendbuffer.cpp index 390c671c1..77e5a5b0d 100644 --- a/src/tsendbuffer.cpp +++ b/src/tsendbuffer.cpp @@ -38,9 +38,8 @@ TSendBuffer::TSendBuffer(const QByteArray &header) : } -TSendBuffer::TSendBuffer(int statusCode, const QHostAddress &address, const QByteArray &method) +TSendBuffer::TSendBuffer(Tf::StatusCode statusCode, const QHostAddress &address, const QByteArray &method) { - _accesslogger.open(); _accesslogger.setStatusCode(statusCode); _accesslogger.setTimestamp(QDateTime::currentDateTime()); _accesslogger.setRemoteHost(address.toString().toLatin1()); @@ -71,7 +70,6 @@ void TSendBuffer::release() delete _bodyFile; _bodyFile = nullptr; } - _accesslogger.close(); } diff --git a/src/tsendbuffer.h b/src/tsendbuffer.h index d968d0ab5..e03a078ca 100644 --- a/src/tsendbuffer.h +++ b/src/tsendbuffer.h @@ -30,7 +30,7 @@ class T_CORE_EXPORT TSendBuffer { TSendBuffer(const QByteArray &header, const QFileInfo &file, bool autoRemove, TAccessLogger &&logger); TSendBuffer(const QByteArray &header); - TSendBuffer(int statusCode, const QHostAddress &address, const QByteArray &method); + TSendBuffer(Tf::StatusCode statusCode, const QHostAddress &address, const QByteArray &method); TSendBuffer(); friend class TEpollSocket; diff --git a/src/tsession.h b/src/tsession.h index 40dabd7fb..21be18146 100644 --- a/src/tsession.h +++ b/src/tsession.h @@ -6,9 +6,12 @@ class T_CORE_EXPORT TSession : public QVariantMap { public: - TSession(const QByteArray &id = QByteArray()); - TSession(const TSession &other); - TSession &operator=(const TSession &other); + explicit TSession(const QByteArray &id = QByteArray()); + ~TSession() = default; + TSession(const TSession &other) = default; + TSession &operator=(const TSession &other) = default; + TSession(TSession &&other) = default; + TSession &operator=(TSession &&other) = default; QByteArray id() const { return sessionId; } void reset(); @@ -33,17 +36,17 @@ inline TSession::TSession(const QByteArray &id) : { } -inline TSession::TSession(const TSession &other) : - QVariantMap(*static_cast(&other)), sessionId(other.sessionId) -{ -} +// inline TSession::TSession(const TSession &other) : +// QVariantMap(*static_cast(&other)), sessionId(other.sessionId) +// { +// } -inline TSession &TSession::operator=(const TSession &other) -{ - QVariantMap::operator=(*static_cast(&other)); - sessionId = other.sessionId; - return *this; -} +// inline TSession &TSession::operator=(const TSession &other) +// { +// QVariantMap::operator=(*static_cast(&other)); +// sessionId = other.sessionId; +// return *this; +// } inline TSession::iterator TSession::insert(const QString &key, const QVariant &value) { diff --git a/src/tsharedmemorykvs.cpp b/src/tsharedmemorykvs.cpp index c9d22249b..1052edf3e 100644 --- a/src/tsharedmemorykvs.cpp +++ b/src/tsharedmemorykvs.cpp @@ -434,13 +434,13 @@ bool TSharedMemoryKvs::unlock() TSharedMemoryKvsDriver *TSharedMemoryKvs::driver() { #ifdef TF_NO_DEBUG - return (TSharedMemoryKvsDriver *)_database.driver(); + return (_database) ? (TSharedMemoryKvsDriver *)_database->driver() : nullptr; #else - if (!_database.driver()) { + if (!_database || !_database->driver()) { return nullptr; } - TSharedMemoryKvsDriver *driver = dynamic_cast(_database.driver()); + TSharedMemoryKvsDriver *driver = dynamic_cast(_database->driver()); if (!driver) { throw RuntimeException("cast error", __FILE__, __LINE__); } @@ -454,13 +454,13 @@ TSharedMemoryKvsDriver *TSharedMemoryKvs::driver() const TSharedMemoryKvsDriver *TSharedMemoryKvs::driver() const { #ifdef TF_NO_DEBUG - return (const TSharedMemoryKvsDriver *)_database.driver(); + return (_database) ? (const TSharedMemoryKvsDriver *)_database->driver() : nullptr; #else - if (!_database.driver()) { + if (!_database || !_database->driver()) { return nullptr; } - const TSharedMemoryKvsDriver *driver = dynamic_cast(_database.driver()); + const TSharedMemoryKvsDriver *driver = dynamic_cast(_database->driver()); if (!driver) { throw RuntimeException("cast error", __FILE__, __LINE__); } diff --git a/src/tsharedmemorykvs.h b/src/tsharedmemorykvs.h index db26037cf..beb5c2415 100644 --- a/src/tsharedmemorykvs.h +++ b/src/tsharedmemorykvs.h @@ -100,7 +100,7 @@ class T_CORE_EXPORT TSharedMemoryKvs { TSharedMemoryKvsDriver *driver(); const TSharedMemoryKvsDriver *driver() const; - TKvsDatabase _database; + TKvsDatabase::Handle &_database; hash_header_t *_h {nullptr}; friend class TCacheSharedMemoryStore; diff --git a/src/tsqldatabase.cpp b/src/tsqldatabase.cpp index 93bb7be29..befce5317 100644 --- a/src/tsqldatabase.cpp +++ b/src/tsqldatabase.cpp @@ -6,58 +6,89 @@ */ #include "tsqldatabase.h" +#include "tsqldatabasepool.h" #include "tsqldriverextension.h" #include "tsystemglobal.h" #include -#include +#include #include -class TDatabaseDict : public QMap { +class TDatabaseDict : public QSet { public: mutable QReadWriteLock lock; }; Q_GLOBAL_STATIC(TDatabaseDict, dbDict) +TSqlDatabase::~TSqlDatabase() +{} + + +TSqlDatabase::TSqlDatabase(const QSqlDatabase &database) : + _sqlDatabase(database) +{} + + +TSqlDatabase::TSqlDatabase(TSqlDatabase &&other) +{ + *this = std::move(other); +} + + +TSqlDatabase &TSqlDatabase::operator=(TSqlDatabase &&other) +{ + if (this != &other) { + _sqlDatabase = std::forward(other._sqlDatabase); + _postOpenStatements = std::forward(other._postOpenStatements); + _enableUpsert = other._enableUpsert; + _driverExtension = std::move(other._driverExtension); + } + return *this; +} + + TSqlDatabase::DbmsType TSqlDatabase::dbmsType() const { return (_sqlDatabase.driver()) ? (TSqlDatabase::DbmsType)_sqlDatabase.driver()->dbmsType() : UnknownDbms; } -void TSqlDatabase::setDriverExtension(TSqlDriverExtension *extension) +void TSqlDatabase::setDriverExtension(std::unique_ptr extension) { - _driverExtension = extension; + _driverExtension = std::move(extension); } -TSqlDatabase &TSqlDatabase::database(const QString &connectionName) +std::unique_ptr TSqlDatabase::database(const QString &connectionName) { - static TSqlDatabase defaultDatabase; auto *dict = dbDict(); QReadLocker locker(&dict->lock); - if (dict->contains(connectionName)) { - return (*dict)[connectionName]; - } else { - return defaultDatabase; + if (!dict->contains(connectionName)) { + tSystemWarn("No such SQL database: {}", connectionName); + return std::unique_ptr{new TSqlDatabase{TSqlDatabase{}}}; } + + return std::unique_ptr{new TSqlDatabase{QSqlDatabase::database(connectionName)}}; } -TSqlDatabase &TSqlDatabase::addDatabase(const QString &driver, const QString &connectionName) +bool TSqlDatabase::addDatabase(const QString &driver, const QString &connectionName) { - TSqlDatabase db(QSqlDatabase::addDatabase(driver, connectionName)); auto *dict = dbDict(); QWriteLocker locker(&dict->lock); - if (dict->contains(connectionName)) { - dict->take(connectionName); + if (!dict->contains(connectionName)) { + auto db = QSqlDatabase::addDatabase(driver, connectionName); + if (db.isValid()) { + dict->insert(connectionName); + } else { + tSystemError("TSqlDatabase::addDatabase error. driver:{} name:{}", driver, connectionName); + return false; + } } - - dict->insert(connectionName, db); - return (*dict)[connectionName]; + return true; } @@ -65,8 +96,9 @@ void TSqlDatabase::removeDatabase(const QString &connectionName) { auto *dict = dbDict(); QWriteLocker locker(&dict->lock); - dict->take(connectionName); - QSqlDatabase::removeDatabase(connectionName); + if (dict->remove(connectionName)) { + QSqlDatabase::removeDatabase(connectionName); + } } @@ -88,3 +120,23 @@ bool TSqlDatabase::isPreparedStatementSupported() const { return _driverExtension && _driverExtension->isPreparedStatementSupported(); } + + +TSqlDatabase::Handle::~Handle() +{ + if (_dbptr) { + TSqlDatabasePool::instance()->pool(std::move(_dbptr)); + } +} + + +TSqlDatabase::Handle &TSqlDatabase::Handle::operator=(TSqlDatabase::Handle &&other) noexcept +{ + if (this != &other) { + if (_dbptr) { + TSqlDatabasePool::instance()->pool(std::move(_dbptr)); + } + _dbptr = std::move(other._dbptr); + } + return *this; +} diff --git a/src/tsqldatabase.h b/src/tsqldatabase.h index d2b0dbd09..330495304 100644 --- a/src/tsqldatabase.h +++ b/src/tsqldatabase.h @@ -1,14 +1,18 @@ #pragma once +#include +#include #include #include #include -#include + class TSqlDriverExtension; class T_CORE_EXPORT TSqlDatabase { public: + using SqlDbPtr = std::unique_ptr; + enum DbmsType { UnknownDbms = QSqlDriver::UnknownDbms, MSSqlServer = QSqlDriver::MSSqlServer, @@ -21,10 +25,29 @@ class T_CORE_EXPORT TSqlDatabase { DB2 = QSqlDriver::DB2 }; - explicit TSqlDatabase(const QSqlDatabase &database = QSqlDatabase()); - TSqlDatabase(const TSqlDatabase &other); - ~TSqlDatabase() { } - TSqlDatabase &operator=(const TSqlDatabase &other); + // TKvsDatabase handle + class Handle { + public: + Handle() = default; + Handle(SqlDbPtr ptr) : _dbptr(std::move(ptr)) {} + Handle(const Handle &) = delete; + Handle &operator=(const Handle &) = delete; + Handle(Handle &&other) noexcept = default; + Handle &operator=(Handle &&other) noexcept; + ~Handle(); + + TSqlDatabase *operator->() { return _dbptr.get(); } + TSqlDatabase &operator*() { return *(_dbptr.get()); } + explicit operator bool() const noexcept { return static_cast(_dbptr); } + + private: + SqlDbPtr _dbptr; + }; + + + ~TSqlDatabase(); + TSqlDatabase(TSqlDatabase &&other); + TSqlDatabase &operator=(TSqlDatabase &&other); DbmsType dbmsType() const; bool isValid() const { return _sqlDatabase.isValid(); } @@ -37,46 +60,25 @@ class T_CORE_EXPORT TSqlDatabase { void setUpsertEnabled(bool enable) { _enableUpsert = enable; } bool isUpsertSupported() const; bool isPreparedStatementSupported() const; - const TSqlDriverExtension *driverExtension() const { return _driverExtension; } + const TSqlDriverExtension *driverExtension() const { return _driverExtension.get(); } static const char *const defaultConnection; - static TSqlDatabase &database(const QString &connectionName = QLatin1String(defaultConnection)); - static TSqlDatabase &addDatabase(const QString &driver, const QString &connectionName = QLatin1String(defaultConnection)); + static std::unique_ptr database(const QString &connectionName = QLatin1String(defaultConnection)); + static bool addDatabase(const QString &driver, const QString &connectionName = QLatin1String(defaultConnection)); static void removeDatabase(const QString &connectionName = QLatin1String(defaultConnection)); static bool contains(const QString &connectionName = QLatin1String(defaultConnection)); private: - TSqlDriverExtension *driverExtension() { return _driverExtension; } - void setDriverExtension(TSqlDriverExtension *extension); + explicit TSqlDatabase(const QSqlDatabase &database = QSqlDatabase()); + TSqlDriverExtension *driverExtension() { return _driverExtension.get(); } + void setDriverExtension(std::unique_ptr extension); QSqlDatabase _sqlDatabase; QStringList _postOpenStatements; bool _enableUpsert {false}; - TSqlDriverExtension *_driverExtension {nullptr}; + std::unique_ptr _driverExtension; friend class TSqlDatabasePool; + friend class TSqlObject; + T_DISABLE_COPY(TSqlDatabase) }; - - -inline TSqlDatabase::TSqlDatabase(const QSqlDatabase &database) : - _sqlDatabase(database) -{ -} - -inline TSqlDatabase::TSqlDatabase(const TSqlDatabase &other) : - _sqlDatabase(other._sqlDatabase), - _postOpenStatements(other._postOpenStatements), - _enableUpsert(other._enableUpsert), - _driverExtension(other._driverExtension) -{ -} - -inline TSqlDatabase &TSqlDatabase::operator=(const TSqlDatabase &other) -{ - _sqlDatabase = other._sqlDatabase; - _postOpenStatements = other._postOpenStatements; - _enableUpsert = other._enableUpsert; - _driverExtension = other._driverExtension; - return *this; -} - diff --git a/src/tsqldatabasepool.cpp b/src/tsqldatabasepool.cpp index 3d1f33243..b228a6c5d 100644 --- a/src/tsqldatabasepool.cpp +++ b/src/tsqldatabasepool.cpp @@ -8,8 +8,8 @@ #include "tsqldatabasepool.h" #include "tsqldatabase.h" #include "tsqldriverextensionfactory.h" +#include "tsqldriverextension.h" #include "tsystemglobal.h" -#include "tstack.h" #include #include #include @@ -24,13 +24,13 @@ constexpr auto CONN_NAME_FORMAT = "rdb%02d_%d"; TSqlDatabasePool *TSqlDatabasePool::instance() { - static TSqlDatabasePool *databasePool = []() { - auto *pool = new TSqlDatabasePool; + static std::unique_ptr databasePool = []() { + std::unique_ptr pool{new TSqlDatabasePool}; pool->maxConnects = Tf::app()->maxNumberOfThreadsPerAppServer(); pool->init(); return pool; }(); - return databasePool; + return databasePool.get(); } @@ -42,29 +42,30 @@ TSqlDatabasePool::TSqlDatabasePool() : TSqlDatabasePool::~TSqlDatabasePool() { - QMutexLocker locker(&_mutex); timer.stop(); for (int j = 0; j < Tf::app()->sqlDatabaseSettingsCount(); ++j) { - auto &cache = cachedDatabase[j]; + auto &cache = cachedDatabases[j]; QString name; - while (!cache.isEmpty()) { - name = cache.pop(); - auto &db = TSqlDatabase::database(name); - closeDatabase(db); - TSqlDatabase::removeDatabase(name); + while (!cache.empty()) { + std::optional db = cache.pop(); + if (db) { + name = (*db)->connectionName(); + closeDatabase(*(*db)); + TSqlDatabase::removeDatabase(name); + } } - auto &stack = availableNames[j]; - while (!stack.isEmpty()) { - name = stack.pop(); - TSqlDatabase::removeDatabase(name); + auto &stack = availableDatabases[j]; + while (!stack.empty()) { + std::optional db = stack.pop(); + if (db) { + TSqlDatabase::removeDatabase((*db)->sqlDatabase().connectionName()); + } } } - delete[] cachedDatabase; delete[] lastCachedTime; - delete[] availableNames; } @@ -88,33 +89,44 @@ void TSqlDatabasePool::init() return; } - QMutexLocker locker(&_mutex); + //QMutexLocker locker(&_mutex); + + if (lastCachedTime) { + return; + } - cachedDatabase = new QStack[Tf::app()->sqlDatabaseSettingsCount()]; - lastCachedTime = new TAtomic[Tf::app()->sqlDatabaseSettingsCount()]; - availableNames = new QStack[Tf::app()->sqlDatabaseSettingsCount()]; + const int dbcount = Tf::app()->sqlDatabaseSettingsCount(); + cachedDatabases.resize(dbcount); + availableDatabases.resize(dbcount); + lastCachedTime = new TAtomic[dbcount]; + tSystemDebug("SQL database available. maxConnects:{}", maxConnects); bool aval = false; - tSystemDebug("SQL database available"); // Adds databases previously - for (int j = 0; j < Tf::app()->sqlDatabaseSettingsCount(); ++j) { + for (int j = 0; j < dbcount; ++j) { QString type = driverType(j); if (type.isEmpty()) { continue; } aval = true; - auto &stack = availableNames[j]; + auto &stack = availableDatabases[j]; for (int i = 0; i < maxConnects; ++i) { - TSqlDatabase &db = TSqlDatabase::addDatabase(type, QString::asprintf(CONN_NAME_FORMAT, j, i)); - if (!db.isValid()) { + QString name = QString::asprintf(CONN_NAME_FORMAT, j, i); + bool res = TSqlDatabase::addDatabase(type, name); + if (!res) { + Tf::warn("Parameter 'DriverType' is invalid"); + continue; + } + auto db = TSqlDatabase::database(name); + if (!db->isValid()) { Tf::warn("Parameter 'DriverType' is invalid"); break; } - setDatabaseSettings(db, j); - stack.push(db.connectionName()); // push onto stack - tSystemDebug("Add Database successfully. name:{}", db.connectionName()); + setDatabaseSettings(*db, j); + tSystemDebug("Add Database successfully. name:{}", db->connectionName()); + stack.push(std::move(db)); // push onto stack } } @@ -125,55 +137,56 @@ void TSqlDatabasePool::init() } -QSqlDatabase TSqlDatabasePool::database(int databaseId) +TSqlDatabase::Handle TSqlDatabasePool::database(int databaseId) { - QMutexLocker locker(&_mutex); + if (databaseId < 0 || databaseId >= Tf::app()->sqlDatabaseSettingsCount()) { + throw RuntimeException("No pooled connection", __FILE__, __LINE__); + } - if (Q_LIKELY(databaseId >= 0 && databaseId < Tf::app()->sqlDatabaseSettingsCount())) { - auto &cache = cachedDatabase[databaseId]; - auto &stack = availableNames[databaseId]; + auto &cache = cachedDatabases[databaseId]; + auto &stack = availableDatabases[databaseId]; - for (;;) { - QString name; - if (!cache.isEmpty()) { - name = cache.pop(); - const auto &tdb = TSqlDatabase::database(name); - if (Q_LIKELY(tdb.sqlDatabase().isOpen())) { - tSystemDebug("Gets cached database: {}", tdb.connectionName()); - return tdb.sqlDatabase(); - } else { - tSystemError("Pooled database is not open: {} [{}:{}]", tdb.connectionName(), __FILE__, __LINE__); - stack.push(name); - continue; - } + while (true) { + std::optional sqlptr = cache.pop(); + if (sqlptr) { + if ((*sqlptr)->sqlDatabase().isOpen()) { + tSystemDebug("Gets cached database: {}", (*sqlptr)->connectionName()); + return TSqlDatabase::Handle(std::move((*sqlptr))); + } else { + tSystemError("Pooled database is not open: {} [{}:{}]", (*sqlptr)->connectionName(), __FILE__, __LINE__); + stack.push(std::move(*sqlptr)); + continue; } + } - if (Q_LIKELY(!stack.isEmpty())) { - name = stack.pop(); - auto &tdb = TSqlDatabase::database(name); - if (Q_UNLIKELY(!openDatabase(tdb))) { - Tf::error("Database open error. Invalid database settings, or maximum number of SQL connection exceeded."); - tSystemError("SQL database open error: {}", tdb.sqlDatabase().connectionName()); - stack.push(name); - return QSqlDatabase(); - } + sqlptr = stack.pop(); + if (!sqlptr) { + break; + } - tSystemDebug("SQL database opened successfully (env:{})", Tf::app()->databaseEnvironment()); - tSystemDebug("Gets database: {}", tdb.sqlDatabase().connectionName()); + if (!openDatabase(*(*sqlptr))) { + Tf::error("Database open error. Invalid database settings, or maximum number of SQL connection exceeded."); + tSystemError("SQL database open error: {}", (*sqlptr)->sqlDatabase().connectionName()); + stack.push(std::move(*sqlptr)); + return TSqlDatabase::Handle(); + } - // Executes setup-queries - if (!tdb.postOpenStatements().isEmpty()) { - TSqlQuery query(tdb.sqlDatabase()); - for (QString st : tdb.postOpenStatements()) { - st = st.trimmed(); - query.exec(st); - } - } - return tdb.sqlDatabase(); + tSystemDebug("SQL database opened successfully (env:{})", Tf::app()->databaseEnvironment()); + tSystemDebug("Gets database: {}", (*sqlptr)->sqlDatabase().connectionName()); + + // Executes setup-queries + if (!(*sqlptr)->postOpenStatements().isEmpty()) { + TSqlQuery query((*sqlptr)->sqlDatabase()); + for (QString st : (*sqlptr)->postOpenStatements()) { + st = st.trimmed(); + query.exec(st); } } + return TSqlDatabase::Handle(std::move(*sqlptr)); } - throw RuntimeException("No pooled connection", __FILE__, __LINE__); + + tSystemError("Empty available databases [{}:{}]", __FILE__, __LINE__); + return TSqlDatabase::Handle(); } @@ -243,43 +256,43 @@ bool TSqlDatabasePool::setDatabaseSettings(TSqlDatabase &database, int databaseI } -void TSqlDatabasePool::pool(QSqlDatabase &database, bool forceClose) +void TSqlDatabasePool::pool(SqlDbPtr dbptr, bool forceClose) { - QMutexLocker locker(&_mutex); + //QMutexLocker locker(&_mutex); - if (database.isValid()) { - int databaseId = getDatabaseId(database); + if (dbptr->isValid()) { + int databaseId = getDatabaseId(dbptr->sqlDatabase()); if (databaseId >= 0 && databaseId < Tf::app()->sqlDatabaseSettingsCount()) { if (forceClose) { - tSystemWarn("Force close database: {}", database.connectionName()); - TSqlDatabase &db = TSqlDatabase::database(database.connectionName()); - closeDatabase(db); + tSystemWarn("Force close database: {}", dbptr->connectionName()); + closeDatabase(*dbptr); + availableDatabases[databaseId].push(std::move(dbptr)); } else { - if (database.isOpen()) { + if (dbptr->sqlDatabase().isOpen()) { // pool - cachedDatabase[databaseId].push(database.connectionName()); + tSystemDebug("Pooled cached connection, name: {}", dbptr->connectionName()); + cachedDatabases[databaseId].push(std::move(dbptr)); lastCachedTime[databaseId].store((uint)std::time(nullptr)); - tSystemDebug("Pooled database: {}", database.connectionName()); } else { - tSystemWarn("Closed SQL database connection, name: {}", database.connectionName()); - availableNames[databaseId].push(database.connectionName()); + tSystemWarn("Pooled database, name: {}", dbptr->connectionName()); + availableDatabases[databaseId].push(std::move(dbptr)); } } } else { tSystemError("Pooled invalid database [{}:{}]", __FILE__, __LINE__); } } - database = QSqlDatabase(); // Sets an invalid object } void TSqlDatabasePool::timerEvent(QTimerEvent *event) { if (event->timerId() == timer.timerId()) { - QMutexLocker locker(&_mutex); + //QMutexLocker locker(&_mutex); QString name; +#if 0 // Closes extra-connection for (int i = 0; i < Tf::app()->sqlDatabaseSettingsCount(); ++i) { auto &cache = cachedDatabase[i]; @@ -294,6 +307,24 @@ void TSqlDatabasePool::timerEvent(QTimerEvent *event) closeDatabase(db); } } +#else + // Closes extra-connection + for (int i = 0; i < Tf::app()->sqlDatabaseSettingsCount(); ++i) { + auto &cache = cachedDatabases[i]; + if (cache.size() == 0) { + continue; + } + + while (lastCachedTime[i].load() < (uint)std::time(nullptr) - 30 + && !cache.empty()) { + auto db = cache.pop(); + if (db) { + closeDatabase(*(*db)); + availableDatabases[i].push(std::move(*db)); + } + } + } +#endif } else { QObject::timerEvent(event); } @@ -311,7 +342,7 @@ bool TSqlDatabasePool::openDatabase(TSqlDatabase &database) } extension = TSqlDriverExtensionFactory::create(database.sqlDatabase().driverName(), database.sqlDatabase().driver()); - database.setDriverExtension(extension); + database.setDriverExtension(std::unique_ptr{extension}); } return ret; @@ -320,10 +351,9 @@ bool TSqlDatabasePool::openDatabase(TSqlDatabase &database) void TSqlDatabasePool::closeDatabase(TSqlDatabase &database) { - QMutexLocker locker(&_mutex); + //QMutexLocker locker(&_mutex); QSqlDatabase db = database.sqlDatabase(); - int id = getDatabaseId(db); QString name = db.connectionName(); db.close(); @@ -331,19 +361,23 @@ void TSqlDatabasePool::closeDatabase(TSqlDatabase &database) if (extension) { TSqlDriverExtensionFactory::destroy(database.sqlDatabase().driverName(), extension); } - database.setDriverExtension(nullptr); + database.setDriverExtension(std::unique_ptr{nullptr}); tSystemDebug("Closed database connection, name: {}", name); - availableNames[id].push(name); } int TSqlDatabasePool::getDatabaseId(const QSqlDatabase &database) { - bool ok; - int id = database.connectionName().mid(3, 2).toInt(&ok); + return databaseIdFromName(database.connectionName()); +} + - if (Q_LIKELY(ok && id >= 0)) { +int TSqlDatabasePool::databaseIdFromName(const QString &name) +{ + bool ok; + int id = name.mid(3, 2).toInt(&ok); + if (ok && id >= 0) { return id; } return -1; diff --git a/src/tsqldatabasepool.h b/src/tsqldatabasepool.h index a20027531..71e3f7f03 100644 --- a/src/tsqldatabasepool.h +++ b/src/tsqldatabasepool.h @@ -1,27 +1,31 @@ #pragma once #include #include +#include +#include #include #include #include #include #include #include - -class TSqlDatabase; -template class TStack; +#include +#include +#include class T_CORE_EXPORT TSqlDatabasePool : public QObject { Q_OBJECT public: + using SqlDbPtr = std::unique_ptr; + ~TSqlDatabasePool(); - QSqlDatabase database(int databaseId = 0); - void pool(QSqlDatabase &database, bool forceClose = false); + TSqlDatabase::Handle database(int databaseId = 0); static TSqlDatabasePool *instance(); static bool setDatabaseSettings(TSqlDatabase &database, int databaseId); static int getDatabaseId(const QSqlDatabase &database); + static int databaseIdFromName(const QString &name); protected: void init(); @@ -30,15 +34,16 @@ class T_CORE_EXPORT TSqlDatabasePool : public QObject { private: bool openDatabase(TSqlDatabase &database); void closeDatabase(TSqlDatabase &database); + void pool(SqlDbPtr dbptr, bool forceClose = false); TSqlDatabasePool(); - mutable QRecursiveMutex _mutex; - QStack *cachedDatabase {nullptr}; TAtomic *lastCachedTime {nullptr}; - QStack *availableNames {nullptr}; int maxConnects {0}; QBasicTimer timer; + std::vector> availableDatabases; + std::vector> cachedDatabases; T_DISABLE_COPY(TSqlDatabasePool) T_DISABLE_MOVE(TSqlDatabasePool) + friend class TSqlDatabase; }; diff --git a/src/tsqlobject.cpp b/src/tsqlobject.cpp index f3a6c0b04..959d3f616 100644 --- a/src/tsqlobject.cpp +++ b/src/tsqlobject.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include const QByteArray LockRevision("lock_revision"); @@ -157,7 +158,7 @@ bool TSqlObject::create() values.reserve(255); ins += QLatin1String("INSERT INTO "); - ins += TSqlQuery::escapeIdentifier(tableName(), QSqlDriver::TableName, database.driver()); + ins += TSqlQuery::escapeIdentifier(tableName(), QSqlDriver::TableName, database.sqlDatabase().driver()); ins += QLatin1String(" ("); int autoidx = metaObject()->propertyOffset() + autoValueIndex(); @@ -167,9 +168,9 @@ bool TSqlObject::create() QVariant val = QObject::property(propName); if (i != autoidx) { - ins += TSqlQuery::escapeIdentifier(QLatin1String(propName), QSqlDriver::FieldName, database.driver()); + ins += TSqlQuery::escapeIdentifier(QLatin1String(propName), QSqlDriver::FieldName, database.sqlDatabase().driver()); ins += QLatin1Char(','); - values += TSqlQuery::formatValue(val, metaProp.metaType(), database); + values += TSqlQuery::formatValue(val, metaProp.metaType(), database.sqlDatabase()); values += QLatin1Char(','); } } @@ -180,7 +181,7 @@ bool TSqlObject::create() ins += values; ins += QLatin1Char(')'); - TSqlQuery query(database); + TSqlQuery query(database.sqlDatabase()); bool ret = query.exec(ins); sqlError = query.lastError(); if (Q_LIKELY(ret)) { @@ -188,7 +189,7 @@ bool TSqlObject::create() if (autoValueIndex() >= 0) { QVariant lastid = query.lastInsertId(); - if (!lastid.isValid() && database.driver()->dbmsType() == QSqlDriver::PostgreSQL) { + if (!lastid.isValid() && database.sqlDatabase().driver()->dbmsType() == QSqlDriver::PostgreSQL) { // For PostgreSQL without OIDS ret = query.exec(QStringLiteral("SELECT LASTVAL()")); sqlError = query.lastError(); @@ -251,7 +252,7 @@ bool TSqlObject::update() revIndex = i; where.append(QLatin1String(propName)); - where.append(QLatin1Char('=')).append(TSqlQuery::formatValue(oldRevision, intMetaType, database)); + where.append(QLatin1Char('=')).append(TSqlQuery::formatValue(oldRevision, intMetaType, database.sqlDatabase())); where.append(QLatin1String(" AND ")); } else { // continue @@ -261,7 +262,7 @@ bool TSqlObject::update() QString upd; // UPDATE Statement upd.reserve(512); upd.append(QLatin1String("UPDATE ")); - upd.append(TSqlQuery::escapeIdentifier(tableName(), QSqlDriver::TableName, database.driver())); + upd.append(TSqlQuery::escapeIdentifier(tableName(), QSqlDriver::TableName, database.sqlDatabase().driver())); upd.append(QLatin1String(" SET ")); int pkidx = metaObject()->propertyOffset() + primaryKeyIndex(); @@ -277,7 +278,7 @@ bool TSqlObject::update() auto pkType = metaProp.metaType(); QVariant origpkval = value(pkName); where.append(QLatin1String(pkName)); - where.append(QLatin1Char('=')).append(TSqlQuery::formatValue(origpkval, pkType, database)); + where.append(QLatin1Char('=')).append(TSqlQuery::formatValue(origpkval, pkType, database.sqlDatabase())); // Restore the value of primary key QObject::setProperty(pkName, origpkval); @@ -287,9 +288,9 @@ bool TSqlObject::update() QVariant newval = QObject::property(propName); QVariant recval = QSqlRecord::value(QLatin1String(propName)); if (i != pkidx && recval.isValid() && recval != newval) { - upd.append(TSqlQuery::escapeIdentifier(QLatin1String(propName), QSqlDriver::FieldName, database.driver())); + upd.append(TSqlQuery::escapeIdentifier(QLatin1String(propName), QSqlDriver::FieldName, database.sqlDatabase().driver())); upd.append(QLatin1Char('=')); - upd.append(TSqlQuery::formatValue(newval, metaProp.metaType(), database)); + upd.append(TSqlQuery::formatValue(newval, metaProp.metaType(), database.sqlDatabase())); upd.append(QLatin1Char(',')); } } @@ -303,7 +304,7 @@ bool TSqlObject::update() syncToSqlRecord(); upd.append(where); - TSqlQuery query(database); + TSqlQuery query(database.sqlDatabase()); bool ret = query.exec(upd); sqlError = query.lastError(); if (ret) { @@ -324,8 +325,7 @@ bool TSqlObject::update() */ bool TSqlObject::save() { - auto &sqldb = getDatabase(); - const auto &db = TSqlDatabase::database(sqldb.connectionName()); + TSqlDatabase &db = getDatabase(); QString lockrev; if (!db.isUpsertSupported() || !db.isUpsertEnabled()) { @@ -374,7 +374,7 @@ bool TSqlObject::save() return (isNew()) ? create() : update(); } - TSqlQuery query(sqldb); + TSqlQuery query(db.sqlDatabase()); bool ret = query.exec(upst); sqlError = query.lastError(); if (ret) { @@ -402,7 +402,7 @@ bool TSqlObject::remove() return false; } - QSqlDatabase &database = getDatabase(); + QSqlDatabase &database = getDatabase().sqlDatabase(); QString del = database.driver()->sqlStatement(QSqlDriver::DeleteStatement, tableName(), *static_cast(this), false); if (del.isEmpty()) { sqlError = QSqlError(QLatin1String("Unable to delete row"), @@ -524,8 +524,8 @@ void TSqlObject::syncToObject() */ void TSqlObject::syncToSqlRecord() { - auto &db = getDatabase(); - QSqlRecord::operator=(db.record(tableName())); + TSqlDatabase &db = getDatabase(); + QSqlRecord::operator=(db.sqlDatabase().record(tableName())); const QMetaObject *metaObj = metaObject(); for (int i = metaObj->propertyOffset(); i < metaObj->propertyCount(); ++i) { const char *propName = metaObj->property(i).name(); @@ -539,10 +539,7 @@ void TSqlObject::syncToSqlRecord() } -QSqlDatabase &TSqlObject::getDatabase() +TSqlDatabase &TSqlObject::getDatabase() { - if (!_database.isValid()) { - _database = Tf::currentSqlDatabase(databaseId()); - } - return _database; + return Tf::currentSqlDatabase(databaseId()); } diff --git a/src/tsqlobject.h b/src/tsqlobject.h index 21fc3558a..ef56f3504 100644 --- a/src/tsqlobject.h +++ b/src/tsqlobject.h @@ -7,6 +7,7 @@ #include #include #include +#include #include @@ -37,10 +38,11 @@ class T_CORE_EXPORT TSqlObject : public TModelObject, public QSqlRecord { protected: void syncToSqlRecord(); void syncToObject(); - QSqlDatabase &getDatabase(); + TSqlDatabase &getDatabase(); QSqlError sqlError; private: - QSqlDatabase _database; + //QSqlDatabase _database; + //TSqlDatabase _database; これに変えたい }; diff --git a/src/tsqlormapper.h b/src/tsqlormapper.h index 4f45ca554..c13084070 100644 --- a/src/tsqlormapper.h +++ b/src/tsqlormapper.h @@ -166,7 +166,7 @@ class TSqlORMapper : public TAbstractSqlORMapper { */ template inline TSqlORMapper::TSqlORMapper() : - TAbstractSqlORMapper(Tf::currentSqlDatabase(T().databaseId())) + TAbstractSqlORMapper(Tf::currentSqlDatabase(T().databaseId()).sqlDatabase()) { setTable(T().tableName()); } diff --git a/src/tsqlquery.cpp b/src/tsqlquery.cpp index 76d0e5b11..170bb25cd 100644 --- a/src/tsqlquery.cpp +++ b/src/tsqlquery.cpp @@ -7,6 +7,7 @@ #include "tsystemglobal.h" #include "tsqldatabase.h" +#include "tsqldatabasepool.h" #include "tsqldriverextension.h" #include #include @@ -31,16 +32,15 @@ QMutex cacheMutex; Constructs a TSqlQuery object using the database \a databaseId. */ TSqlQuery::TSqlQuery(int databaseId) : - QSqlQuery(QString(), Tf::currentSqlDatabase(databaseId)) + QSqlQuery(QString(), Tf::currentSqlDatabase(databaseId).sqlDatabase()), _databaseId(databaseId) { - _connectionName = Tf::currentSqlDatabase(databaseId).connectionName(); } TSqlQuery::TSqlQuery(const QSqlDatabase &db) : QSqlQuery(db) { - _connectionName = db.connectionName(); + _databaseId = TSqlDatabasePool::databaseIdFromName(db.connectionName()); } @@ -101,7 +101,7 @@ void TSqlQuery::clearCachedQueries() */ QString TSqlQuery::escapeIdentifier(const QString &identifier, QSqlDriver::IdentifierType type, int databaseId) { - return escapeIdentifier(identifier, type, Tf::currentSqlDatabase(databaseId).driver()); + return escapeIdentifier(identifier, type, Tf::currentSqlDatabase(databaseId).sqlDatabase().driver()); } /*! @@ -124,7 +124,7 @@ QString TSqlQuery::escapeIdentifier(const QString &identifier, QSqlDriver::Ident */ QString TSqlQuery::formatValue(const QVariant &val, const QMetaType &type, int databaseId) { - return formatValue(val, type, Tf::currentSqlDatabase(databaseId).driver()); + return formatValue(val, type, Tf::currentSqlDatabase(databaseId).sqlDatabase().driver()); } /*! @@ -175,7 +175,8 @@ TSqlQuery &TSqlQuery::prepare(const QString &query) { QElapsedTimer time; time.start(); - const auto &db = TSqlDatabase::database(_connectionName); + + const auto &db = Tf::currentSqlDatabase(_databaseId); bool res = false; if (db.isPreparedStatementSupported()) { @@ -215,7 +216,7 @@ bool TSqlQuery::exec() bool ret = false; QElapsedTimer time; time.start(); - const auto &db = TSqlDatabase::database(_connectionName); + const auto &db = Tf::currentSqlDatabase(_databaseId); if (db.isPreparedStatementSupported()) { QString statement = db.driverExtension()->executeStatement(_boundValues); @@ -251,7 +252,7 @@ bool TSqlQuery::exec() */ TSqlQuery &TSqlQuery::bind(const QString &placeholder, const QVariant &val) { - const auto &db = TSqlDatabase::database(_connectionName); + const auto &db = Tf::currentSqlDatabase(_databaseId); if (db.isPreparedStatementSupported()) { Tf::error("Not supported colon-name placeholder of prepared statement for the database"); @@ -267,7 +268,7 @@ TSqlQuery &TSqlQuery::bind(const QString &placeholder, const QVariant &val) */ TSqlQuery &TSqlQuery::bind(int pos, const QVariant &val) { - const auto &db = TSqlDatabase::database(_connectionName); + const auto &db = Tf::currentSqlDatabase(_databaseId); if (pos < 0) { return *this; @@ -297,7 +298,7 @@ TSqlQuery &TSqlQuery::bind(int pos, const QVariant &val) */ TSqlQuery &TSqlQuery::addBind(const QVariant &val) { - const auto &db = TSqlDatabase::database(_connectionName); + const auto &db = Tf::currentSqlDatabase(_databaseId); if (db.isPreparedStatementSupported()) { _boundValues.append(val); @@ -310,14 +311,14 @@ TSqlQuery &TSqlQuery::addBind(const QVariant &val) QVariant TSqlQuery::boundValue(int pos) const { - const auto &db = TSqlDatabase::database(_connectionName); + const auto &db = Tf::currentSqlDatabase(_databaseId); return (db.isPreparedStatementSupported()) ? _boundValues.value(pos) : QSqlQuery::boundValue(pos); } QVariantList TSqlQuery::boundValues() const { - const auto &db = TSqlDatabase::database(_connectionName); + const auto &db = Tf::currentSqlDatabase(_databaseId); if (db.isPreparedStatementSupported()) { return _boundValues; } else { diff --git a/src/tsqlquery.h b/src/tsqlquery.h index dafabef82..e38dd7429 100644 --- a/src/tsqlquery.h +++ b/src/tsqlquery.h @@ -36,7 +36,8 @@ class T_CORE_EXPORT TSqlQuery : public QSqlQuery { static QString formatValue(const QVariant &val, const QSqlDatabase &database) { return formatValue(val, database.driver()); } private: - QString _connectionName; + //QString _connectionName; + int _databaseId {0}; QVariantList _boundValues; // For prepared query }; diff --git a/src/tsqltransaction.cpp b/src/tsqltransaction.cpp index 74ef134eb..e9f803b38 100644 --- a/src/tsqltransaction.cpp +++ b/src/tsqltransaction.cpp @@ -30,8 +30,8 @@ TSqlTransaction::~TSqlTransaction() bool TSqlTransaction::begin() { - if (Q_UNLIKELY(!_database.isValid())) { - tSystemError("Can not begin transaction. Invalid database: {}", _database.connectionName()); + if (!_database->isValid()) { + tSystemError("Can not begin transaction. Invalid database: {}", _database->connectionName()); return false; } @@ -39,22 +39,22 @@ bool TSqlTransaction::begin() return true; } - if (!_database.driver()->hasFeature(QSqlDriver::Transactions)) { + if (!_database->sqlDatabase().driver()->hasFeature(QSqlDriver::Transactions)) { return true; } if (_active) { - tSystemDebug("Has begun transaction already. database:{}", _database.connectionName()); + tSystemDebug("Has begun transaction already. database:{}", _database->connectionName()); return true; } QElapsedTimer time; time.start(); - _active = _database.transaction(); - _connectionName = _database.connectionName(); - int id = TSqlDatabasePool::getDatabaseId(_database); - if (Q_LIKELY(_active)) { + _active = _database->sqlDatabase().transaction(); + _connectionName = _database->sqlDatabase().connectionName(); + int id = TSqlDatabasePool::getDatabaseId(_database->sqlDatabase()); + if (_active) { Tf::traceQueryLog(time.elapsed(), "[BEGIN] [databaseId:{}] {}", id, _connectionName); } else { Tf::traceQueryLog(time.elapsed(), "[BEGIN Failed] [databaseId:{}] {}", id, _connectionName); @@ -72,19 +72,19 @@ bool TSqlTransaction::commit() } if (_active) { - if (!_database.isValid()) { + if (!_database->sqlDatabase().isValid()) { tSystemWarn("Database is invalid. [{}] [{}:{}]", _connectionName, __FILE__, __LINE__); } else { QElapsedTimer time; time.start(); - res = _database.commit(); - int id = TSqlDatabasePool::getDatabaseId(_database); + res = _database->sqlDatabase().commit(); + int id = TSqlDatabasePool::getDatabaseId(_database->sqlDatabase()); if (Q_LIKELY(res)) { - Tf::traceQueryLog(time.elapsed(), "[COMMIT] [databaseId:{}] {}", id, qUtf8Printable(_database.connectionName())); + Tf::traceQueryLog(time.elapsed(), "[COMMIT] [databaseId:{}] {}", id, qUtf8Printable(_database->connectionName())); } else { - Tf::traceQueryLog(time.elapsed(), "[COMMIT Failed] [databaseId:{}] {}", id, qUtf8Printable(_database.connectionName())); + Tf::traceQueryLog(time.elapsed(), "[COMMIT Failed] [databaseId:{}] {}", id, qUtf8Printable(_database->connectionName())); } } } @@ -103,19 +103,19 @@ bool TSqlTransaction::rollback() } if (_active) { - if (!_database.isValid()) { + if (!_database->isValid()) { tSystemWarn("Database is invalid. [{}] [{}:{}]", _connectionName, __FILE__, __LINE__); } else { QElapsedTimer time; time.start(); - res = _database.rollback(); - int id = TSqlDatabasePool::getDatabaseId(_database); + res = _database->sqlDatabase().rollback(); + int id = TSqlDatabasePool::getDatabaseId(_database->sqlDatabase()); if (Q_LIKELY(res)) { - Tf::traceQueryLog(time.elapsed(), "[ROLLBACK] [databaseId:{}] {}", id, qUtf8Printable(_database.connectionName())); + Tf::traceQueryLog(time.elapsed(), "[ROLLBACK] [databaseId:{}] {}", id, qUtf8Printable(_database->connectionName())); } else { - Tf::traceQueryLog(time.elapsed(), "[ROLLBACK Failed] [databaseId:{}] {}", id, qUtf8Printable(_database.connectionName())); + Tf::traceQueryLog(time.elapsed(), "[ROLLBACK Failed] [databaseId:{}] {}", id, qUtf8Printable(_database->connectionName())); } } } diff --git a/src/tsqltransaction.h b/src/tsqltransaction.h index c423d4a8e..a69041079 100644 --- a/src/tsqltransaction.h +++ b/src/tsqltransaction.h @@ -10,11 +10,11 @@ class T_CORE_EXPORT TSqlTransaction { public: TSqlTransaction(); - TSqlTransaction(const TSqlTransaction &other) = default; ~TSqlTransaction(); + TSqlTransaction(TSqlTransaction &&) = default; + TSqlTransaction &operator=(TSqlTransaction &&) = default; - TSqlTransaction &operator=(const TSqlTransaction &) = default; - QSqlDatabase &database() { return _database; } + TSqlDatabase::Handle &database() { return _database; } bool begin(); bool commit(); bool rollback(); @@ -23,10 +23,12 @@ class T_CORE_EXPORT TSqlTransaction { void setDisabled(bool disable); private: - QSqlDatabase _database; + TSqlDatabase::Handle _database; bool _enabled {true}; bool _active {false}; QString _connectionName; + + T_DISABLE_COPY(TSqlTransaction) }; diff --git a/src/tstack.cpp b/src/tstack.cpp index 79835685e..e0a3b878c 100644 --- a/src/tstack.cpp +++ b/src/tstack.cpp @@ -1,13 +1,9 @@ #include "tstack.h" -#include - - -namespace { -QThreadStorage hzptrTls; -} +#include THazardPtr &Tf::hazardPtrForStack() { - return hzptrTls.localData(); + static thread_local THazardPtr hzptrTls; + return hzptrTls; } diff --git a/src/tstack.h b/src/tstack.h index 7d2b30474..6fa448201 100644 --- a/src/tstack.h +++ b/src/tstack.h @@ -11,35 +11,53 @@ T_CORE_EXPORT THazardPtr &hazardPtrForStack(); } +// +// Non-blocking move stack +// template class TStack { private: struct Node : public THazardObject { T value; Node *next {nullptr}; - Node(const T &v) : - value(v) { } - }; + Node(T &&v) : value(std::move(v)) { } + Node(const Node &) = delete; + Node &operator=(const Node &) = delete; + Node(Node &&) = delete; + Node &operator=(Node &&) = delete; + }; TAtomicPtr stkHead {nullptr}; + TAtomic counter {0}; public: - TStack() { } - void push(const T &val); - bool pop(T &val); - bool top(T &val); + TStack() = default; + TStack(const TStack &) = delete; + TStack &operator=(const TStack &) = delete; + TStack(TStack &&) noexcept; + TStack &operator=(TStack &&) = delete; + + void push(T val); + std::optional pop(); int count() const { return counter.load(); } - - T_DISABLE_COPY(TStack) - T_DISABLE_MOVE(TStack) + int size() const { return counter.load(); } + bool empty() const { return counter.load() == 0; } }; template -inline void TStack::push(const T &val) +inline TStack::TStack(TStack &&other) noexcept { - auto *pnode = new Node(val); + stkHead = other.stkHead; + counter.store(other.counter.load()); +} + + +template +inline void TStack::push(T val) +{ + auto *pnode = new Node(std::move(val)); do { pnode->next = stkHead.load(); } while (!stkHead.compareExchange(pnode->next, pnode)); @@ -48,9 +66,11 @@ inline void TStack::push(const T &val) template -inline bool TStack::pop(T &val) +inline std::optional TStack::pop() { + std::optional val; Node *pnode; + while ((pnode = Tf::hazardPtrForStack().guard(&stkHead))) { if (stkHead.compareExchange(pnode, pnode->next)) { break; @@ -59,25 +79,10 @@ inline bool TStack::pop(T &val) if (pnode) { counter--; - val = pnode->value; + val = std::move(pnode->value); pnode->next = nullptr; pnode->deleteLater(); } Tf::hazardPtrForStack().clear(); - return (bool)pnode; + return val; } - - -template -inline bool TStack::top(T &val) -{ - Node *pnode; - pnode = Tf::hazardPtrForStack().guard(&stkHead); - - if (pnode) { - val = pnode->value; - } - Tf::hazardPtrForStack().clear(); - return (bool)pnode; -} - diff --git a/src/tsystembus.cpp b/src/tsystembus.cpp index b4d7c5e1d..b31f5cb1b 100644 --- a/src/tsystembus.cpp +++ b/src/tsystembus.cpp @@ -22,12 +22,12 @@ constexpr auto SYSTEMBUS_DOMAIN_PREFIX = "treefrog_systembus_"; TSystemBus *TSystemBus::instance() { - static TSystemBus *systemBus = []() { - auto *bus = new TSystemBus(); + static std::unique_ptr systemBus = []() { + std::unique_ptr bus { new TSystemBus }; bus->connect(); return bus; }(); - return systemBus; + return systemBus.get(); } diff --git a/src/ttcpsocket.cpp b/src/ttcpsocket.cpp index b6d3584c7..8aa679a6a 100644 --- a/src/ttcpsocket.cpp +++ b/src/ttcpsocket.cpp @@ -57,9 +57,9 @@ bool TTcpSocket::waitUntil(bool (TEpollSocket::*method)(), int msecs) break; } - if (Tf::app()->multiProcessingModule() == TWebApplication::Epoll || epollMutex.tryLock()) { + if (Tf::app()->multiProcessingModule() == TWebApplication::MultiProcessingModule::Epoll || epollMutex.tryLock()) { int ret = _esocket->waitUntil(method, msecs); - if (Tf::app()->multiProcessingModule() != TWebApplication::Epoll) { + if (Tf::app()->multiProcessingModule() != TWebApplication::MultiProcessingModule::Epoll) { epollMutex.unlock(); } return ret; diff --git a/src/tthreadapplicationserver.cpp b/src/tthreadapplicationserver.cpp index efc58f627..f3b567c7f 100644 --- a/src/tthreadapplicationserver.cpp +++ b/src/tthreadapplicationserver.cpp @@ -17,10 +17,10 @@ an web application server for thread. */ -TStack *TThreadApplicationServer::threadPoolPtr() +TLockStack &TThreadApplicationServer::threadPoolPtr() { - static TStack threadPool; - return &threadPool; + static TLockStack threadPool; + return threadPool; } @@ -51,3 +51,18 @@ void TThreadApplicationServer::timerEvent(QTimerEvent *event) } } } + + +TThreadApplicationServer *TThreadApplicationServer::instance(int listeningSocket, QObject *parent) +{ + static std::unique_ptr instance; + static std::once_flag once; + + std::call_once(once, [&]() { + if (listeningSocket <= 0) { + throw StandardException("Invalid socket", __FILE__, __LINE__); + } + instance.reset(new TThreadApplicationServer(listeningSocket, parent)); + }); + return instance.get(); +} diff --git a/src/tthreadapplicationserver.h b/src/tthreadapplicationserver.h index bb02365b1..04b85d56d 100644 --- a/src/tthreadapplicationserver.h +++ b/src/tthreadapplicationserver.h @@ -1,5 +1,5 @@ #pragma once -#include "tstack.h" +#include #include #include #include @@ -25,7 +25,7 @@ class T_CORE_EXPORT TThreadApplicationServer : public QTcpServer, public TApplic void timerEvent(QTimerEvent *event) override; private: - static TStack *threadPoolPtr(); + static TLockStack *threadPoolPtr(); int listenSocket {0}; int maxThreads {0}; @@ -38,13 +38,13 @@ class T_CORE_EXPORT TThreadApplicationServer : public QTcpServer, public TApplic class T_CORE_EXPORT TThreadApplicationServer : public QThread, public TApplicationServerBase { Q_OBJECT public: - TThreadApplicationServer(int listeningSocket, QObject *parent = 0); ~TThreadApplicationServer() { } bool start(bool debugMode) override; void stop() override; void setAutoReloadingEnabled(bool enable) override; bool isAutoReloadingEnabled() override; + static TThreadApplicationServer *instance(int listeningSocket = 0,QObject *parent = nullptr); protected: void incomingConnection(qintptr socketDescriptor); @@ -52,7 +52,8 @@ class T_CORE_EXPORT TThreadApplicationServer : public QThread, public TApplicati void run() override; private: - static TStack *threadPoolPtr(); + static TLockStack &threadPoolPtr(); + TThreadApplicationServer(int listeningSocket, QObject *parent = nullptr); int listenSocket {0}; int maxThreads {0}; diff --git a/src/tthreadapplicationserver_linux.cpp b/src/tthreadapplicationserver_linux.cpp index 2413ff95c..4296b180c 100644 --- a/src/tthreadapplicationserver_linux.cpp +++ b/src/tthreadapplicationserver_linux.cpp @@ -33,9 +33,9 @@ TThreadApplicationServer::TThreadApplicationServer(int listeningSocket, QObject for (int i = 0; i < maxThreads; i++) { TActionThread *thread = new TActionThread(0); connect(thread, &TActionThread::finished, [=]() { - threadPoolPtr()->push(thread); + threadPoolPtr().push(thread); }); - threadPoolPtr()->push(thread); + threadPoolPtr().push(thread); } } @@ -108,16 +108,16 @@ void TThreadApplicationServer::run() int socketDescriptor = tf_accept4(listenSocket, nullptr, nullptr, (SOCK_CLOEXEC | SOCK_NONBLOCK)); if (socketDescriptor > 0) { tSystemDebug("incomingConnection sd:{} thread count:{} max:{}", socketDescriptor, TActionThread::threadCount(), maxThreads); - TActionThread *thread; + std::optional thread; - while (!threadPoolPtr()->pop(thread)) { + while (!(thread = threadPoolPtr().pop())) { std::this_thread::yield(); Tf::msleep(1); } - tSystemDebug("thread ptr: {}", (quint64)thread); - thread->setSocketDescriptor(socketDescriptor); - thread->start(); + tSystemDebug("thread ptr: {}", (quint64)*thread); + (*thread)->setSocketDescriptor(socketDescriptor); + (*thread)->start(); } } } diff --git a/src/tthreadapplicationserver_qt.cpp b/src/tthreadapplicationserver_qt.cpp index 5a8c2bfa9..219f78338 100644 --- a/src/tthreadapplicationserver_qt.cpp +++ b/src/tthreadapplicationserver_qt.cpp @@ -85,7 +85,9 @@ void TThreadApplicationServer::incomingConnection(qintptr socketDescriptor) { tSystemDebug("incomingConnection sd:{} thread count:{} max:{}", (qint64)socketDescriptor, TActionThread::threadCount(), maxThreads); TActionThread *thread; - while (!threadPoolPtr()->pop(thread)) { + bool ok; + + while (!((thread = threadPoolPtr().pop(&ok) && ok)) { std::this_thread::yield(); //qApp->processEvents(QEventLoop::ExcludeSocketNotifiers); Tf::msleep(1); diff --git a/src/tthreadpoolawaiter.cpp b/src/tthreadpoolawaiter.cpp new file mode 100644 index 000000000..d756c074b --- /dev/null +++ b/src/tthreadpoolawaiter.cpp @@ -0,0 +1 @@ +#include "tthreadpoolawaiter.h" diff --git a/src/tthreadpoolawaiter.h b/src/tthreadpoolawaiter.h new file mode 100644 index 000000000..8f63249f1 --- /dev/null +++ b/src/tthreadpoolawaiter.h @@ -0,0 +1,67 @@ +#pragma once +#include "turingserver.h" +#include +#include +#include +#include +#include +#include "tfcore.h" + + +template +class TThreadPoolAwaiter : public TAwaitBase { +public: + using FuncType = std::decay_t; + using ReturnType = std::invoke_result_t; + using Result = std::conditional_t, std::monostate, ReturnType>; + + TThreadPoolAwaiter(Func &&f) : + _func(std::forward(f)) + { + static std::once_flag once; + + std::call_once(once, []() { + // 最大何個まで? TODO TODO TODO TODO TODO TODO TODO TODO TODO + QThreadPool::globalInstance()->setMaxThreadCount(128); + }); + } + + ~TThreadPoolAwaiter() = default; + + bool await_ready() const noexcept { return false; } + + bool await_suspend(std::coroutine_handle handle) + { + _handle = handle; + QThreadPool::globalInstance()->start([this, handle] { + try { + if constexpr (std::is_void_v) { + _func(); + } else { + _result = _func(); + } + } catch (...) { + handle.promise().exptr = std::current_exception(); + } + + TUringServer::instance()->addResumeHandle(handle); + }); + return true; + } + + Result await_resume() + { + if (_handle.promise().exptr) { + std::rethrow_exception(_handle.promise().exptr); + } + + if constexpr (std::is_void_v) { + return std::monostate{}; + } + return std::move(_result); + } + +private: + FuncType _func; + Result _result; +}; diff --git a/src/turingcoroutine.h b/src/turingcoroutine.h new file mode 100644 index 000000000..f1cae253a --- /dev/null +++ b/src/turingcoroutine.h @@ -0,0 +1,18 @@ +#pragma once + +struct TUringTask; + + +class TUringCoroutine { +public: + explicit TUringCoroutine(int socketDescriptor) : + _sd(socketDescriptor) {} + virtual ~TUringCoroutine(); + + TUringTask start(); + +private: + int _sd {0}; + QByteArray _response; + QString _fileName; +}; diff --git a/src/turingcoroutine_linux.cpp b/src/turingcoroutine_linux.cpp new file mode 100644 index 000000000..1cf7eb719 --- /dev/null +++ b/src/turingcoroutine_linux.cpp @@ -0,0 +1,355 @@ +#include "turingcoroutine.h" +#include "turingserver.h" +#include "tthreadpoolawaiter.h" +#include "tactioncontextroutine.h" +#include "TSystemGlobal" +#include "TAppSettings" +#include "THttpRequest" +#include "TTemporaryFile" +#include +#include +#include +#include +#include + +constexpr uint READ_THRESHOLD_LENGTH = 4 * 1024 * 1024; // bytes + + +class AsyncRecv : public TAwaitBase { +public: + AsyncRecv(int sd, void* buffer, size_t length, int msecs) : + _sd(sd), _buf(buffer), _len(length), _msecs(msecs) { } + + void await_suspend(std::coroutine_handle handle) + { + _handle = handle; + if (TUringServer::instance()->addRecv(_sd, _buf, _len, _msecs, this) < 0) { + tSystemError("addRecv error: {}", strerror(errno)); + } + } + + inline int await_resume() + { + return _cqeres; + } + +private: + int _sd {0}; + void* _buf {nullptr}; + size_t _len {0}; + int _msecs {0}; +}; + + +class AsyncSend : public TAwaitBase { +public: + AsyncSend(int sd, const void *buf, size_t len) : + _sd(sd), _buf(buf), _len(len) { } + + void await_suspend(std::coroutine_handle handle) + { + _handle = handle; + int res = TUringServer::instance()->addSendZc(_sd, _buf, _len, this); + + if (res < 0) { + tSystemError("addSend error: {}", strerror(errno)); + } + } + + inline int await_resume() + { + tSystemDebug("await_resume : _len:{} _cqeflags:{} _cqeres:{}", _len, _cqeflags, _cqeres); + return (_cqeflags == IORING_CQE_F_NOTIF) ? _len : _cqeres; + } + +private: + int _sd {0}; + const void* _buf {nullptr}; + size_t _len {0}; +}; + + +class AsyncSendFile : public TAwaitBase { +public: + AsyncSendFile(int sd, int fd, int fileSize) : + _sd(sd), _fd(fd), _fileSize(fileSize) + { + if (::pipe2(_pipefd, O_CLOEXEC) < 0) { + tSystemError("pipe error: {}", strerror(errno)); + } + } + + ~AsyncSendFile() + { + if (_pipefd[0] > 0) { + tf_close(_pipefd[0]); + } + + if (_pipefd[1] > 0) { + tf_close(_pipefd[1]); + } + } + + void await_suspend(std::coroutine_handle handle) + { + _handle = handle; + iterate(); + } + + inline int await_resume() + { + //tSystemInfo("AsyncSendFile::await_resume : _offset:{} _cqeflags:{} _cqeres:{}", _offset, _cqeflags, _cqeres); + return (_cqeres < 0) ? _cqeres : _offset; + } + + bool completed() const override + { + //tSystemInfo("AsyncSendFile::completed : _offset:{} _cqeflags:{} _cqeres:{}", _offset, _cqeflags, _cqeres); + switch (_state) { + case State::WaitForPollOut: + if (_cqeres == POLLOUT) { + return (_offset + _cqeres >= _fileSize); + } else { + // POLLERR or POLLHUP + return true; + } + break; + + case State::Sending: + return (_cqeres <= 0); + + case State::Idle: + default: + tSystemError("Bad status [{}:{}]", __FILE__, __LINE__); + return true; + } + } + + void iterate() override + { + //tSystemInfo("AsyncSendFile::iterate : _offset:{} state:{}", _offset, (int)_state); + constexpr size_t SPLICE_LEN = 256 * 1024; + + switch (_state) { + case State::WaitForPollOut: + if (_cqeres < 0) { + return; + } + _state = State::Idle; + _cqeres = 0; + [[fallthrough]]; + + case State::Idle: { + size_t len = std::min(SPLICE_LEN, _fileSize - _offset); + int res = TUringServer::instance()->addSendFile(_sd, _fd, _offset, len, _pipefd, this); + if (res < 0) { + tSystemError("addSend error: {}", strerror(errno)); + _cqeres = -1; + } else { + _state = State::Sending; + } + break; } + + case State::Sending: { + if (_cqeres > 0) { + _offset += _cqeres; + _cqeres = 0; + + if (_offset >= _fileSize) { + return; + } + } + + int res = TUringServer::instance()->addPoll(_sd, (POLLOUT | POLLERR | POLLHUP), this); + if (res < 0) { + tSystemError("addPoll error: {}", strerror(errno)); + _cqeres = -1; + } else { + _state = State::WaitForPollOut; + } + break; } + + default: + break; + } + } + +private: + enum class State { + Idle, + Sending, + WaitForPollOut, + }; + + int _sd {0}; + int _fd {0}; + size_t _fileSize {0}; + size_t _offset {0}; + int _pipefd[2] {0}; + State _state {State::Idle}; +}; + + +template +class ScopeExitFunction { +public: + explicit ScopeExitFunction(Func&& func) : _func(std::move(func)) {} + ~ScopeExitFunction() noexcept { _func(); } +private: + Func _func; +}; + + +TUringCoroutine::~TUringCoroutine() +{ + //tSystemDebug("~TUringCoroutine: sd:{}", _sd); + if (_sd > 0) { + tf_close(_sd); + } +} + + +TUringTask TUringCoroutine::start() +{ + static const int64_t systemLimitBodyBytes = Tf::appSettings()->value(Tf::LimitRequestBody).toLongLong() * 2; + static int keepAlivetimeout = Tf::appSettings()->value(Tf::HttpKeepAliveTimeout).toInt(); + constexpr int64_t bufsize = 8 * 1024; + + ScopeExitFunction closing([this]{ + TUringServer::instance()->registerForGC(this); + }); + + TActionContextRoutine routine; + int timeout = 5000; + + while (timeout > 0) { + //int res; + int64_t lengthToRead = INT64_MAX; + int64_t readLength = 0; + + // ソケット受信 + QByteArray readBuffer; + QByteArray headerBuffer; + TTemporaryFile fileBuffer; + + while (lengthToRead > 0) { + int64_t buflen = std::min(bufsize, lengthToRead); + readBuffer.reserve(readLength + buflen); + + int len = co_await AsyncRecv(_sd, readBuffer.data() + readLength, buflen, timeout); + if (len < 0) { + // timeout or error + if (len == -ETIME) { + tSystemDebug("Recv timer expired fd:{}", _sd); + } else { + tSystemError("Recv error fd:{} error:{}", _sd, strerror(-len)); + } + co_return; + } + if (!len) { + tSystemWarn("Recv peer closed fd:{}", _sd); + co_return; + } + + readLength += len; + readBuffer.resize(readLength); + tSystemDebug("readBuffer size:{}", readBuffer.size()); + + int idx = readBuffer.indexOf(Tf::CRLFCRLF); + if (idx > 0) { + THttpRequestHeader header(readBuffer); + + if (systemLimitBodyBytes > 0 && header.contentLength() > systemLimitBodyBytes) { + throw ClientErrorException((int)Tf::StatusCode::RequestEntityTooLarge); // Request Entity Too Large + } + + lengthToRead = std::max(idx + 4 + header.contentLength() - (int64_t)readBuffer.length(), (int64_t)0); + + if (header.contentLength() > READ_THRESHOLD_LENGTH || (header.contentLength() > 0 && header.contentType().trimmed().startsWith("multipart/form-data"))) { + headerBuffer = readBuffer.mid(0, idx + 4); + // Writes to file buffer + if (!fileBuffer.open()) { + throw RuntimeException(QLatin1String("temporary file open error: ") + fileBuffer.fileTemplate(), __FILE__, __LINE__); + } + fileBuffer.resize(0); // truncate + if (readBuffer.length() > idx + 4) { + tSystemDebug("fileBuffer name: {}", fileBuffer.fileName()); + if (fileBuffer.write(readBuffer.data() + idx + 4, readBuffer.length() - (idx + 4)) < 0) { + throw RuntimeException(QLatin1String("write error: ") + fileBuffer.fileName(), __FILE__, __LINE__); + } + } + readBuffer.resize(0); + } + } + } + + auto result = co_await TThreadPoolAwaiter([&] { + routine.start(readBuffer); + return routine.result; + }); + _response = std::move(result.response); + _fileName = std::move(result.fileName); + + if (!_response.isEmpty()) { + int res = co_await AsyncSend(_sd, _response.data(), _response.length()); + if (res <= 0) { + tSystemError("Send error fd={} res={}", _sd, res); + co_return; + } + } + + // file + if (!_fileName.isEmpty()) { + int fd = ::open(qUtf8Printable(_fileName), O_RDONLY | O_CLOEXEC); + if (fd < 0) { + tSystemError("File open error: {}", _fileName); + Tf::warn("File open error: {}", _fileName); + co_return; + } + + ScopeExitFunction fd_closing([fd]{ ::close(fd); }); + + struct stat st{}; + if (fstat(fd, &st) != 0) { + tSystemError("fstat error: {}", strerror(errno)); + co_return; + } + const auto fileSize = st.st_size; +#if 0 + void *mapped = mmap(nullptr, fileSize, PROT_READ, MAP_PRIVATE, fd, 0); + if (mapped == MAP_FAILED) { + tSystemError("mmap error: {}\n", strerror(errno)); + co_return; + } + + const int64_t slice_len = 16 * 1024; + int64_t sent_len = 0; + while (sent_len < fileSize) { + int64_t send_len = std::min(fileSize - sent_len, slice_len); + res = co_await AsyncSend(_sd, (char*)mapped + sent_len, send_len); + if (res <= 0) { + if (res == -EAGAIN || res == -ENOMEM) { + continue; + } + tSystemError("Send error sd={} res={}", _sd, res); + break; + } + sent_len += res; + } + + munmap(mapped, fileSize); +#else + int res = co_await AsyncSendFile(_sd, fd, fileSize); + tSystemDebug("AsyncSendFile: res:{}", res); + +#endif + _fileName.resize(0); + } + + if (keepAlivetimeout > 0) { + timeout = keepAlivetimeout * 1000; // msecs + } else { + break; + } + } +} diff --git a/src/turingserver.h b/src/turingserver.h new file mode 100644 index 000000000..f863d2513 --- /dev/null +++ b/src/turingserver.h @@ -0,0 +1,101 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class QIODevice; +class THttpHeader; +class THttpSendBuffer; +class TEpollSocket; +class TActionWorker; +class TActionController; +class TUringCoroutine; + + +class TUringTask { +public: + class promise_type { + public: + std::exception_ptr exptr; + + TUringTask get_return_object() + { + return TUringTask{std::coroutine_handle::from_promise(*this)}; + } + std::suspend_never initial_suspend() noexcept { return {}; } + std::suspend_never final_suspend() noexcept { return {}; } + void return_void() noexcept {} + void unhandled_exception() { exptr = std::current_exception(); } + }; + + explicit TUringTask(std::coroutine_handle h) : handle(h) {} + std::coroutine_handle handle {}; +}; + + +class TAwaitBase { +public: + virtual ~TAwaitBase() { } + virtual bool await_ready() const noexcept { return false; } + void clear() + { + _cqeres = 0; + _cqeflags = 0; + _sqecounter = 1; + } + virtual void iterate() { } + virtual bool completed() const { return true; } + + std::coroutine_handle _handle {}; + int _cqeres {0}; + int _cqeflags {0}; + int _sqecounter {1}; +}; + + +class T_CORE_EXPORT TUringServer : public TDatabaseContextThread, public TApplicationServerBase { + Q_OBJECT +public: + TUringServer(int listeningSocket, QObject *parent = nullptr); // Constructor + ~TUringServer(); + + bool isListening() const { return _listenSocket > 0; } + bool start(bool debugMode) override; + void stop() override; + void setAutoReloadingEnabled(bool enable) override; + bool isAutoReloadingEnabled() override; + void registerForGC(TUringCoroutine *); + static TUringServer *instance(int listeningSocket = 0); + + int addAccept(int sd, TAwaitBase *await = nullptr) const; + int addRecv(int sd, void *buf, size_t len, int msecs = 0, TAwaitBase *await = nullptr) const; + int addSend(int sd, const void* buf, size_t len, TAwaitBase *await = nullptr) const; + int addSendZc(int sd, const void* buf, size_t len, TAwaitBase *await = nullptr) const; + int addSendFile(int sd, int fd, int offset, size_t len, int pipefd[2], TAwaitBase* await) const; + int addPoll(int sd, unsigned int poll_mask, TAwaitBase *await = nullptr) const; + void addResumeHandle(std::coroutine_handle handle); + +protected: + void run() override; + void addNotifyEvent(); + +private: + mutable io_uring _ring {}; + bool _stopped {false}; + int _listenSocket {0}; + int _notifyFd {0}; + bool _autoReload {false}; + TLockQueue> _resumeHandlers; + TLockStack _garbage; + friend class TEpollSocket; + + T_DISABLE_COPY(TUringServer) + T_DISABLE_MOVE(TUringServer) +}; diff --git a/src/turingserver_linux.cpp b/src/turingserver_linux.cpp new file mode 100644 index 000000000..f8f59fe5d --- /dev/null +++ b/src/turingserver_linux.cpp @@ -0,0 +1,481 @@ +/* Copyright (c) 2015-2019, AOYAMA Kazuharu + * All rights reserved. + * + * This software may be used and distributed according to the terms of + * the New BSD License, which is incorporated herein by reference. + */ + +#include "tkvsdatabasepool.h" +#include "tpublisher.h" +#include "tsqldatabasepool.h" +#include "tsystembus.h" +#include "tsystemglobal.h" +#include "turlroute.h" +#include "turingcoroutine.h" +#include "turingserver.h" +#include "tfcore_unix.h" +#include +#include +#include +#include +#include +#include +#include + +constexpr int SEND_BUF_SIZE = 128 * 1024; +constexpr int RECV_BUF_SIZE = 128 * 1024; +constexpr uint64_t UD_NOTIFY = 0xFF00000000000000ULL; + + +TUringServer *TUringServer::instance(int listeningSocket) +{ + static std::unique_ptr instance = [&]() { + if (listeningSocket <= 0) { + throw StandardException("Invalid socket", __FILE__, __LINE__); + } + return std::make_unique(listeningSocket); + }(); + return instance.get(); +} + + +static void setDefferAcceptOption(int fd) +{ + int res, flag; + + flag = 2; // secs + res = setsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, (char *)&flag, sizeof(flag)); + if (res < 0) { + tSystemWarn("setsockopt error [TCP_DEFER_ACCEPT] fd:{}", fd); + } +} + + +static void setBufferOption(int fd) +{ + int res, flag, bufsize; + + // Disable the Nagle (TCP No Delay) algorithm + flag = 1; + res = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag)); + if (res < 0) { + tSystemWarn("setsockopt error [TCP_NODELAY] fd:{}", fd); + } + + bufsize = SEND_BUF_SIZE; + res = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize)); + if (res < 0) { + tSystemWarn("setsockopt error [SO_SNDBUF] fd:{}", fd); + } + + bufsize = RECV_BUF_SIZE; + res = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize)); + if (res < 0) { + tSystemWarn("setsockopt error [SO_RCVBUF] fd:{}", fd); + } +} + +TUringServer::TUringServer(int listeningSocket, QObject *parent) : + TDatabaseContextThread(parent), + TApplicationServerBase(), + _listenSocket(listeningSocket) +{ + io_uring_queue_init(8192, &_ring, 0); + + // Event fd + _notifyFd = eventfd(0, (EFD_NONBLOCK | EFD_CLOEXEC)); + if (_notifyFd < 0) { + tSystemError("eventfd error: {} [{}:{}]", strerror(errno), __FILE__, __LINE__); + return; + } + + addNotifyEvent(); +} + + +TUringServer::~TUringServer() +{ + if (_notifyFd > 0) { + tf_close(_notifyFd); + } + io_uring_queue_exit(&_ring); +} + + +bool TUringServer::start(bool debugMode) +{ + if (isRunning()) { + return true; + } + + // Loads libs + bool res = loadLibraries(); + if (!res) { + if (debugMode) { + tSystemError("Failed to load application libraries."); + return false; + } else { + tSystemWarn("Failed to load application libraries."); + } + } + + // To work a timer in main thread + TSqlDatabasePool::instance(); + TKvsDatabasePool::instance(); + + TStaticInitializeThread::exec(); + QThread::start(); + return true; +} + + +void TUringServer::run() +{ + setDefferAcceptOption(_listenSocket); + + TAwaitBase accepter; + instance()->addAccept(_listenSocket, &accepter); + + __kernel_timespec ts = { + .tv_sec = 2, // 2 secs + .tv_nsec = 0 + }; + + std::optional gcco; + std::optional> cohandle; + + while (!_stopped) { + while ((gcco = _garbage.pop())) { + delete *gcco; + } + + // Resume handlers + cohandle = _resumeHandlers.pop(); + if (cohandle) { + uint64_t tmp; + tf_read(_notifyFd, &tmp, sizeof(tmp)); + + do { + if (!cohandle->done()) { + cohandle->resume(); + } + } while ((cohandle = _resumeHandlers.pop())); + } + + io_uring_cqe *cqe = nullptr; + int res = io_uring_wait_cqe_timeout(&_ring, &cqe, &ts); + Tf::ScopeExitFunction seen([&]{ if (cqe) io_uring_cqe_seen(&_ring, cqe); }); + + if (res < 0 || !cqe) [[unlikely]] { + if (res == -EINTR || res == -EAGAIN) { + continue; + } + + if (res == -ETIME) { + if (_autoReload && newerLibraryExists()) { + tSystemInfo("Detect new library of application. Reloading the libraries."); + Tf::app()->exit(127); + } + continue; + } + + tSystemError("io_uring_wait_cqe error: {}", strerror(-res)); + break; + } + + void *user_data = io_uring_cqe_get_data(cqe); + if (user_data) [[likely]] { + if (user_data == (void*)UD_NOTIFY) [[unlikely]] { + if (!(cqe->flags & IORING_CQE_F_MORE)) { + addNotifyEvent(); + } + continue; + } + + auto *await = static_cast(user_data); + if (await == &accepter) { + // Accepts + if (cqe->res >= 0) { + // Starts coroutine + int fd = cqe->res; + setBufferOption(fd); + auto *coro = new TUringCoroutine(fd); + coro->start(); + await->clear(); // clear + } else { + int err = -cqe->res; + switch (err) { + case EAGAIN: + case ECONNABORTED: + case ECANCELED: + // ignore + break; + case EINVAL: + case EBADF: + case ENOTSOCK: + tSystemError("Listen socket invalid, terminating. error: {}", strerror(err)); + stop(); + break; + default: + tSystemError("Accept error: {}\n", strerror(err)); + stop(); + break; + } + } + + if (!(cqe->flags & IORING_CQE_F_MORE)) { + if (addAccept(_listenSocket, &accepter) < 0) { + tSystemError("addAccept error: {}", strerror(errno)); + } + } + } else { + tSystemDebug("cqe->res:{} cqe->flags: {}", cqe->res, cqe->flags); + if (cqe->flags != IORING_CQE_F_MORE) [[likely]] { + if (!await->_cqeres && !await->_cqeflags) { + await->_cqeres = cqe->res; + await->_cqeflags = cqe->flags; + } + + if (--await->_sqecounter == 0 && await->_handle && !await->_handle.done()) { + if (await->completed()) { + await->_handle.resume(); + } else { + await->iterate(); + } + } + } + } + } + } +} + +void TUringServer::addNotifyEvent() +{ + if (_notifyFd <= 0) { + tSystemError("nofify fd error [{}:{}]", __FILE__, __LINE__); + return; + } + + io_uring_sqe *sqe = io_uring_get_sqe(&_ring); + if (!sqe) { + tSystemError("io_uring_get_sqe error: {} [{}:{}]", strerror(errno), __FILE__, __LINE__); + return; + } + + io_uring_prep_poll_multishot(sqe, _notifyFd, POLL_IN); + io_uring_sqe_set_data(sqe, (void*)UD_NOTIFY); + io_uring_submit(&_ring); +} + + +void TUringServer::stop() +{ + _stopped = true; + if (isRunning()) { + QThread::wait(10000); + } + TStaticReleaseThread::exec(); +} + + +void TUringServer::setAutoReloadingEnabled(bool enable) +{ + _autoReload = enable; +} + + +bool TUringServer::isAutoReloadingEnabled() +{ + return _autoReload; +} + +// +// Prepare a accept request +// +int TUringServer::addAccept(int fd, TAwaitBase* await) const +{ + io_uring_sqe *sqe = io_uring_get_sqe(&_ring); + if (!sqe) { + tSystemError("io_uring_get_sqe error: {} [{}:{}]", strerror(errno), __FILE__, __LINE__); + return -1; + } + + io_uring_prep_accept(sqe, fd, nullptr, nullptr, (SOCK_CLOEXEC | SOCK_NONBLOCK)); + if (await) { + await->clear(); + io_uring_sqe_set_data(sqe, await); + } + return io_uring_submit(&_ring); +} + +// +// Prepare a recv request +// +int TUringServer::addRecv(int fd, void* buf, size_t len, int msecs, TAwaitBase* await) const +{ + io_uring_sqe *sqe = io_uring_get_sqe(&_ring); + if (!sqe) { + tSystemError("io_uring_get_sqe error: {} [{}:{}]", strerror(errno), __FILE__, __LINE__); + return -1; + } + + io_uring_prep_recv(sqe, fd, buf, len, 0); + if (await) { + await->clear(); + io_uring_sqe_set_data(sqe, await); + } + + if (msecs > 0) { + sqe->flags |= IOSQE_IO_LINK; + io_uring_sqe *sqe2 = io_uring_get_sqe(&_ring); + if (!sqe2) { + tSystemError("io_uring_get_sqe error: {} [{}:{}]", strerror(errno), __FILE__, __LINE__); + return -1; + } + __kernel_timespec ts = { .tv_sec = msecs / 1000, .tv_nsec = (msecs % 1000) * 1'000'000 }; + io_uring_prep_link_timeout(sqe2, &ts, 0); + if (await) { + await->_sqecounter++; + io_uring_sqe_set_data(sqe2, await); + } + } + return io_uring_submit(&_ring); +} + +// +// Prepare a send request +// +int TUringServer::addSend(int fd, const void* buf, size_t len, TAwaitBase* await) const +{ + io_uring_sqe *sqe = io_uring_get_sqe(&_ring); + if (!sqe) { + tSystemError("io_uring_get_sqe error: {} [{}:{}]", strerror(errno), __FILE__, __LINE__); + return -1; + } + io_uring_prep_send(sqe, fd, buf, len, 0); + if (await) { + await->clear(); + io_uring_sqe_set_data(sqe, await); + } + return io_uring_submit(&_ring); +} + +// +// Prepare a zerocopy send request +// +int TUringServer::addSendZc(int fd, const void* buf, size_t len, TAwaitBase* await) const +{ + io_uring_sqe *sqe = io_uring_get_sqe(&_ring); + if (!sqe) { + tSystemError("io_uring_get_sqe error: {} [{}:{}]", strerror(errno), __FILE__, __LINE__); + return -1; + } + io_uring_prep_send_zc(sqe, fd, buf, len, 0, 0); + if (await) { + await->clear(); + io_uring_sqe_set_data(sqe, await); + } + return io_uring_submit(&_ring); +} + +// int TUringServer::addSendFile(int sd, int fd, int offset, size_t slice_len, TAwaitBase* await) +// { +// size_t file_size = lseek(fd, 0, SEEK_END); +// if (file_size < 0) { +// tSystemError("lseek error: {}\n", strerror(err)); +// return -1; +// } +// lseek(fd, 0, SEEK_SET); + +// void *mapped = mmap(nullptr, file_size, PROT_READ, MAP_PRIVATE, fd, 0); +// if (mapped == MAP_FAILED) { +// tSystemError("mmap error: {}\n", strerror(err)); +// return -1; +// } + +// return addSend(sd, const void* bu, size_t slice_len, await); +// } + + +int TUringServer::addSendFile(int sd, int fd, int offset, size_t len, int pipefd[2], TAwaitBase* await) const +{ + io_uring_sqe *sqe = io_uring_get_sqe(&_ring); + if (!sqe) { + tSystemError("io_uring_get_sqe error: {} [{}:{}]", strerror(errno), __FILE__, __LINE__); + return -1; + } + + io_uring_prep_splice(sqe, fd, offset, pipefd[1], -1, len, SPLICE_F_MOVE | SPLICE_F_MORE); + if (await) { + await->clear(); + io_uring_sqe_set_data(sqe, await); + } + + io_uring_sqe *sqe2 = io_uring_get_sqe(&_ring); + if (!sqe2) { + tSystemError("io_uring_get_sqe error: {} [{}:{}]", strerror(errno), __FILE__, __LINE__); + return -1; + } + + io_uring_prep_splice(sqe2, pipefd[0], -1, sd, -1, len, SPLICE_F_MOVE | SPLICE_F_MORE); + if (await) { + await->_sqecounter++; + io_uring_sqe_set_data(sqe2, await); + } + return io_uring_submit(&_ring); +} + + +int TUringServer::addPoll(int sd, unsigned int poll_mask, TAwaitBase *await) const +{ + io_uring_sqe *sqe = io_uring_get_sqe(&_ring); + if (!sqe) { + tSystemError("io_uring_get_sqe error: {} [{}:{}]", strerror(errno), __FILE__, __LINE__); + return -1; + } + + io_uring_prep_poll_add(sqe, sd, poll_mask); + if (await) { + await->clear(); + io_uring_sqe_set_data(sqe, await); + } + return io_uring_submit(&_ring); +} + + +// +// Prepare a event request +// +// int TUringServer::addEvent(int fd, TAwaitBase* await) const +// { +// io_uring_sqe *sqe = io_uring_get_sqe(&_ring); +// if (!sqe) { +// tSystemError("io_uring_get_sqe error: {} [{}:{}]", strerror(errno), __FILE__, __LINE__); +// return -1; +// } + +// io_uring_prep_poll_add(sqe, fd, POLL_IN); +// if (await) { +// await->clear(); +// io_uring_sqe_set_data(sqe, await); +// } +// return io_uring_submit(&_ring); +// } + + +void TUringServer::addResumeHandle(std::coroutine_handle handle) +{ + _resumeHandlers.push(handle); + + int64_t one = 1; + auto n = tf_write(_notifyFd, &one, sizeof(one)); + if (n < 0) { + tSystemError("write error: {}. fd:{} [{}:{}]", strerror(errno), _notifyFd, __FILE__, __LINE__); + } +} + + +void TUringServer::registerForGC(TUringCoroutine *coroutine) +{ + _garbage.push(coroutine); +} diff --git a/src/turlroute.cpp b/src/turlroute.cpp index 35450aa09..c29106556 100644 --- a/src/turlroute.cpp +++ b/src/turlroute.cpp @@ -15,27 +15,27 @@ #include -const QMap directiveHash = { - {"match", TRoute::Match}, - {"get", TRoute::Get}, - {"post", TRoute::Post}, - {"put", TRoute::Put}, - {"patch", TRoute::Patch}, - {"delete", TRoute::Delete}, - {"trace", TRoute::Trace}, - {"connect", TRoute::Connect}, - {"patch", TRoute::Patch}, +const QMap directiveHash = { + {"match", TRoute::RouteDirective::Match}, + {"get", TRoute::RouteDirective::Get}, + {"post", TRoute::RouteDirective::Post}, + {"put", TRoute::RouteDirective::Put}, + {"patch", TRoute::RouteDirective::Patch}, + {"delete", TRoute::RouteDirective::Delete}, + {"trace", TRoute::RouteDirective::Trace}, + {"connect", TRoute::RouteDirective::Connect}, + {"patch", TRoute::RouteDirective::Patch}, }; const TUrlRoute &TUrlRoute::instance() { - static TUrlRoute *urlRoute = []() { - auto *route = new TUrlRoute(); - route->parseConfigFile(); + static TUrlRoute urlRoute = []() { + TUrlRoute route; + route.parseConfigFile(); return route; }(); - return *urlRoute; + return urlRoute; } @@ -89,8 +89,8 @@ bool TUrlRoute::addRouteFromString(const QString &line) TRoute rt; // Check method - rt.method = directiveHash.value(items[0].toLower(), TRoute::Invalid); - if (rt.method == TRoute::Invalid) { + rt.method = directiveHash.value(items[0].toLower(), TRoute::RouteDirective::Invalid); + if (rt.method == TRoute::RouteDirective::Invalid) { Tf::error("Invalid directive, '{}'", items[0]); return false; } @@ -128,7 +128,7 @@ bool TUrlRoute::addRouteFromString(const QString &line) _routes << rt; tSystemDebug("route: method:{} path:{} ctrl:{} action:{} params:{}", - rt.method, (QLatin1String("/") + rt.componentList.join("/")), (const char*)rt.controller.data(), + (int)rt.method, (QLatin1String("/") + rt.componentList.join("/")), (const char*)rt.controller.data(), (const char*)rt.action.data(), rt.hasVariableParams); return true; } @@ -158,7 +158,7 @@ TRouting TUrlRoute::findRouting(Tf::HttpMethod method, const QStringList &compon } } - if (rt.method == TRoute::Match || rt.method == method) { + if (rt.method == TRoute::RouteDirective::Match || (int)rt.method == (int)method) { // Generates parameters for action QStringList params = components; diff --git a/src/turlroute.h b/src/turlroute.h index 6c77b608d..c99a6e2fd 100644 --- a/src/turlroute.h +++ b/src/turlroute.h @@ -6,21 +6,21 @@ class TRoute { public: - enum RouteDirective { + enum class RouteDirective { Match = 0, - Get = Tf::Get, - Head = Tf::Head, - Post = Tf::Post, - Options = Tf::Options, - Put = Tf::Put, - Delete = Tf::Delete, - Trace = Tf::Trace, - Connect = Tf::Connect, - Patch = Tf::Patch, + Get = (int)Tf::HttpMethod::Get, + Head = (int)Tf::HttpMethod::Head, + Post = (int)Tf::HttpMethod::Post, + Options = (int)Tf::HttpMethod::Options, + Put = (int)Tf::HttpMethod::Put, + Delete = (int)Tf::HttpMethod::Delete, + Trace = (int)Tf::HttpMethod::Trace, + Connect = (int)Tf::HttpMethod::Connect, + Patch = (int)Tf::HttpMethod::Patch, Invalid = 0xff, }; - int method {Invalid}; + RouteDirective method {RouteDirective::Invalid}; QStringList componentList; QList keywordIndexes; QByteArray controller; diff --git a/src/tviewhelper.cpp b/src/tviewhelper.cpp index 02263ee48..9f7133a99 100644 --- a/src/tviewhelper.cpp +++ b/src/tviewhelper.cpp @@ -43,7 +43,7 @@ QString TViewHelper::linkTo(const QString &text, const QUrl &url, Tf::HttpMethod QString string("").append(inputAuthenticityTag()); endTags << endTag("form"); diff --git a/src/tviewhelper.h b/src/tviewhelper.h index 5f6f0e264..492d39ab3 100644 --- a/src/tviewhelper.h +++ b/src/tviewhelper.h @@ -33,11 +33,11 @@ class T_CORE_EXPORT TViewHelper { const THtmlAttribute &attributes = THtmlAttribute()) const; QString linkToIf(bool condition, const QString &text, const QUrl &url, - Tf::HttpMethod method = Tf::Get, const QString &jsCondition = QString(), + Tf::HttpMethod method = Tf::HttpMethod::Get, const QString &jsCondition = QString(), const THtmlAttribute &attributes = THtmlAttribute()) const; QString linkToUnless(bool condition, const QString &text, const QUrl &url, - Tf::HttpMethod method = Tf::Get, const QString &jsCondition = QString(), + Tf::HttpMethod method = Tf::HttpMethod::Get, const QString &jsCondition = QString(), const THtmlAttribute &attributes = THtmlAttribute()) const; QString linkToFunction(const QString &text, const QString &function, @@ -52,7 +52,7 @@ class T_CORE_EXPORT TViewHelper { QString anchor(const QString &text, const QUrl &url, Tf::HttpMethod method, const THtmlAttribute &attributes = THtmlAttribute()) const; - QString anchor(const QString &text, const QUrl &url, Tf::HttpMethod method = Tf::Get, + QString anchor(const QString &text, const QUrl &url, Tf::HttpMethod method = Tf::HttpMethod::Get, const QString &jsCondition = QString(), const THtmlAttribute &attributes = THtmlAttribute()) const; @@ -64,17 +64,17 @@ class T_CORE_EXPORT TViewHelper { const THtmlAttribute &attributes = THtmlAttribute()) const; QString anchorIf(bool condition, const QString &text, const QUrl &url, - Tf::HttpMethod method = Tf::Get, const QString &jsCondition = QString(), + Tf::HttpMethod method = Tf::HttpMethod::Get, const QString &jsCondition = QString(), const THtmlAttribute &attributes = THtmlAttribute()) const; QString anchorUnless(bool condition, const QString &text, const QUrl &url, - Tf::HttpMethod method = Tf::Get, const QString &jsCondition = QString(), + Tf::HttpMethod method = Tf::HttpMethod::Get, const QString &jsCondition = QString(), const THtmlAttribute &attributes = THtmlAttribute()) const; QString anchorFunction(const QString &text, const QString &function, const THtmlAttribute &attributes = THtmlAttribute()) const; - QString formTag(const QUrl &url, Tf::HttpMethod method = Tf::Post, bool multipart = false, + QString formTag(const QUrl &url, Tf::HttpMethod method = Tf::HttpMethod::Post, bool multipart = false, const THtmlAttribute &attributes = THtmlAttribute()); QString inputTag(const QString &type, const QString &name, const QVariant &value, @@ -219,7 +219,7 @@ class T_CORE_EXPORT TViewHelper { */ inline QString TViewHelper::linkTo(const QString &text, const QUrl &url, const THtmlAttribute &attributes) const { - return linkTo(text, url, Tf::Get, QString(), attributes); + return linkTo(text, url, Tf::HttpMethod::Get, QString(), attributes); } /*! diff --git a/src/twebapplication.cpp b/src/twebapplication.cpp index 0b51724cf..168ce6476 100644 --- a/src/twebapplication.cpp +++ b/src/twebapplication.cpp @@ -21,6 +21,7 @@ constexpr auto DEFAULT_INTERNET_MEDIA_TYPE = "text/plain"; constexpr auto DEFAULT_DATABASE_ENVIRONMENT = "product"; +using enum TWebApplication::MultiProcessingModule; /*! \class TWebApplication @@ -361,25 +362,33 @@ QString TWebApplication::validationErrorMessage(int rule) const TWebApplication::MultiProcessingModule TWebApplication::multiProcessingModule() const { static TWebApplication::MultiProcessingModule module = [this]() { - if (_mpmTemp != Invalid) { + if (_mpmTemp != MultiProcessingModule::Invalid) { return _mpmTemp; } QString str = Tf::appSettings()->value(Tf::MultiProcessingModule).toString().toLower(); if (str == "thread") { - return Thread; + return MultiProcessingModule::Thread; } else if (str == "epoll") { #ifdef Q_OS_LINUX - return Epoll; + return MultiProcessingModule::Epoll; #else tSystemWarn("Unsupported MPM: epoll (Linux only)"); Tf::warn("Unsupported MPM: epoll (Linux only)"); - return Thread; + return MultiProcessingModule::Thread; +#endif + } else if (str == "uring") { +#ifdef Q_OS_LINUX + return MultiProcessingModule::Uring; +#else + tSystemWarn("Unsupported MPM: uring (Linux only)"); + Tf::warn("Unsupported MPM: uring (Linux only)"); + return MultiProcessingModule::Thread; #endif } tSystemWarn("Unsupported MPM: {}", str); Tf::warn("Unsupported MPM: {}", str); - return Thread; + return MultiProcessingModule::Thread; }(); return module; } @@ -414,15 +423,19 @@ int TWebApplication::maxNumberOfThreadsPerAppServer() const QString mpm = Tf::appSettings()->value(Tf::MultiProcessingModule).toString().toLower(); switch (Tf::app()->multiProcessingModule()) { - case TWebApplication::Thread: + case MultiProcessingModule::Thread: maxNum = Tf::appSettings()->readValue(QLatin1String("MPM.") + mpm + ".MaxThreadsPerAppServer").toInt(); maxNum = (maxNum > 0) ? maxNum : 16; break; - case TWebApplication::Epoll: + case MultiProcessingModule::Epoll: maxNum = 128; break; + case MultiProcessingModule::Uring: + maxNum = 128; // TODO TODO TODO + break; + default: break; } diff --git a/src/twebapplication.h b/src/twebapplication.h index 5fe6f7781..9f1908204 100644 --- a/src/twebapplication.h +++ b/src/twebapplication.h @@ -23,10 +23,11 @@ class T_CORE_EXPORT TWebApplication { Q_OBJECT public: - enum MultiProcessingModule { + enum class MultiProcessingModule { Invalid = 0, - Thread = 1, - Epoll = 2, + Thread, + Epoll, + Uring, }; TWebApplication(int &argc, char **argv); @@ -109,7 +110,7 @@ private slots: QBasicTimer _timer; QMap _configMap; int _cacheSqlDbIndex {-1}; - MultiProcessingModule _mpmTemp {Invalid}; + MultiProcessingModule _mpmTemp {MultiProcessingModule::Invalid}; static void resetSignalNumber(); diff --git a/src/twebapplication_win.cpp b/src/twebapplication_win.cpp index 540d4f458..567b54461 100644 --- a/src/twebapplication_win.cpp +++ b/src/twebapplication_win.cpp @@ -91,7 +91,7 @@ void TWebApplication::watchLocalSocket() void TWebApplication::recvLocalSocket() { - QLocalServer *server = dynamic_cast(QObject::sender()); + QLocalServer *server = qobject_cast(QObject::sender()); if (server) { QLocalSocket *socket = server->nextPendingConnection(); if (socket->waitForReadyRead(50)) { diff --git a/src/twebsocket.cpp b/src/twebsocket.cpp index de79099af..c5af8355f 100644 --- a/src/twebsocket.cpp +++ b/src/twebsocket.cpp @@ -82,7 +82,7 @@ void TWebSocket::sendPong(const QByteArray &data) bool TWebSocket::canReadRequest() const { for (const auto &frm : frames) { - if (frm.isFinalFrame() && frm.state() == TWebSocketFrame::Completed) { + if (frm.isFinalFrame() && frm.state() == TWebSocketFrame::ProcessingState::Completed) { return true; } } @@ -126,7 +126,7 @@ void TWebSocket::readRequest() while (!frames.isEmpty()) { TWebSocketFrame frm = frames.takeFirst(); pay += frm.payload(); - if (frm.isFinalFrame() && frm.state() == TWebSocketFrame::Completed) { + if (frm.isFinalFrame() && frm.state() == TWebSocketFrame::ProcessingState::Completed) { payloads << qMakePair(opcode, pay); break; } @@ -135,7 +135,7 @@ void TWebSocket::readRequest() if (!payloads.isEmpty()) { // Starts worker thread - TWebSocketWorker *worker = new TWebSocketWorker(TWebSocketWorker::Receiving, this, reqHeader.path()); + TWebSocketWorker *worker = new TWebSocketWorker(TWebSocketWorker::RunMode::Receiving, this, reqHeader.path()); worker->setPayloads(payloads); startWorker(worker); } @@ -144,7 +144,7 @@ void TWebSocket::readRequest() void TWebSocket::startWorkerForOpening(const TSession &session) { - TWebSocketWorker *worker = new TWebSocketWorker(TWebSocketWorker::Opening, this, reqHeader.path()); + TWebSocketWorker *worker = new TWebSocketWorker(TWebSocketWorker::RunMode::Opening, this, reqHeader.path()); worker->setSession(session); startWorker(worker); } @@ -153,7 +153,7 @@ void TWebSocket::startWorkerForOpening(const TSession &session) void TWebSocket::startWorkerForClosing() { if (!closing.load()) { - TWebSocketWorker *worker = new TWebSocketWorker(TWebSocketWorker::Closing, this, reqHeader.path()); + TWebSocketWorker *worker = new TWebSocketWorker(TWebSocketWorker::RunMode::Closing, this, reqHeader.path()); startWorker(worker); } } diff --git a/src/twebsocketframe.cpp b/src/twebsocketframe.cpp index 910e95b88..3026e2f37 100644 --- a/src/twebsocketframe.cpp +++ b/src/twebsocketframe.cpp @@ -99,7 +99,7 @@ void TWebSocketFrame::clear() _maskKey = 0; _payloadLength = 0; _payload.truncate(0); - _state = Empty; + _state = ProcessingState::Empty; _valid = false; } @@ -148,7 +148,7 @@ QByteArray TWebSocketFrame::toByteArray() const bool TWebSocketFrame::validate() { - if (_state != Completed) { + if (_state != ProcessingState::Completed) { return false; } diff --git a/src/twebsocketframe.h b/src/twebsocketframe.h index e93605309..47305a850 100644 --- a/src/twebsocketframe.h +++ b/src/twebsocketframe.h @@ -43,7 +43,7 @@ class T_CORE_EXPORT TWebSocketFrame { QByteArray toByteArray() const; private: - enum ProcessingState { + enum class ProcessingState { Empty = 0, HeaderParsed, MoreData, @@ -66,7 +66,7 @@ class T_CORE_EXPORT TWebSocketFrame { uint32_t _maskKey {0}; uint64_t _payloadLength {0}; QByteArray _payload; // unmasked data stored - ProcessingState _state {Empty}; + ProcessingState _state {ProcessingState::Empty}; bool _valid {false}; friend class TAbstractWebSocket; diff --git a/src/twebsocketworker.cpp b/src/twebsocketworker.cpp index eb601802c..201469e7a 100644 --- a/src/twebsocketworker.cpp +++ b/src/twebsocketworker.cpp @@ -56,7 +56,7 @@ void TWebSocketWorker::setSession(const TSession &session) void TWebSocketWorker::run() { - if (_mode == Receiving) { + if (_mode == RunMode::Receiving) { for (auto &p : (const QList> &)_payloads) { execute(p.first, p.second); } @@ -93,7 +93,7 @@ void TWebSocketWorker::execute(int opcode, const QByteArray &payload) } switch (_mode) { - case Opening: { + case RunMode::Opening: { bool res = endpoint->onOpen(_httpSession); if (res) { // For switch response @@ -105,17 +105,16 @@ void TWebSocketWorker::execute(int opcode, const QByteArray &payload) } else { endpoint->taskList.prepend(qMakePair((int)TWebSocketEndpoint::OpenError, QVariant())); } - break; - } + break; } - case Closing: + case RunMode::Closing: if (!_socket->closing.exchange(true)) { endpoint->onClose(Tf::GoingAway); endpoint->unsubscribeFromAll(); } break; - case Receiving: { + case RunMode::Receiving: { switch (opcode) { case TWebSocketFrame::TextFrame: @@ -139,8 +138,7 @@ void TWebSocketWorker::execute(int opcode, const QByteArray &payload) endpoint->unsubscribeFromAll(); } endpoint->close(closeCode); // close response or disconnect - break; - } + break; } case TWebSocketFrame::Ping: endpoint->onPing(payload); diff --git a/src/twebsocketworker.h b/src/twebsocketworker.h index 458a1bf61..de26e2aa1 100644 --- a/src/twebsocketworker.h +++ b/src/twebsocketworker.h @@ -13,7 +13,7 @@ class TAbstractWebSocket; class T_CORE_EXPORT TWebSocketWorker : public TDatabaseContextThread { Q_OBJECT public: - enum RunMode { + enum class RunMode { Opening = 0, Receiving, Closing, @@ -31,10 +31,9 @@ class T_CORE_EXPORT TWebSocketWorker : public TDatabaseContextThread { void execute(int opcode = 0, const QByteArray &payload = QByteArray()); private: - RunMode _mode {Opening}; + RunMode _mode {RunMode::Opening}; TAbstractWebSocket *_socket {nullptr}; TSession _httpSession; QByteArray _requestPath; QList> _payloads; }; - diff --git a/tools/tfmanager/main.cpp b/tools/tfmanager/main.cpp index 3f5788490..02e7f9fd0 100644 --- a/tools/tfmanager/main.cpp +++ b/tools/tfmanager/main.cpp @@ -579,8 +579,9 @@ int managerMain(int argc, char *argv[]) for (;;) { ServerManager *manager = nullptr; switch (app.multiProcessingModule()) { - case TWebApplication::Thread: // FALLTHRU - case TWebApplication::Epoll: { + case TWebApplication::MultiProcessingModule::Thread: // FALLTHRU + case TWebApplication::MultiProcessingModule::Epoll: + case TWebApplication::MultiProcessingModule::Uring: { int num = app.maxNumberOfAppServers(); if (autoReloadMode && num > 1) { num = 1; diff --git a/tools/tfmanager/servermanager.cpp b/tools/tfmanager/servermanager.cpp index 62a9a26a7..c815a6cf8 100644 --- a/tools/tfmanager/servermanager.cpp +++ b/tools/tfmanager/servermanager.cpp @@ -214,7 +214,7 @@ void ServerManager::startServer(int id) const } TWebApplication::MultiProcessingModule mpm = Tf::app()->multiProcessingModule(); - if (mpm == TWebApplication::Epoll || mpm == TWebApplication::Thread) { + if (mpm == TWebApplication::MultiProcessingModule::Epoll || mpm == TWebApplication::MultiProcessingModule::Thread || mpm == TWebApplication::MultiProcessingModule::Uring) { if (id < maxServers) { args.prepend(QString::number(id)); args.prepend("-i"); // give ID for app server diff --git a/tools/tfmanager/tfmanager.pro b/tools/tfmanager/tfmanager.pro index 8b806b447..f4afa39d8 100644 --- a/tools/tfmanager/tfmanager.pro +++ b/tools/tfmanager/tfmanager.pro @@ -39,7 +39,7 @@ windows { } } else:unix { LIBS += -Wl,-rpath,$$lib.path -L$$lib.path -ltreefrog - linux-*:LIBS += -lrt + linux-*:LIBS += -lrt -luring } INSTALLS += target diff --git a/tools/tfserver/main.cpp b/tools/tfserver/main.cpp index a1e2f7931..74975e391 100644 --- a/tools/tfserver/main.cpp +++ b/tools/tfserver/main.cpp @@ -18,8 +18,10 @@ #include #include #include +#include "turingserver.h" #include #include +#include #include @@ -77,15 +79,15 @@ QMap convertArgs(const QStringList &args) return map; } -const QMap methodDef = { - {TRoute::Match, QByteArray("match ")}, - {TRoute::Get, QByteArray("get ")}, - {TRoute::Head, QByteArray("head ")}, - {TRoute::Post, QByteArray("post ")}, - {TRoute::Options, QByteArray("options ")}, - {TRoute::Put, QByteArray("put ")}, - {TRoute::Delete, QByteArray("delete ")}, - {TRoute::Trace, QByteArray("trace ")}, +const QMap methodDef = { + {TRoute::RouteDirective::Match, QByteArray("match ")}, + {TRoute::RouteDirective::Get, QByteArray("get ")}, + {TRoute::RouteDirective::Head, QByteArray("head ")}, + {TRoute::RouteDirective::Post, QByteArray("post ")}, + {TRoute::RouteDirective::Options, QByteArray("options ")}, + {TRoute::RouteDirective::Put, QByteArray("put ")}, + {TRoute::RouteDirective::Delete, QByteArray("delete ")}, + {TRoute::RouteDirective::Trace, QByteArray("trace ")}, }; @@ -141,7 +143,7 @@ int showRoutes() } else { action = QByteArrayLiteral("(not found)"); } - std::printf(" %s%s -> %s\n", methodDef.value(route.method).data(), qUtf8Printable(path), qUtf8Printable(action)); + std::printf(" %s%s -> %s\n", methodDef.value(static_cast(route.method)).data(), qUtf8Printable(path), qUtf8Printable(action)); } std::printf("\n"); } @@ -278,14 +280,14 @@ int main(int argc, char *argv[]) } switch (webapp.multiProcessingModule()) { - case TWebApplication::Thread: - server = new TThreadApplicationServer(sock, &webapp); + case TWebApplication::MultiProcessingModule::Thread: + server = TThreadApplicationServer::instance(sock, &webapp); #ifdef Q_OS_LINUX TMultiplexingServer::instantiate(0); // For epoll eventloop #endif break; - case TWebApplication::Epoll: + case TWebApplication::MultiProcessingModule::Epoll: #ifdef Q_OS_LINUX // Sets a listening socket descriptor TMultiplexingServer::instantiate(sock); @@ -296,6 +298,14 @@ int main(int argc, char *argv[]) #endif break; + case TWebApplication::MultiProcessingModule::Uring: +#ifdef Q_OS_LINUX + server = TUringServer::instance(sock); +#else + tFatal("Unsupported MPM: epoll"); +#endif + break; + default: break; } @@ -310,22 +320,10 @@ int main(int argc, char *argv[]) // Initialize cache webapp.initializeCache(); - QObject::connect(&webapp, &QCoreApplication::aboutToQuit, [=]() { server->stop(); }); + QObject::connect(&webapp, &QCoreApplication::aboutToQuit, [&]() { server->stop(); }); ret = webapp.exec(); finish: - switch (webapp.multiProcessingModule()) { - case TWebApplication::Thread: - delete server; - break; - - case TWebApplication::Epoll: - break; - - default: - break; - } - // Release loggers Tf::releaseAppLoggers(); Tf::releaseQueryLogger(); diff --git a/tools/tfserver/tfserver.pro b/tools/tfserver/tfserver.pro index 2121ac22b..e937e6157 100644 --- a/tools/tfserver/tfserver.pro +++ b/tools/tfserver/tfserver.pro @@ -50,7 +50,7 @@ windows { # for linux linux-* { # -lunwind - LIBS += -lrt $$system("pkg-config --libs libunwind 2>/dev/null") + LIBS += -lrt -luring $$system("pkg-config --libs libunwind 2>/dev/null") } } diff --git a/tools/tspawn/tspawn.pro b/tools/tspawn/tspawn.pro index ea1e86107..40c882bea 100644 --- a/tools/tspawn/tspawn.pro +++ b/tools/tspawn/tspawn.pro @@ -36,7 +36,7 @@ windows { LIBS += -L"$$target.path" } else:unix { LIBS += -Wl,-rpath,$$lib.path -L$$lib.path -ltreefrog - linux-*:LIBS += -lrt + linux-*:LIBS += -lrt -luring } isEmpty( target.path ) {