From 89743eb4c647d05fcf7c990111c90deb75063dae Mon Sep 17 00:00:00 2001 From: aoyama Date: Wed, 22 Oct 2025 22:12:59 +0900 Subject: [PATCH 01/16] uring server --- src/corelib.pro | 6 + src/tactionroutine.cpp | 118 +++++++++ src/tactionroutine.h | 45 ++++ src/tapplicationserverbase.h | 1 + src/thttprequest.h | 2 + src/turingcoroutine.h | 21 ++ src/turingcoroutine_linux.cpp | 197 ++++++++++++++ src/turingserver.h | 104 ++++++++ src/turingserver_linux.cpp | 474 ++++++++++++++++++++++++++++++++++ 9 files changed, 968 insertions(+) create mode 100644 src/tactionroutine.cpp create mode 100644 src/tactionroutine.h create mode 100644 src/turingcoroutine.h create mode 100644 src/turingcoroutine_linux.cpp create mode 100644 src/turingserver.h create mode 100644 src/turingserver_linux.cpp diff --git a/src/corelib.pro b/src/corelib.pro index 2f2fbf594..869bdaef2 100644 --- a/src/corelib.pro +++ b/src/corelib.pro @@ -397,8 +397,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/tactionroutine.cpp b/src/tactionroutine.cpp new file mode 100644 index 000000000..122c582d2 --- /dev/null +++ b/src/tactionroutine.cpp @@ -0,0 +1,118 @@ +/* Copyright (c) 2010, 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 "tactionroutine.h" +//#include "TWebApplication" +//#include "TSqlDatabasePool" + +// static TActionRoutineManager *manager = 0; // For parent process +// TActionRoutine *TActionRoutine::currentActionRoutine = 0; // For child process + + +// TActionRoutineManager::TActionRoutineManager() : QObject() +// { +// timer.start(1000, this); +// } + + +// TActionRoutineManager::~TActionRoutineManager() +// { } + + +// TActionRoutineManager *TActionRoutineManager::instance() +// { +// return manager; +// } + + +// static void cleanupManager() +// { +// if (manager) { +// delete manager; +// manager = 0; +// } +// } + + +TActionRoutine::TActionRoutine() : + TActionContext() +{ + // if (!manager) { + // manager = new TActionRoutineManager(); + // qAddPostRoutine(cleanupManager); + // } + // setParent(manager); +} + + +TActionRoutine::~TActionRoutine() +{ +// closeDatabase(); +} + + +THttpResponse TActionRoutine::start(THttpRequest &request) +{ + execute(request); + + return THttpResponse(); +} + +// bool TActionRoutine::openDatabase() +// { +// QString env = tWebApp->databaseEnvironment(); +// QString type = TSqlDatabasePool::driverType(env); +// if (type.isEmpty()) { +// return false; +// } + +// sqlDatabase = QSqlDatabase::addDatabase(type); +// TSqlDatabasePool::openDatabase(sqlDatabase, env); +// return sqlDatabase.isValid(); +// } + + +// void TActionRoutine::closeDatabase() +// { +// if (sqlDatabase.isValid()) { +// sqlDatabase.rollback(); +// sqlDatabase.close(); +// sqlDatabase = QSqlDatabase(); // Sets an invalid object +// } +// } + + +// void TActionRoutine::emitError(int socketError) +// { +// emit error(socketError); +// } + + +// TActionRoutine *TActionRoutine::currentProcess() +// { +// return currentActionRoutine; +// } + + +// bool TActionRoutine::isChildProcess() +// { +// return (bool)currentActionRoutine; +// } + + +// void TActionRoutine::terminate(int status) +// { +// tSystemDebug("Child process({}) teminated. status:{}", childPid, status); +// emit finished(); +// } + + +// void TActionRoutine::kill(int sig) +// { +// tSystemDebug("Child process({}) killed. signal:{}", childPid, sig); +// emit finished(); +// } diff --git a/src/tactionroutine.h b/src/tactionroutine.h new file mode 100644 index 000000000..b1df256cd --- /dev/null +++ b/src/tactionroutine.h @@ -0,0 +1,45 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + + +class T_CORE_EXPORT TActionRoutine : public TActionContext +{ +public: + TActionRoutine(); + virtual ~TActionRoutine(); + + THttpResponse start(THttpRequest &request); + // static TActionRoutine *currentProcess(); + // static bool isChildProcess(); + +protected: + virtual int64_t writeResponse(THttpResponseHeader &, QIODevice *) override; + + +// virtual bool openDatabase(); +// virtual void closeDatabase(); +// virtual void emitError(int socketError); + +// protected slots: +// void terminate(int status); +// void kill(int sig); + +// signals: +// void started(); // for the parent +// void forked(); // for the child +// void finished(); // for the parent and the child +// void error(int socketError); + +// private: +// int childPid; +// static TActionRoutine *currentActionRoutine; + +// friend class TActionRoutineManager; + Q_DISABLE_COPY(TActionRoutine) +}; diff --git a/src/tapplicationserverbase.h b/src/tapplicationserverbase.h index 1097f051b..0953f8f1e 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/thttprequest.h b/src/thttprequest.h index 65e2a13ee..bacd17423 100644 --- a/src/thttprequest.h +++ b/src/thttprequest.h @@ -39,6 +39,8 @@ class T_CORE_EXPORT THttpRequest { THttpRequest(const QByteArray &header, const QString &filePath, const QHostAddress &clientAddress, TActionContext *context); virtual ~THttpRequest(); THttpRequest &operator=(const THttpRequest &other); + THttpRequest(THttpRequest&&) = default; + THttpRequest &operator=(THttpRequest &&) = default; const THttpRequestHeader &header() const { return d->header; } Tf::HttpMethod method() const; diff --git a/src/turingcoroutine.h b/src/turingcoroutine.h new file mode 100644 index 000000000..9d3f486d0 --- /dev/null +++ b/src/turingcoroutine.h @@ -0,0 +1,21 @@ +#pragma once +#include +class TActionContext; + + +struct Task { + struct promise_type { + Task get_return_object() { return {}; } + std::suspend_never initial_suspend() noexcept { return {}; } + std::suspend_never final_suspend() noexcept { return {}; } + void return_void() noexcept {} + void unhandled_exception() { std::terminate(); } + }; +}; + + +class TURingCoroutine { +public: + static Task incomingConnection(int socketDescriptor); + static TActionContext *currentContext(); +}; diff --git a/src/turingcoroutine_linux.cpp b/src/turingcoroutine_linux.cpp new file mode 100644 index 000000000..216290bfe --- /dev/null +++ b/src/turingcoroutine_linux.cpp @@ -0,0 +1,197 @@ +#include "turingcoroutine.h" +#include "turingserver.h" +#include "tactionroutine.h" +#include "TSystemGlobal" +#include "TAppSettings" +#include "THttpRequest" +#include +#include +#include +#include + + +class AsyncRecv : public TAwaitBase { +public: + AsyncRecv(int fd, void* buffer, size_t length, int msecs) : + _fd(fd), _buf(buffer), _len(length), _msecs(msecs) { } + + void await_suspend(std::coroutine_handle<> handle) + { + //std::print("== AsyncRecv \n"); + _handle = handle; + if (TURingServer::instance()->addRecv(_fd, _buf, _len, _msecs, this) < 0) { + tSystemError("addRecv error: {}", strerror(errno)); + } + } + + inline int await_resume() { return _cqeres; } + +private: + int _fd{0}; + void* _buf{nullptr}; + size_t _len{0}; + int _msecs{0}; +}; + + +class AsyncSend : public TAwaitBase { +public: + AsyncSend(int fd, const void* buf, size_t len) : + _fd(fd), _buf(buf), _len(len) { } + + void await_suspend(std::coroutine_handle<> handle) + { + //std::print("== AsyncSend \n"); + _handle = handle; + if (TURingServer::instance()->addSend(_fd, _buf, _len, this) < 0) { + tSystemError("addSend error: {}", strerror(errno)); + } + } + + inline int await_resume() { return _cqeres; } + +private: + int _fd{0}; + const void* _buf{nullptr}; + size_t _len{0}; +}; + + +template +class AsyncFunction : public TAwaitBase { +public: + explicit AsyncFunction(std::function f) : + TAwaitBase(), _func(std::move(f)) {} + ~AsyncFunction() { if (_fd > 0) ::close(_fd); } + + void await_suspend(std::coroutine_handle<> handle) + { + _handle = handle; + _fd = eventfd(0, (EFD_NONBLOCK | EFD_CLOEXEC)); + TURingServer::instance()->addEvent(_fd, this); + std::thread([this]() { + _result = _func(); + notifyResume(); + }).detach(); + } + + R await_resume() { return _result; } + +protected: + void notifyResume() + { + if (_fd > 0) { + uint64_t one = 1; + write(_fd, &one, sizeof(one)); // 通知 + } + } + +private: + int _fd{0}; + std::function _func; + R _result{}; +}; + + +// void AsyncRecv::await_suspend(std::coroutine_handle<> handle) +// { +// //std::print("== AsyncRecv \n"); +// _handle = handle; +// if (TURingServer::instance()->addRecv(_fd, _buf, _len, _msecs, this) < 0) { +// tSystemError("addRecv error: {}", strerror(errno)); +// } +// } + + +// void AsyncSend::await_suspend(std::coroutine_handle<> handle) +// { +// //std::print("== AsyncSend \n"); +// _handle = handle; +// if (TURingServer::instance()->addSend(_fd, _buf, _len, this) < 0) { +// tSystemError("addSend error: {}", strerror(errno)); +// } +// } + + +template +class ScopeExitFunction { +public: + explicit ScopeExitFunction(Func&& func) : _func(std::move(func)) {} + ~ScopeExitFunction() noexcept { _func(); } +private: + Func _func; +}; + + + +QStack _processingContext; + + +Task TURingCoroutine::incomingConnection(int sd) +{ + static int keepAlivetimeout = Tf::appSettings()->value(Tf::HttpKeepAliveTimeout).toInt(); + + //std::print("accept {}\n", sd); + ScopeExitFunction closing([sd]{ if (sd > 0) ::close(sd); }); + int res; + + // ソケット受信 + //int tmp = ++counter; + char buf[124]; + AsyncRecv receiver(sd, buf, sizeof(buf), 5000); + res = co_await receiver; + //std::print("[RECV] fd={} res={}\n", sd, len); + if (res <= 0) { + if (res < 0) { + tSystemError("Recv error fd:{} error:{}\n", sd, strerror(-res)); + } else { + tSystemError("Recv peer closed fd:{}\n", sd); + } + co_return; + } + + std::string s(buf, buf + res); + //std::print("[RECV] recv len:{} {}\n", res, s); + //std::print("[Main] ({}) Received ({} bytes): {}\n", tmp, len, s); + + + THttpRequest request; + auto context = std::make_unique(); + auto response = context->start(request); + + + // std::string response = "HTTP/1.1 200 OK\r\n" + // "Content-Type: text/plain; charset=utf-8\r\n" + // "Content-Length: 13\r\n" + // "Connection: close\r\n" + // "\r\n" + // "Hello world.\n"; + + +/* + if (sock->canReadRequest()) { + _processingSocketStack.push(sock); + sock->process(); + _processingSocketStack.pop(); + } +*/ + + std::string dummy; + AsyncSend sender(sd, dummy.c_str(), dummy.length()); + res = co_await sender; + if (res <= 0) { + tSystemError("Send error fd={} res={}\n", sd, res); + co_return; + } + //std::print("send ({}) len:{}\n", tmp, res); + //std::print("[SEND] fd={} res={}\n", sd, res); +} + + +TActionContext *TURingCoroutine::currentContext() +{ + if (!_processingContext.isEmpty()) { + return _processingContext.top(); + } + return nullptr; +} diff --git a/src/turingserver.h b/src/turingserver.h new file mode 100644 index 000000000..168e64e84 --- /dev/null +++ b/src/turingserver.h @@ -0,0 +1,104 @@ +#pragma once +#include "tatomic.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class QIODevice; +class THttpHeader; +class THttpSendBuffer; +class TEpollSocket; +class TActionWorker; +class TActionController; + + +class TAwaitBase { +public: + virtual ~TAwaitBase() { } + virtual bool await_ready() const noexcept { return false; } + inline void clear() + { + _cqeres = 0; + _sqecounter = 1; + } + + std::coroutine_handle<> _handle{}; + int _cqeres{0}; + int _sqecounter{1}; +}; + + +class T_CORE_EXPORT TURingServer : public TDatabaseContextThread, public TApplicationServerBase { + Q_OBJECT +public: + ~TURingServer(); + + bool isListening() const { return _listenSocket > 0; } + bool start(bool debugMode) override; + void stop() override; + void setAutoReloadingEnabled(bool enable) override; + bool isAutoReloadingEnabled() override; + //int processEvents(int maxMilliSeconds); + TActionContext *currentContext() const; + TActionController *currentController() const; + + static void instantiate(int listeningSocket); + static TURingServer *instance(); + + // + int addAccept(int fd, TAwaitBase* await = nullptr); + int addRecv(int fd, void* buf, size_t len, int msecs = 0, TAwaitBase* await = nullptr); + int addSend(int fd, const void* buf, size_t len, TAwaitBase* await = nullptr); + int addEvent(int fd, TAwaitBase* await = nullptr); + +protected: + void run() override; + //void timerEvent(QTimerEvent *event) override; + + +// signals: +// bool incomingRequest(TEpollSocket *socket); + +private: + io_uring _ring{}; + bool _stopped {false}; + int _listenSocket {0}; + //QBasicTimer reloadTimer; + bool _autoReload {false}; + //mutable QStack _processingSocketStack; + //mutable QSet _garbageSockets; + + TURingServer(int listeningSocket, QObject *parent = 0); // Constructor + + friend class TEpollSocket; + T_DISABLE_COPY(TURingServer) + T_DISABLE_MOVE(TURingServer) +}; + + +/* + * WorkerStarter class declaration + * This object creates worker threads in the main event loop. + */ +/* +class TWorkerStarter : public QObject +{ + Q_OBJECT +public: + TWorkerStarter(QObject *parent = 0) : QObject(parent) { } + virtual ~TWorkerStarter(); + +public slots: + void startWorker(TEpollSocket *); +}; +*/ + diff --git a/src/turingserver_linux.cpp b/src/turingserver_linux.cpp new file mode 100644 index 000000000..5010cbc83 --- /dev/null +++ b/src/turingserver_linux.cpp @@ -0,0 +1,474 @@ +/* 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 +#include +#include +//#include +#include +#include +#include +//#include +//#include +//#include +#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include + +constexpr int SEND_BUF_SIZE = 8 * 1024; +constexpr int RECV_BUF_SIZE = 16 * 1024; + +namespace { + +TURingServer *uringServer = nullptr; + +void cleanup() +{ + delete uringServer; + uringServer = nullptr; +} +} + + +void TURingServer::instantiate(int listeningSocket) +{ + if (!uringServer) { + uringServer = new TURingServer(listeningSocket); + qAddPostRoutine(::cleanup); + } +} + + +TURingServer *TURingServer::instance() +{ + if (Q_UNLIKELY(!uringServer)) { + tFatal("Call TURingServer::instantiate() function first"); + } + return uringServer; +} + + +static void setNoDeleyOption(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); +} + + +TURingServer::~TURingServer() +{ + 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; +} + +/* +int TURingServer::processEvents(int maxMilliSeconds) +{ + TEpoll::instance()->dispatchEvents(); + + // Poll Sending/Receiving/Incoming + maxMilliSeconds = std::max(maxMilliSeconds, 0); + int res = TEpoll::instance()->wait(maxMilliSeconds); + if (res < 0) { + return res; + } + + TEpollSocket *sock; + while ((sock = TEpoll::instance()->next())) { + + int cltfd = sock->socketDescriptor(); + if (cltfd == listenSocket && cltfd > 0) { + if (_processingSocketStack.count() > 64) { // Not accept in deep context + continue; + } + + TEpollSocket *acceptedSock = TEpollHttpSocket::accept(listenSocket); + if (Q_LIKELY(acceptedSock)) { + if (!acceptedSock->watch()) { + acceptedSock->dispose(); + } + } + continue; + + } else { + if (TEpoll::instance()->canSend()) { + if (sock->state() == Tf::SocketState::Connecting) { + sock->_state = Tf::SocketState::Connected; + continue; + } + + // Send data + int len = TEpoll::instance()->send(sock); + if (Q_UNLIKELY(len < 0)) { + TEpoll::instance()->deletePoll(sock); + sock->dispose(); + continue; + } + } + + if (TEpoll::instance()->canReceive()) { + try { + // Receive data + int len = TEpoll::instance()->recv(sock); + if (Q_UNLIKELY(len < 0)) { + TEpoll::instance()->deletePoll(sock); + sock->dispose(); + continue; + } + } catch (ClientErrorException &e) { + Tf::warn("Caught ClientErrorException: status code:{}", e.statusCode()); + tSystemWarn("Caught ClientErrorException: status code:{}", e.statusCode()); + TEpoll::instance()->deletePoll(sock); + sock->dispose(); + continue; + } + + if (sock->canReadRequest()) { + _processingSocketStack.push(sock); + sock->process(); + _processingSocketStack.pop(); + } + } + } + } + + // Garbage + if (!_garbageSockets.isEmpty()) { + auto set = _garbageSockets; + for (auto ptr : set) { + if (!ptr->isProcessing()) { + delete ptr; // Remove it from garbageSockets-set in the destructor + } + } + } + + return res; +} +*/ + +template +class ScopeExitFunction { +public: + explicit ScopeExitFunction(Func&& func) : _func(std::move(func)) {} + ~ScopeExitFunction() noexcept { _func(); } +private: + Func _func; +}; + + +void TURingServer::run() +{ + setNoDeleyOption(_listenSocket); + + // if (newerLibraryExists()) { + // tSystemInfo("Detect new library of application. Reloading the libraries."); + // Tf::app()->exit(127); + // } + + + //std::cout << "# Event Loop\n"; + //_stop = false; + + TAwaitBase accepter; + instance()->addAccept(_listenSocket, &accepter); + + struct __kernel_timespec ts { + .tv_sec = 2, // 2秒 + .tv_nsec = 0 + }; + + // --- イベントループ --- + while (!_stopped) { + io_uring_cqe* cqe = nullptr; + int res = io_uring_wait_cqe_timeout(&_ring, &cqe, &ts); + ScopeExitFunction seen([&]{ if (cqe) io_uring_cqe_seen(&_ring, cqe); }); + + if (res < 0 || !cqe) { + 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) { + auto* await = static_cast(user_data); + if (await == &accepter) { + // マルチショット accept 結果 + if (cqe->res >= 0) { + // コルーチンを起動 + //handleClient(cqe->res); + TURingCoroutine::incomingConnection(cqe->res); + await->_cqeres = 0; // clear + } else { + int err = -cqe->res; + switch (err) { + case EAGAIN: + case ECONNABORTED: + case ECANCELED: + // ignore + break; + case EINVAL: + case EBADF: + case ENOTSOCK: + // listen_fd が閉じられた → 終了処理 + tSystemError("Listen socket invalid, terminating. error: {}\n", strerror(err)); + stop(); + break; + default: + tSystemError("Accept error: {}\n", strerror(err)); + stop(); + break; + } + } + + if (!(cqe->flags & IORING_CQE_F_MORE)) { + //std::print(std::cerr, "flags & IORING_CQE_F_MORE\n"); + if (addAccept(_listenSocket, &accepter) < 0) { + tSystemError("addAccept error: {}", strerror(errno)); + } + } + } else { + if (!await->_cqeres) { + await->_cqeres = cqe->res; + } + // recv/send + //std::print("cqe->res:{} cqe->flags: {}\n", await->_cqeres, cqe->flags); + //std::print("#[Loop] resume res:{}\n", await->_cqeres); + if (--await->_sqecounter == 0) { + if (await->_handle && !await->_handle.done()) { + //std::print("[Loop] resume res:{}\n", await->_cqeres); + await->_handle.resume(); + } + } + } + } + } +} + + +void TURingServer::stop() +{ + _stopped = true; + if (isRunning()) { + QThread::wait(10000); + } + TStaticReleaseThread::exec(); +} + + +void TURingServer::setAutoReloadingEnabled(bool enable) +{ + _autoReload = enable; + // if (enable) { + // reloadTimer.start(500, this); + // } else { + // reloadTimer.stop(); + // } +} + + +bool TURingServer::isAutoReloadingEnabled() +{ + return _autoReload; + //return reloadTimer.isActive(); +} + + +TActionContext *TURingServer::currentContext() const +{ + return TURingCoroutine::currentContext(); + + // if (!_processingSocketStack.isEmpty()) { + // auto *worker = _processingSocketStack.top(); + // return worker; + // } + // return nullptr; +} + + +TActionController *TURingServer::currentController() const +{ + auto *context = currentContext(); + return (context) ? context->currentController() : nullptr; +} + +/* +void TURingServer::timerEvent(QTimerEvent *event) +{ + if (event->timerId() != reloadTimer.timerId()) { + QThread::timerEvent(event); + } else { + if (newerLibraryExists()) { + tSystemInfo("Detect new library of application. Reloading the libraries."); + Tf::app()->exit(127); + } + } +} +*/ + +// アクセプト +int TURingServer::addAccept(int fd, TAwaitBase* await) +{ + //std::print("addAccept fd: {}\n", fd); + io_uring_sqe* sqe = io_uring_get_sqe(&_ring); + io_uring_prep_multishot_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); +} + +// 受信 +int TURingServer::addRecv(int fd, void* buf, size_t len, int msecs, TAwaitBase* await) +{ + //std::print("addRecv fd: {}\n", fd); + io_uring_sqe* sqe = io_uring_get_sqe(&_ring); + 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); + __kernel_timespec ts; + ts.tv_sec = msecs / 1000; + ts.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); +} + +int TURingServer::addSend(int fd, const void* buf, size_t len, TAwaitBase* await) +{ + //std::print("addSend fd: {}\n", fd); + io_uring_sqe* sqe = io_uring_get_sqe(&_ring); + 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); +} + +// int addPoll(int fd, int mask, int msecs, TAwaitBase* await) +// { +// io_uring_sqe* sqe1 = io_uring_get_sqe(&_ring); +// io_uring_prep_poll_add(sqe1, fd, mask); +// io_uring_sqe_set_data(sqe1, await); + +// if (msecs > 0) { +// sqe1->flags |= IOSQE_IO_LINK; +// io_uring_sqe* sqe2 = io_uring_get_sqe(&_ring); +// __kernel_timespec ts; +// ts.tv_sec = msecs / 1000; +// ts.tv_nsec = (msecs % 1000) * 1'000'000; +// io_uring_prep_link_timeout(sqe2, &ts, 0); +// io_uring_sqe_set_data(sqe2, await); +// if (await) { +// await->_sqecounter = 2; +// } +// } + +// return io_uring_submit(&_ring); +// } + +// 状態変数待ち +int TURingServer::addEvent(int fd, TAwaitBase* await) +{ + io_uring_sqe* sqe = io_uring_get_sqe(&_ring); + 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); +} From 324cbe247380d5320b3ab4fc9a7872950888f4ee Mon Sep 17 00:00:00 2001 From: aoyama Date: Mon, 27 Oct 2025 20:12:38 +0900 Subject: [PATCH 02/16] temp --- include/headers.pri | 2 + src/corelib.pro | 4 +- src/tabstractwebsocket.cpp | 16 ++- src/taccesslog.h | 9 +- src/tactioncontext.cpp | 42 +++--- src/tactioncontext.h | 4 +- src/tactioncontroller.cpp | 8 +- src/tactioncontroller.h | 14 +- src/tactionthread.cpp | 41 +++--- src/tactionthread.h | 2 +- src/tactionworker.cpp | 5 +- src/tapplicationserverbase.h | 2 +- src/tdatabasecontext.cpp | 54 +++++-- src/tdatabasecontext.h | 15 +- src/tepollhttpsocket.cpp | 4 +- src/tfnamespace.h | 12 +- src/tformvalidator.cpp | 118 ++++++++-------- src/tglobal.cpp | 22 ++- src/tglobal.h | 10 ++ src/thttpheader.cpp | 4 +- src/thttpheader.h | 5 +- src/thttprequest.cpp | 95 +++++++------ src/thttprequest.h | 17 ++- src/thttpsocket.cpp | 12 +- src/thttpsocket.h | 2 +- src/thttputility.cpp | 84 +++++------ src/thttputility.h | 2 +- src/tkvsdatabase.cpp | 22 +-- src/tkvsdatabase.h | 7 +- src/tmemcached.h | 2 +- src/tmongoquery.cpp | 30 ++-- src/tmongoquery.h | 9 +- src/tpublisher.cpp | 12 +- src/tredis.h | 2 +- src/tsendbuffer.cpp | 2 +- src/tsendbuffer.h | 2 +- src/tsharedmemorykvs.h | 2 +- src/tsqltransaction.h | 8 +- src/ttcpsocket.cpp | 4 +- src/tthreadapplicationserver.cpp | 15 ++ src/tthreadapplicationserver.h | 3 +- src/turingcoroutine.h | 22 ++- src/turingcoroutine_linux.cpp | 228 ++++++++++++++++++++---------- src/turingserver.h | 21 +-- src/turingserver_linux.cpp | 115 ++++++++------- src/turlroute.cpp | 28 ++-- src/turlroute.h | 22 +-- src/tviewhelper.cpp | 4 +- src/tviewhelper.h | 14 +- src/twebapplication.cpp | 23 ++- src/twebapplication.h | 9 +- tools/tfmanager/main.cpp | 5 +- tools/tfmanager/servermanager.cpp | 2 +- tools/tfmanager/tfmanager.pro | 2 +- tools/tfserver/main.cpp | 50 ++++--- tools/tfserver/tfserver.pro | 2 +- tools/tspawn/tspawn.pro | 2 +- 57 files changed, 752 insertions(+), 522 deletions(-) diff --git a/include/headers.pri b/include/headers.pri index be620c106..4448b0570 100644 --- a/include/headers.pri +++ b/include/headers.pri @@ -202,6 +202,8 @@ HEADER_FILES += tmemcacheddriver.h HEADER_FILES += tsystemlogger.h HEADER_FILES += tstdoutsystemlogger.h HEADER_FILES += tstderrsystemlogger.h +HEADER_FILES += turingserver.h +HEADER_FILES += turingcoroutine.h HEADER_FILES += tsqldatabasepool.h HEADER_FILES += tkvsdatabasepool.h diff --git a/src/corelib.pro b/src/corelib.pro index 869bdaef2..7010e3d54 100644 --- a/src/corelib.pro +++ b/src/corelib.pro @@ -403,8 +403,8 @@ linux-* { SOURCES += turingcoroutine_linux.cpp HEADERS += tactionworker.h SOURCES += tactionworker.cpp - HEADERS += tactionroutine.h - SOURCES += tactionroutine.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..6d3163de3 100644 --- a/src/tabstractwebsocket.cpp +++ b/src/tabstractwebsocket.cpp @@ -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.h b/src/taccesslog.h index d67ca01f9..1c0afa9a2 100644 --- a/src/taccesslog.h +++ b/src/taccesslog.h @@ -54,7 +54,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 +68,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) { diff --git a/src/tactioncontext.cpp b/src/tactioncontext.cpp index 303c4a295..027f33dfa 100644 --- a/src/tactioncontext.cpp +++ b/src/tactioncontext.cpp @@ -87,7 +87,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 +140,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 +169,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 +193,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 +215,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 +244,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 +252,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 +391,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 { @@ -413,15 +413,15 @@ void TActionContext::flushResponse(TActionController *controller, bool immediate } -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 +432,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 +441,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)); diff --git a/src/tactioncontext.h b/src/tactioncontext.h index 78e908682..e41a4ab15 100644 --- a/src/tactioncontext.h +++ b/src/tactioncontext.h @@ -38,8 +38,8 @@ class T_CORE_EXPORT TActionContext : public TDatabaseContext, public TAbstractAc 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/tactioncontroller.cpp b/src/tactioncontroller.cpp index d01e983a4..4536d584b 100644 --- a/src/tactioncontroller.cpp +++ b/src/tactioncontroller.cpp @@ -550,7 +550,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 +559,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 +589,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 +874,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..bb67cad9a 100644 --- a/src/tactionthread.cpp +++ b/src/tactionthread.cpp @@ -111,30 +111,31 @@ 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); - } + // for (auto &req : requests) { + // TActionContext::execute(req); + // } + TActionContext::execute(request); if (keepAliveTimeout() == 0) { break; @@ -154,12 +155,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 +186,18 @@ void TActionThread::emitError(int socketError) } -QList TActionThread::readRequest(THttpSocket *socket) +THttpRequest TActionThread::readRequest(THttpSocket *socket) { - QList reqs; + //THttpRequest req; for (;;) { if (socket->waitForReadyReadRequest(500)) { - reqs = socket->read(); - if (!reqs.isEmpty()) { - return reqs; - } else { - break; - } + return socket->read(); + // if (!req.isEmpty()) { + // return req; + // } else { + // break; + // } } if (Q_UNLIKELY(socket->state() != QAbstractSocket::ConnectedState)) { @@ -211,7 +212,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..5f50cff9e 100644 --- a/src/tactionworker.cpp +++ b/src/tactionworker.cpp @@ -68,9 +68,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 +80,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 0953f8f1e..ef9c12153 100644 --- a/src/tapplicationserverbase.h +++ b/src/tapplicationserverbase.h @@ -35,7 +35,7 @@ class T_CORE_EXPORT TApplicationServerBase { friend class TThreadApplicationServer; friend class TMultiplexingServer; - friend class TURingServer; + friend class TUringServer; T_DISABLE_COPY(TApplicationServerBase) T_DISABLE_MOVE(TApplicationServerBase) }; diff --git a/src/tdatabasecontext.cpp b/src/tdatabasecontext.cpp index 860392d83..73b6c2df4 100644 --- a/src/tdatabasecontext.cpp +++ b/src/tdatabasecontext.cpp @@ -22,17 +22,22 @@ namespace { // - qulonglong type to prevent qThreadStorage_deleteData() function to work QThreadStorage databaseContextPtrTls; QSqlDatabase invalidDb; + } +// std::map TDatabaseContext::sqlDatabases; +// std::map TDatabaseContext::kvsDatabases; + /*! \class TDatabaseContext \brief The TDatabaseContext class is the base class of contexts for database access. */ -TDatabaseContext::TDatabaseContext() : - sqlDatabases(), - kvsDatabases() +TDatabaseContext::TDatabaseContext() +//: + // sqlDatabases(), + // kvsDatabases() { } @@ -46,13 +51,20 @@ TDatabaseContext::~TDatabaseContext() QSqlDatabase &TDatabaseContext::getSqlDatabase(int id) { +tSystemDebug("getSqlDatabase #0"); + if (sqlDatabases.size() < (size_t)Tf::app()->sqlDatabaseSettingsCount()) { + sqlDatabases.resize(Tf::app()->sqlDatabaseSettingsCount()); + } + if (id < 0) { return invalidDb; // invalid database } +tSystemDebug("getSqlDatabase #1"); if (id >= Tf::app()->sqlDatabaseSettingsCount()) { throw RuntimeException("error database id", __FILE__, __LINE__); } +tSystemDebug("getSqlDatabase #2"); TSqlTransaction &tx = sqlDatabases[id]; QSqlDatabase &db = tx.database(); @@ -60,6 +72,7 @@ QSqlDatabase &TDatabaseContext::getSqlDatabase(int id) if (db.isValid() && tx.isActive()) { return db; } +tSystemDebug("getSqlDatabase #3"); int n = 0; do { @@ -73,6 +86,7 @@ QSqlDatabase &TDatabaseContext::getSqlDatabase(int id) TSqlDatabasePool::instance()->pool(db, true); } while (++n < 2); // try two times + tSystemDebug("getSqlDatabase #4"); idleElapsed = (uint)std::time(nullptr); return db; } @@ -87,6 +101,10 @@ void TDatabaseContext::releaseSqlDatabases() TKvsDatabase &TDatabaseContext::getKvsDatabase(Tf::KvsEngine engine) { + if (kvsDatabases.size() < (size_t)Tf::app()->sqlDatabaseSettingsCount()) { + kvsDatabases.resize(Tf::app()->sqlDatabaseSettingsCount()); + } + TKvsDatabase &db = kvsDatabases[(int)engine]; if (!db.isValid()) { db = TKvsDatabasePool::instance()->database(engine); @@ -99,8 +117,9 @@ TKvsDatabase &TDatabaseContext::getKvsDatabase(Tf::KvsEngine engine) void TDatabaseContext::releaseKvsDatabases() { - for (QMap::iterator it = kvsDatabases.begin(); it != kvsDatabases.end(); ++it) { - TKvsDatabasePool::instance()->pool(it.value()); + for (auto it = kvsDatabases.begin(); it != kvsDatabases.end(); ++it) { + // TKvsDatabasePool::instance()->pool(it->second); + TKvsDatabasePool::instance()->pool(*it); } kvsDatabases.clear(); } @@ -124,16 +143,19 @@ void TDatabaseContext::setTransactionEnabled(bool enable, int id) Tf::error("Invalid database ID: {}", id); return; } +tSystemDebug("### setTransactionEnabled : id:{}", id); return sqlDatabases[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 = sqlDatabases.begin(); it != sqlDatabases.end(); ++it) { + // TSqlTransaction &tx = it->second; + // tx.commit(); + // TSqlDatabasePool::instance()->pool(tx.database()); + it->commit(); + TSqlDatabasePool::instance()->pool(it->database()); } } @@ -142,7 +164,7 @@ bool TDatabaseContext::commitTransaction(int id) { bool res = false; - if (id < 0 || id >= sqlDatabases.count()) { + if (id < 0 || id >= (int)sqlDatabases.size()) { Tf::error("Failed to commit transaction. Invalid database ID: {}", id); return res; } @@ -156,10 +178,12 @@ bool TDatabaseContext::commitTransaction(int id) 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 = sqlDatabases.begin(); it != sqlDatabases.end(); ++it) { + // TSqlTransaction &tx = it->second; + // tx.rollback(); + // TSqlDatabasePool::instance()->pool(tx.database(), true); + it->rollback(); + TSqlDatabasePool::instance()->pool(it->database(), true); } } @@ -168,7 +192,7 @@ bool TDatabaseContext::rollbackTransaction(int id) { bool res = false; - if (id < 0 || id >= sqlDatabases.count()) { + if (id < 0 || id >= (int)sqlDatabases.size()) { Tf::error("Failed to rollback transaction. Invalid database ID: {}", id); return res; } diff --git a/src/tdatabasecontext.h b/src/tdatabasecontext.h index 4baecbf50..6c95ed0c6 100644 --- a/src/tdatabasecontext.h +++ b/src/tdatabasecontext.h @@ -1,5 +1,6 @@ #pragma once -#include +//#include +#include #include #include @@ -12,6 +13,8 @@ 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); @@ -31,14 +34,18 @@ class T_CORE_EXPORT TDatabaseContext { void releaseKvsDatabases(); void releaseSqlDatabases(); - QMap sqlDatabases; - QMap kvsDatabases; + // QMap sqlDatabases; + // QMap kvsDatabases; + //std::map sqlDatabases; + //std::map kvsDatabases; + std::vector sqlDatabases; + std::vector kvsDatabases; private: uint idleElapsed {0}; TCache *cachep {nullptr}; T_DISABLE_COPY(TDatabaseContext) - T_DISABLE_MOVE(TDatabaseContext) + //T_DISABLE_MOVE(TDatabaseContext) }; 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/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..5e94bf045 100644 --- a/src/tglobal.cpp +++ b/src/tglobal.cpp @@ -121,19 +121,33 @@ 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 + tFatal("Error TODO TODO"); + // TODO TODO TODO TODO TODO TODO TODO +#else + tFatal("Unsupported MPM: uring"); +#endif + break; + + default: + break; } throw RuntimeException("Can not cast the current thread", __FILE__, __LINE__); @@ -144,7 +158,7 @@ TDatabaseContext *Tf::currentDatabaseContext() { TDatabaseContext *context; - if (Tf::app()->multiProcessingModule() == TWebApplication::Epoll) { + if (Tf::app()->multiProcessingModule() == TWebApplication::MultiProcessingModule::Epoll) { #ifdef Q_OS_LINUX context = TMultiplexingServer::instance()->currentWorker(); if (context) { diff --git a/src/tglobal.h b/src/tglobal.h index b0fc94f43..205f55f8d 100644 --- a/src/tglobal.h +++ b/src/tglobal.h @@ -249,6 +249,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/thttpheader.cpp b/src/thttpheader.cpp index af47bb7d7..10d4f2943 100644 --- a/src/thttpheader.cpp +++ b/src/thttpheader.cpp @@ -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; diff --git a/src/thttpheader.h b/src/thttpheader.h index cafb75ca4..1cfc5bfb1 100644 --- a/src/thttpheader.h +++ b/src/thttpheader.h @@ -34,6 +34,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; @@ -47,8 +48,8 @@ class T_CORE_EXPORT THttpResponseHeader : public THttpHeader { THttpResponseHeader(const THttpResponseHeader &other); THttpResponseHeader(const QByteArray &str); - 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(); diff --git a/src/thttprequest.cpp b/src/thttprequest.cpp index 780ed49a9..0559e84ba 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}, }; @@ -43,7 +43,7 @@ static bool httpMethodOverride() */ THttpRequestData::THttpRequestData(const THttpRequestData &other) : - QSharedData(other), + //QSharedData(other), header(other.header), bodyArray(other.bodyArray), queryItems(other.queryItems), @@ -64,7 +64,8 @@ THttpRequestData::THttpRequestData(const THttpRequestData &other) : Constructor. */ THttpRequest::THttpRequest() : - d(new THttpRequestData) + d(std::make_unique()) + //d(new THttpRequestData) { } @@ -72,16 +73,17 @@ THttpRequest::THttpRequest() : \fn THttpRequest::THttpRequest(const THttpRequest &other) Copy constructor. */ -THttpRequest::THttpRequest(const THttpRequest &other) : - d(other.d) -{ -} +// THttpRequest::THttpRequest(const THttpRequest &other) : +// d(other.d) +// { +// } /*! 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(new THttpRequestData) + d(std::make_unique()) { d->header = header; d->clientAddress = clientAddress; @@ -116,15 +118,16 @@ THttpRequest::THttpRequest(const QByteArray &header, const QString &filePath, co */ THttpRequest::~THttpRequest() { - if (bodyDevice) { - bodyDevice->close(); - delete bodyDevice; - } + // if (bodyDevice) { + // bodyDevice->close(); + // delete bodyDevice; + // } } /*! Assignment operator. */ +/* THttpRequest &THttpRequest::operator=(const THttpRequest &other) { if (bodyDevice) { @@ -136,6 +139,7 @@ THttpRequest &THttpRequest::operator=(const THttpRequest &other) d = other.d; return *this; } +*/ /*! Returns the method of an HTTP request, which can be overridden by @@ -146,15 +150,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 +171,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 +181,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 +204,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); } @@ -488,9 +492,9 @@ QVariantMap THttpRequest::formItems() const 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()) { @@ -512,7 +516,7 @@ void THttpRequest::parseBody(const QByteArray &body, const THttpRequestHeader &h } } /* FALLTHRU */ - case Tf::Get: { + case Tf::HttpMethod::Get: { // query parameter QByteArrayList data = header.path().split('?'); QString query = QString::fromLatin1(data.value(1)); @@ -589,23 +593,24 @@ QVariantMap THttpRequest::allParameters() const } -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 +618,7 @@ QList THttpRequest::generate(QByteArray &byteArray, const QHostAdd } else { byteArray.remove(0, from); } - return reqList; + return request; } /*! @@ -669,12 +674,14 @@ QIODevice *THttpRequest::rawBody() { if (!bodyDevice) { if (!d->multipartFormData.bodyFile.isEmpty()) { - bodyDevice = new QFile(d->multipartFormData.bodyFile); + auto p = new QFile(d->multipartFormData.bodyFile); + bodyDevice.reset(p); } else { - bodyDevice = new QBuffer(&d->bodyArray); + auto p = new QBuffer(&d->bodyArray); + bodyDevice.reset(p); } } - return bodyDevice; + return bodyDevice.get(); } diff --git a/src/thttprequest.h b/src/thttprequest.h index bacd17423..9ff9171c7 100644 --- a/src/thttprequest.h +++ b/src/thttprequest.h @@ -10,12 +10,14 @@ #include #include #include +#include class TActionContext; class QIODevice; -class T_CORE_EXPORT THttpRequestData : public QSharedData { +//class T_CORE_EXPORT THttpRequestData : public QSharedData { +class T_CORE_EXPORT THttpRequestData { public: THttpRequestData() { } THttpRequestData(const THttpRequestData &other); @@ -34,11 +36,11 @@ class T_CORE_EXPORT THttpRequestData : public QSharedData { class T_CORE_EXPORT THttpRequest { public: THttpRequest(); - THttpRequest(const THttpRequest &other); + //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 &operator=(const THttpRequest &other); THttpRequest(THttpRequest&&) = default; THttpRequest &operator=(THttpRequest &&) = default; @@ -50,6 +52,7 @@ class T_CORE_EXPORT THttpRequest { QString parameter(const QString &name) const; QVariantMap allParameters() const; + bool isEmpty() const { return d->header.isEmpty(); } bool hasQuery() const { return !d->queryItems.isEmpty(); } bool hasQueryItem(const QString &name) const; QString queryItemValue(const QString &name) const; @@ -77,7 +80,7 @@ class T_CORE_EXPORT THttpRequest { bool hasJson() const { return !d->jsonData.isNull(); } const QJsonDocument &jsonData() const { return d->jsonData; } - 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: @@ -93,8 +96,10 @@ class T_CORE_EXPORT THttpRequest { private: void parseBody(const QByteArray &body, const THttpRequestHeader &header, TActionContext *context); - QSharedDataPointer d; - QIODevice *bodyDevice {nullptr}; + //QSharedDataPointer d; + //QIODevice *bodyDevice {nullptr}; + std::unique_ptr d; + std::unique_ptr bodyDevice; friend class TMultipartFormData; }; 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..884daaedf 100644 --- a/src/thttputility.cpp +++ b/src/thttputility.cpp @@ -22,50 +22,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 +320,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); } /*! 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/tkvsdatabase.cpp b/src/tkvsdatabase.cpp index d4bb87f3c..7fff3fc10 100644 --- a/src/tkvsdatabase.cpp +++ b/src/tkvsdatabase.cpp @@ -117,11 +117,11 @@ TKvsDatabaseData TKvsDatabase::settings(const QString &connectionName) } -TKvsDatabase::TKvsDatabase(const TKvsDatabase &other) : - connectName(other.connectName), - drv(other.drv) -{ -} +// TKvsDatabase::TKvsDatabase(const TKvsDatabase &other) : +// connectName(other.connectName), +// drv(other.drv) +// { +// } TKvsDatabase::TKvsDatabase(const QString &connectionName, TKvsDriver *driver) : @@ -138,12 +138,12 @@ TKvsDatabase::TKvsDatabase(const TKvsDatabaseData &data) : } -TKvsDatabase &TKvsDatabase::operator=(const TKvsDatabase &other) -{ - connectName = other.connectName; - drv = other.drv; - return *this; -} +// TKvsDatabase &TKvsDatabase::operator=(const TKvsDatabase &other) +// { +// connectName = other.connectName; +// drv = other.drv; +// return *this; +// } bool TKvsDatabase::isValid() const diff --git a/src/tkvsdatabase.h b/src/tkvsdatabase.h index 1e50d2fd5..c7fdba5d9 100644 --- a/src/tkvsdatabase.h +++ b/src/tkvsdatabase.h @@ -9,9 +9,11 @@ class TKvsDatabaseData; class T_CORE_EXPORT TKvsDatabase { public: TKvsDatabase() { } - TKvsDatabase(const TKvsDatabase &other); + //TKvsDatabase(const TKvsDatabase &other); + TKvsDatabase(TKvsDatabase &&) = default; ~TKvsDatabase() { } - TKvsDatabase &operator=(const TKvsDatabase &other); + //TKvsDatabase &operator=(const TKvsDatabase &other); + TKvsDatabase &operator=(TKvsDatabase &&) = default; QString driverName() const; QString connectionName() const { return connectName; } @@ -51,6 +53,7 @@ class T_CORE_EXPORT TKvsDatabase { TKvsDatabase(const QString &connectionName, TKvsDriver *driver); TKvsDatabase(const TKvsDatabaseData &data); + T_DISABLE_COPY(TKvsDatabase) }; diff --git a/src/tmemcached.h b/src/tmemcached.h index 04bb69890..7697bc2b1 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 &_database; friend class TCacheMemcachedStore; T_DISABLE_COPY(TMemcached) diff --git a/src/tmongoquery.cpp b/src/tmongoquery.cpp index dcfddfb1a..a865975c9 100644 --- a/src/tmongoquery.cpp +++ b/src/tmongoquery.cpp @@ -39,25 +39,25 @@ 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) -{ -} +// 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; -} +// 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 diff --git a/src/tmongoquery.h b/src/tmongoquery.h index a94a25e10..4fe6c43cc 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 &_database; QString _collection; int _queryLimit {0}; int _queryOffset {0}; friend class TCacheMongoStore; + T_DISABLE_COPY(TMongoQuery) }; diff --git a/src/tpublisher.cpp b/src/tpublisher.cpp index 2e7e69327..5d027ed4e 100644 --- a/src/tpublisher.cpp +++ b/src/tpublisher.cpp @@ -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/tredis.h b/src/tredis.h index f774908bb..5dde9e2e1 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 &_database; friend class TCacheRedisStore; T_DISABLE_COPY(TRedis) diff --git a/src/tsendbuffer.cpp b/src/tsendbuffer.cpp index 390c671c1..3b4204d05 100644 --- a/src/tsendbuffer.cpp +++ b/src/tsendbuffer.cpp @@ -38,7 +38,7 @@ 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); 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/tsharedmemorykvs.h b/src/tsharedmemorykvs.h index db26037cf..b744ae1d1 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 &_database; hash_header_t *_h {nullptr}; friend class TCacheSharedMemoryStore; diff --git a/src/tsqltransaction.h b/src/tsqltransaction.h index c423d4a8e..91c20bbef 100644 --- a/src/tsqltransaction.h +++ b/src/tsqltransaction.h @@ -10,10 +10,12 @@ class T_CORE_EXPORT TSqlTransaction { public: TSqlTransaction(); - TSqlTransaction(const TSqlTransaction &other) = default; + //TSqlTransaction(const TSqlTransaction &other) = default; + TSqlTransaction(TSqlTransaction &&) = default; ~TSqlTransaction(); + //TSqlTransaction &operator=(const TSqlTransaction &) = default; + TSqlTransaction &operator=(TSqlTransaction &&) = default; - TSqlTransaction &operator=(const TSqlTransaction &) = default; QSqlDatabase &database() { return _database; } bool begin(); bool commit(); @@ -27,6 +29,8 @@ class T_CORE_EXPORT TSqlTransaction { bool _enabled {true}; bool _active {false}; QString _connectionName; + + T_DISABLE_COPY(TSqlTransaction) }; 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..7602fb2d5 100644 --- a/src/tthreadapplicationserver.cpp +++ b/src/tthreadapplicationserver.cpp @@ -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..baea8bd1d 100644 --- a/src/tthreadapplicationserver.h +++ b/src/tthreadapplicationserver.h @@ -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); @@ -53,6 +53,7 @@ class T_CORE_EXPORT TThreadApplicationServer : public QThread, public TApplicati private: static TStack *threadPoolPtr(); + TThreadApplicationServer(int listeningSocket, QObject *parent = nullptr); int listenSocket {0}; int maxThreads {0}; diff --git a/src/turingcoroutine.h b/src/turingcoroutine.h index 9d3f486d0..c3b4af132 100644 --- a/src/turingcoroutine.h +++ b/src/turingcoroutine.h @@ -1,6 +1,7 @@ #pragma once +#include +#include #include -class TActionContext; struct Task { @@ -14,8 +15,21 @@ struct Task { }; -class TURingCoroutine { +class TUringCoroutine : public TActionContext { public: - static Task incomingConnection(int socketDescriptor); - static TActionContext *currentContext(); + TUringCoroutine() : TActionContext() {} + virtual ~TUringCoroutine() {} + + Task start(int socketDescriptor); + // static TActionRoutine *currentProcess(); + // static bool isChildProcess(); + //QByteArray response() const { return _response; } + //static TActionContext *currentContext(); + +protected: + virtual int64_t writeResponse(THttpResponseHeader &, QIODevice *) override; + +private: + QByteArray _response; + QFile _file; }; diff --git a/src/turingcoroutine_linux.cpp b/src/turingcoroutine_linux.cpp index 216290bfe..2821761af 100644 --- a/src/turingcoroutine_linux.cpp +++ b/src/turingcoroutine_linux.cpp @@ -1,14 +1,17 @@ #include "turingcoroutine.h" #include "turingserver.h" -#include "tactionroutine.h" +//#include "tactionroutine.h" #include "TSystemGlobal" #include "TAppSettings" #include "THttpRequest" +#include "TTemporaryFile" #include #include #include #include +constexpr uint READ_THRESHOLD_LENGTH = 4 * 1024 * 1024; // bytes + class AsyncRecv : public TAwaitBase { public: @@ -19,7 +22,7 @@ class AsyncRecv : public TAwaitBase { { //std::print("== AsyncRecv \n"); _handle = handle; - if (TURingServer::instance()->addRecv(_fd, _buf, _len, _msecs, this) < 0) { + if (TUringServer::instance()->addRecv(_fd, _buf, _len, _msecs, this) < 0) { tSystemError("addRecv error: {}", strerror(errno)); } } @@ -27,23 +30,23 @@ class AsyncRecv : public TAwaitBase { inline int await_resume() { return _cqeres; } private: - int _fd{0}; - void* _buf{nullptr}; - size_t _len{0}; - int _msecs{0}; + int _fd {0}; + void* _buf {nullptr}; + size_t _len {0}; + int _msecs {0}; }; class AsyncSend : public TAwaitBase { public: - AsyncSend(int fd, const void* buf, size_t len) : + AsyncSend(int fd, const void *buf, size_t len) : _fd(fd), _buf(buf), _len(len) { } void await_suspend(std::coroutine_handle<> handle) { //std::print("== AsyncSend \n"); _handle = handle; - if (TURingServer::instance()->addSend(_fd, _buf, _len, this) < 0) { + if (TUringServer::instance()->addSend(_fd, _buf, _len, this) < 0) { tSystemError("addSend error: {}", strerror(errno)); } } @@ -51,9 +54,9 @@ class AsyncSend : public TAwaitBase { inline int await_resume() { return _cqeres; } private: - int _fd{0}; - const void* _buf{nullptr}; - size_t _len{0}; + int _fd {0}; + const void* _buf {nullptr}; + size_t _len {0}; }; @@ -68,7 +71,7 @@ class AsyncFunction : public TAwaitBase { { _handle = handle; _fd = eventfd(0, (EFD_NONBLOCK | EFD_CLOEXEC)); - TURingServer::instance()->addEvent(_fd, this); + TUringServer::instance()->addEvent(_fd, this); std::thread([this]() { _result = _func(); notifyResume(); @@ -87,32 +90,12 @@ class AsyncFunction : public TAwaitBase { } private: - int _fd{0}; + int _fd {0}; std::function _func; - R _result{}; + R _result {}; }; -// void AsyncRecv::await_suspend(std::coroutine_handle<> handle) -// { -// //std::print("== AsyncRecv \n"); -// _handle = handle; -// if (TURingServer::instance()->addRecv(_fd, _buf, _len, _msecs, this) < 0) { -// tSystemError("addRecv error: {}", strerror(errno)); -// } -// } - - -// void AsyncSend::await_suspend(std::coroutine_handle<> handle) -// { -// //std::print("== AsyncSend \n"); -// _handle = handle; -// if (TURingServer::instance()->addSend(_fd, _buf, _len, this) < 0) { -// tSystemError("addSend error: {}", strerror(errno)); -// } -// } - - template class ScopeExitFunction { public: @@ -123,75 +106,174 @@ class ScopeExitFunction { }; +//QStack _processingContext; +// QSet _activeContexts; -QStack _processingContext; +// TActionContext *TUringCoroutine::currentContext() +// { +// return _activeContexts. +// } -Task TURingCoroutine::incomingConnection(int sd) +Task TUringCoroutine::start(int sd) { + static const int64_t systemLimitBodyBytes = Tf::appSettings()->value(Tf::LimitRequestBody).toLongLong() * 2; static int keepAlivetimeout = Tf::appSettings()->value(Tf::HttpKeepAliveTimeout).toInt(); + //_activeContexts.insert(this); + //ScopeExitFunction remove([&]{ _activeContexts.remove(this); }); + //std::print("accept {}\n", sd); ScopeExitFunction closing([sd]{ if (sd > 0) ::close(sd); }); int res; + int lengthToRead = -1; // ソケット受信 - //int tmp = ++counter; - char buf[124]; - AsyncRecv receiver(sd, buf, sizeof(buf), 5000); - res = co_await receiver; - //std::print("[RECV] fd={} res={}\n", sd, len); - if (res <= 0) { - if (res < 0) { - tSystemError("Recv error fd:{} error:{}\n", sd, strerror(-res)); + QByteArray readBuffer; + readBuffer.reserve(1024); + QByteArray headerBuffer; + TTemporaryFile fileBuffer; +tSystemDebug("###1"); + while (lengthToRead) { + int len = co_await AsyncRecv(sd, readBuffer.data(), 1024, 5000); +tSystemDebug("###2"); + if (len <= 0) { + if (len < 0) { +tSystemDebug("###3"); + tSystemError("Recv error fd:{} error:{}\n", sd, strerror(-len)); + } else { + tSystemError("Recv peer closed fd:{}\n", sd); + } + co_return; + } + +tSystemDebug("###4"); + readBuffer.resize(readBuffer.size() + len); + if (lengthToRead < 0) { +tSystemDebug("###5"); + 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); +tSystemDebug("###6 lengthToRead:{}", lengthToRead); + + 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 +tSystemDebug("###7"); + 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__); + } + } +tSystemDebug("###8"); + readBuffer.resize(0); + } else { +tSystemDebug("###9 lengthToRead:{}", lengthToRead); + if (lengthToRead > 0) { + readBuffer.reserve((idx + 4 + header.contentLength()) * 1.1); + } + } + } else { +tSystemDebug("###10"); + if (readBuffer.size() > readBuffer.capacity() * 0.8) { +tSystemDebug("###11"); + readBuffer.reserve(readBuffer.capacity() * 2); + } + } + } else if (lengthToRead > 0) { +tSystemDebug("###12"); + lengthToRead = std::max(lengthToRead - len, 0); } else { - tSystemError("Recv peer closed fd:{}\n", sd); +tSystemDebug("###13"); + // do nothing + break; } - co_return; } +tSystemDebug("###14 readBuffer: {}", readBuffer); - std::string s(buf, buf + res); + //std::string s(buf, buf + res); //std::print("[RECV] recv len:{} {}\n", res, s); //std::print("[Main] ({}) Received ({} bytes): {}\n", tmp, len, s); - THttpRequest request; - auto context = std::make_unique(); - auto response = context->start(request); - + //auto context = std::make_unique(); + //QList THttpRequest::generate(QByteArray &byteArray, const QHostAddress &address, TActionContext *context); + auto request = THttpRequest::generate(readBuffer, QHostAddress("localhost"), this); - // std::string response = "HTTP/1.1 200 OK\r\n" - // "Content-Type: text/plain; charset=utf-8\r\n" - // "Content-Length: 13\r\n" - // "Connection: close\r\n" - // "\r\n" - // "Hello world.\n"; +tSystemDebug("###15 : {}", request.header().toByteArray()); +tSystemDebug("###15.1 : {}", request.header().cookie("TFSESSION")); + //THttpRequest request; + execute(request); +tSystemDebug("###16"); - -/* - if (sock->canReadRequest()) { - _processingSocketStack.push(sock); - sock->process(); - _processingSocketStack.pop(); - } -*/ - - std::string dummy; - AsyncSend sender(sd, dummy.c_str(), dummy.length()); - res = co_await sender; + res = co_await AsyncSend(sd, _response.data(), _response.length()); if (res <= 0) { tSystemError("Send error fd={} res={}\n", sd, res); co_return; } + + // file + if (_file.exists()) { + if (!_file.isOpen()) { + if (!_file.open(QIODevice::ReadOnly)) { + Tf::warn("open failed"); + co_return; + } + } + + // AsyncSend sender2(sd, _file.data().data(), _file.length()); + // res = co_await sender2; + // if (res <= 0) { + // tSystemError("Send error fd={} res={}\n", sd, res); + // co_return; + // } + } + //std::print("send ({}) len:{}\n", tmp, res); //std::print("[SEND] fd={} res={}\n", sd, res); } -TActionContext *TURingCoroutine::currentContext() +// TActionContext *TUringCoroutine::currentContext() +// { +// if (!_processingContext.isEmpty()) { +// return _processingContext.top(); +// } +// return nullptr; +// } + + +int64_t TUringCoroutine::writeResponse(THttpResponseHeader &header, QIODevice *body) { - if (!_processingContext.isEmpty()) { - return _processingContext.top(); + if (keepAliveTimeout() > 0) { + header.setRawHeader(QByteArrayLiteral("Connection"), QByteArrayLiteral("Keep-Alive")); + } + + // Writes HTTP header + _response = header.toByteArray(); + + if (body) { + if (body->inherits("QBuffer")) { + _response += body->readAll(); + } else if (body->inherits("QFile")) { + //_file = *dynamic_cast(body); + + // TODO TODO TODO TODO TODO TODO TODO TODO + } else { + tSystemError("Invalid body [{}:{}]", __FILE__, __LINE__); + _response += body->readAll(); + } } - return nullptr; + return 0; } diff --git a/src/turingserver.h b/src/turingserver.h index 168e64e84..fd195a96f 100644 --- a/src/turingserver.h +++ b/src/turingserver.h @@ -37,10 +37,11 @@ class TAwaitBase { }; -class T_CORE_EXPORT TURingServer : public TDatabaseContextThread, public TApplicationServerBase { +class T_CORE_EXPORT TUringServer : public TDatabaseContextThread, public TApplicationServerBase { Q_OBJECT public: - ~TURingServer(); + TUringServer(int listeningSocket, QObject *parent = nullptr); // Constructor + ~TUringServer(); bool isListening() const { return _listenSocket > 0; } bool start(bool debugMode) override; @@ -51,8 +52,8 @@ class T_CORE_EXPORT TURingServer : public TDatabaseContextThread, public TApplic TActionContext *currentContext() const; TActionController *currentController() const; - static void instantiate(int listeningSocket); - static TURingServer *instance(); + //static void instantiate(int listeningSocket); + static TUringServer *instance(int listeningSocket = 0); // int addAccept(int fd, TAwaitBase* await = nullptr); @@ -62,11 +63,6 @@ class T_CORE_EXPORT TURingServer : public TDatabaseContextThread, public TApplic protected: void run() override; - //void timerEvent(QTimerEvent *event) override; - - -// signals: -// bool incomingRequest(TEpollSocket *socket); private: io_uring _ring{}; @@ -76,12 +72,9 @@ class T_CORE_EXPORT TURingServer : public TDatabaseContextThread, public TApplic bool _autoReload {false}; //mutable QStack _processingSocketStack; //mutable QSet _garbageSockets; - - TURingServer(int listeningSocket, QObject *parent = 0); // Constructor - friend class TEpollSocket; - T_DISABLE_COPY(TURingServer) - T_DISABLE_MOVE(TURingServer) + T_DISABLE_COPY(TUringServer) + T_DISABLE_MOVE(TUringServer) }; diff --git a/src/turingserver_linux.cpp b/src/turingserver_linux.cpp index 5010cbc83..3e65c9f89 100644 --- a/src/turingserver_linux.cpp +++ b/src/turingserver_linux.cpp @@ -36,33 +36,44 @@ constexpr int SEND_BUF_SIZE = 8 * 1024; constexpr int RECV_BUF_SIZE = 16 * 1024; -namespace { +//namespace { -TURingServer *uringServer = nullptr; +// TUringServer *uringServer = nullptr; -void cleanup() -{ - delete uringServer; - uringServer = nullptr; -} -} +// void cleanup() +// { +// delete uringServer; +// uringServer = nullptr; +// } +// } -void TURingServer::instantiate(int listeningSocket) -{ - if (!uringServer) { - uringServer = new TURingServer(listeningSocket); - qAddPostRoutine(::cleanup); - } -} +// void TUringServer::instantiate(int listeningSocket) +// { +// if (!uringServer) { +// uringServer = new TUringServer(listeningSocket); +// qAddPostRoutine(::cleanup); +// } +// } -TURingServer *TURingServer::instance() +TUringServer *TUringServer::instance(int listeningSocket) { - if (Q_UNLIKELY(!uringServer)) { - tFatal("Call TURingServer::instantiate() function first"); - } - return uringServer; + static std::unique_ptr instance; + static std::once_flag once; + + std::call_once(once, [&]() { + if (listeningSocket <= 0) { + throw StandardException("Invalid socket", __FILE__, __LINE__); + } + instance = std::make_unique(listeningSocket); + }); + return instance.get(); + + // if (Q_UNLIKELY(!uringServer)) { + // tFatal("Call TUringServer::instantiate() function first"); + // } + // return uringServer; } @@ -91,7 +102,7 @@ static void setNoDeleyOption(int fd) } -TURingServer::TURingServer(int listeningSocket, QObject *parent) : +TUringServer::TUringServer(int listeningSocket, QObject *parent) : TDatabaseContextThread(parent), TApplicationServerBase(), _listenSocket(listeningSocket) @@ -100,13 +111,13 @@ TURingServer::TURingServer(int listeningSocket, QObject *parent) : } -TURingServer::~TURingServer() +TUringServer::~TUringServer() { io_uring_queue_exit(&_ring); } -bool TURingServer::start(bool debugMode) +bool TUringServer::start(bool debugMode) { if (isRunning()) { return true; @@ -133,7 +144,7 @@ bool TURingServer::start(bool debugMode) } /* -int TURingServer::processEvents(int maxMilliSeconds) +int TUringServer::processEvents(int maxMilliSeconds) { TEpoll::instance()->dispatchEvents(); @@ -217,17 +228,8 @@ int TURingServer::processEvents(int maxMilliSeconds) } */ -template -class ScopeExitFunction { -public: - explicit ScopeExitFunction(Func&& func) : _func(std::move(func)) {} - ~ScopeExitFunction() noexcept { _func(); } -private: - Func _func; -}; - -void TURingServer::run() +void TUringServer::run() { setNoDeleyOption(_listenSocket); @@ -250,9 +252,10 @@ void TURingServer::run() // --- イベントループ --- while (!_stopped) { + tSystemDebug("-----------------------------------"); io_uring_cqe* cqe = nullptr; int res = io_uring_wait_cqe_timeout(&_ring, &cqe, &ts); - ScopeExitFunction seen([&]{ if (cqe) io_uring_cqe_seen(&_ring, cqe); }); + Tf::ScopeExitFunction seen([&]{ if (cqe) io_uring_cqe_seen(&_ring, cqe); }); if (res < 0 || !cqe) { if (res == -EINTR || res == -EAGAIN) { @@ -279,8 +282,9 @@ void TURingServer::run() if (cqe->res >= 0) { // コルーチンを起動 //handleClient(cqe->res); - TURingCoroutine::incomingConnection(cqe->res); - await->_cqeres = 0; // clear + auto routine = std::make_unique(); + routine->start(cqe->res); + await->clear(); // clear } else { int err = -cqe->res; switch (err) { @@ -328,7 +332,7 @@ void TURingServer::run() } -void TURingServer::stop() +void TUringServer::stop() { _stopped = true; if (isRunning()) { @@ -338,7 +342,7 @@ void TURingServer::stop() } -void TURingServer::setAutoReloadingEnabled(bool enable) +void TUringServer::setAutoReloadingEnabled(bool enable) { _autoReload = enable; // if (enable) { @@ -349,33 +353,34 @@ void TURingServer::setAutoReloadingEnabled(bool enable) } -bool TURingServer::isAutoReloadingEnabled() +bool TUringServer::isAutoReloadingEnabled() { return _autoReload; //return reloadTimer.isActive(); } -TActionContext *TURingServer::currentContext() const +TActionContext *TUringServer::currentContext() const { - return TURingCoroutine::currentContext(); - - // if (!_processingSocketStack.isEmpty()) { - // auto *worker = _processingSocketStack.top(); - // return worker; - // } - // return nullptr; + return nullptr; +// return TURingCoroutine::currentContext(); + +// // if (!_processingSocketStack.isEmpty()) { +// // auto *worker = _processingSocketStack.top(); +// // return worker; +// // } +// // return nullptr; } -TActionController *TURingServer::currentController() const +TActionController *TUringServer::currentController() const { auto *context = currentContext(); return (context) ? context->currentController() : nullptr; } /* -void TURingServer::timerEvent(QTimerEvent *event) +void TUringServer::timerEvent(QTimerEvent *event) { if (event->timerId() != reloadTimer.timerId()) { QThread::timerEvent(event); @@ -389,7 +394,7 @@ void TURingServer::timerEvent(QTimerEvent *event) */ // アクセプト -int TURingServer::addAccept(int fd, TAwaitBase* await) +int TUringServer::addAccept(int fd, TAwaitBase* await) { //std::print("addAccept fd: {}\n", fd); io_uring_sqe* sqe = io_uring_get_sqe(&_ring); @@ -402,7 +407,7 @@ int TURingServer::addAccept(int fd, TAwaitBase* await) } // 受信 -int TURingServer::addRecv(int fd, void* buf, size_t len, int msecs, TAwaitBase* await) +int TUringServer::addRecv(int fd, void* buf, size_t len, int msecs, TAwaitBase* await) { //std::print("addRecv fd: {}\n", fd); io_uring_sqe* sqe = io_uring_get_sqe(&_ring); @@ -427,11 +432,11 @@ int TURingServer::addRecv(int fd, void* buf, size_t len, int msecs, TAwaitBase* return io_uring_submit(&_ring); } -int TURingServer::addSend(int fd, const void* buf, size_t len, TAwaitBase* await) +int TUringServer::addSend(int fd, const void* buf, size_t len, TAwaitBase* await) { //std::print("addSend fd: {}\n", fd); io_uring_sqe* sqe = io_uring_get_sqe(&_ring); - io_uring_prep_send(sqe, fd, buf, len, 0); + io_uring_prep_send_zc(sqe, fd, buf, len, 0, 0); if (await) { await->clear(); io_uring_sqe_set_data(sqe, await); @@ -462,7 +467,7 @@ int TURingServer::addSend(int fd, const void* buf, size_t len, TAwaitBase* await // } // 状態変数待ち -int TURingServer::addEvent(int fd, TAwaitBase* await) +int TUringServer::addEvent(int fd, TAwaitBase* await) { io_uring_sqe* sqe = io_uring_get_sqe(&_ring); io_uring_prep_poll_add(sqe, fd, POLL_IN); diff --git a/src/turlroute.cpp b/src/turlroute.cpp index 35450aa09..c97e6537d 100644 --- a/src/turlroute.cpp +++ b/src/turlroute.cpp @@ -15,16 +15,16 @@ #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}, }; @@ -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..22e65760e 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,12 +423,12 @@ 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; 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/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 ) { From d30f3be0a564b41650237676d35b4719ff0e5299 Mon Sep 17 00:00:00 2001 From: aoyama Date: Sat, 8 Nov 2025 09:10:40 +0900 Subject: [PATCH 03/16] update --- src/taccesslog.cpp | 41 +---- src/taccesslog.h | 17 +- src/tactioncontext.cpp | 3 - src/tactioncontroller.cpp | 2 + src/tactionthread.cpp | 3 - src/tactionworker.cpp | 1 - src/tdatabasecontext.cpp | 22 ++- src/tglobal.cpp | 21 ++- src/tglobal.h | 1 + src/tsendbuffer.cpp | 2 - src/tsqldatabasepool.cpp | 2 +- src/turingcoroutine.h | 21 ++- src/turingcoroutine_linux.cpp | 269 ++++++++++++++++------------- src/turingserver.h | 81 +++++---- src/turingserver_linux.cpp | 309 ++++++++++++++-------------------- src/twebapplication.cpp | 4 + 16 files changed, 391 insertions(+), 408 deletions(-) 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 1c0afa9a2..68e382f12 100644 --- a/src/taccesslog.h +++ b/src/taccesslog.h @@ -3,6 +3,8 @@ #include #include #include +#include +#include class T_CORE_EXPORT TAccessLog { @@ -23,15 +25,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) { @@ -73,7 +70,7 @@ class T_CORE_EXPORT TAccessLogger { void setResponseBytes(int bytes) { if (_accessLog) { - _accessLog->responseBytes = bytes; + _accessLog->responseBytes = bytes; } } @@ -83,6 +80,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 027f33dfa..6d179b317 100644 --- a/src/tactioncontext.cpp +++ b/src/tactioncontext.cpp @@ -39,7 +39,6 @@ TActionContext::TActionContext() : TActionContext::~TActionContext() { release(); - accessLogger.close(); } @@ -66,7 +65,6 @@ void TActionContext::execute(THttpRequest &request) // Access log if (Tf::isAccessLoggerAvailable()) { - accessLogger.open(); QByteArray firstLine; firstLine.reserve(200); firstLine += reqHeader.method(); @@ -407,7 +405,6 @@ void TActionContext::flushResponse(TActionController *controller, bool immediate if (immediate) { flushSocket(); accessLogger.write(); // Writes access log - accessLogger.close(); closeSocket(); } } diff --git a/src/tactioncontroller.cpp b/src/tactioncontroller.cpp index 4536d584b..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()); diff --git a/src/tactionthread.cpp b/src/tactionthread.cpp index bb67cad9a..4ac345d04 100644 --- a/src/tactionthread.cpp +++ b/src/tactionthread.cpp @@ -132,9 +132,6 @@ void TActionThread::run() goto socket_cleanup; } - // for (auto &req : requests) { - // TActionContext::execute(req); - // } TActionContext::execute(request); if (keepAliveTimeout() == 0) { diff --git a/src/tactionworker.cpp b/src/tactionworker.cpp index 5f50cff9e..9397a9791 100644 --- a/src/tactionworker.cpp +++ b/src/tactionworker.cpp @@ -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; } diff --git a/src/tdatabasecontext.cpp b/src/tdatabasecontext.cpp index 73b6c2df4..d26eb7ac1 100644 --- a/src/tdatabasecontext.cpp +++ b/src/tdatabasecontext.cpp @@ -39,6 +39,13 @@ TDatabaseContext::TDatabaseContext() // sqlDatabases(), // kvsDatabases() { + if (sqlDatabases.size() < (size_t)Tf::app()->sqlDatabaseSettingsCount()) { + sqlDatabases.resize(Tf::app()->sqlDatabaseSettingsCount()); + } + + if (kvsDatabases.size() < (size_t)Tf::app()->sqlDatabaseSettingsCount()) { + kvsDatabases.resize(Tf::app()->sqlDatabaseSettingsCount()); + } } @@ -51,20 +58,17 @@ TDatabaseContext::~TDatabaseContext() QSqlDatabase &TDatabaseContext::getSqlDatabase(int id) { -tSystemDebug("getSqlDatabase #0"); - if (sqlDatabases.size() < (size_t)Tf::app()->sqlDatabaseSettingsCount()) { - sqlDatabases.resize(Tf::app()->sqlDatabaseSettingsCount()); - } + // if (sqlDatabases.size() < (size_t)Tf::app()->sqlDatabaseSettingsCount()) { + // sqlDatabases.resize(Tf::app()->sqlDatabaseSettingsCount()); + // } if (id < 0) { return invalidDb; // invalid database } -tSystemDebug("getSqlDatabase #1"); if (id >= Tf::app()->sqlDatabaseSettingsCount()) { throw RuntimeException("error database id", __FILE__, __LINE__); } -tSystemDebug("getSqlDatabase #2"); TSqlTransaction &tx = sqlDatabases[id]; QSqlDatabase &db = tx.database(); @@ -72,7 +76,6 @@ tSystemDebug("getSqlDatabase #2"); if (db.isValid() && tx.isActive()) { return db; } -tSystemDebug("getSqlDatabase #3"); int n = 0; do { @@ -86,7 +89,6 @@ tSystemDebug("getSqlDatabase #3"); TSqlDatabasePool::instance()->pool(db, true); } while (++n < 2); // try two times - tSystemDebug("getSqlDatabase #4"); idleElapsed = (uint)std::time(nullptr); return db; } @@ -101,10 +103,6 @@ void TDatabaseContext::releaseSqlDatabases() TKvsDatabase &TDatabaseContext::getKvsDatabase(Tf::KvsEngine engine) { - if (kvsDatabases.size() < (size_t)Tf::app()->sqlDatabaseSettingsCount()) { - kvsDatabases.resize(Tf::app()->sqlDatabaseSettingsCount()); - } - TKvsDatabase &db = kvsDatabases[(int)engine]; if (!db.isValid()) { db = TKvsDatabasePool::instance()->database(engine); diff --git a/src/tglobal.cpp b/src/tglobal.cpp index 5e94bf045..50795ebf5 100644 --- a/src/tglobal.cpp +++ b/src/tglobal.cpp @@ -21,6 +21,7 @@ #ifdef Q_OS_LINUX #include #include +#include "turingserver.h" #endif #ifdef Q_OS_WIN #define NOMINMAX @@ -139,8 +140,7 @@ TAbstractController *Tf::currentController() case TWebApplication::MultiProcessingModule::Uring: #ifdef Q_OS_LINUX - tFatal("Error TODO TODO"); - // TODO TODO TODO TODO TODO TODO TODO + return TUringServer::instance()->currentController(); #else tFatal("Unsupported MPM: uring"); #endif @@ -174,6 +174,22 @@ TDatabaseContext *Tf::currentDatabaseContext() #endif } + if (Tf::app()->multiProcessingModule() == TWebApplication::MultiProcessingModule::Uring) { +#ifdef Q_OS_LINUX + context = TUringServer::instance()->currentContext(); + if (context) { + return context; + } + + context = Tf::app()->mainDatabaseContext(); + if (context) { + return context; + } +#else + tFatal("Unsupported MPM: epoll"); +#endif + } + context = TDatabaseContext::currentDatabaseContext(); if (context) { return context; @@ -195,6 +211,7 @@ QSqlDatabase &Tf::currentSqlDatabase(int id) noexcept QMap> *Tf::objectFactories() noexcept +//std::map> *Tf::objectFactories() noexcept { static QMap> objectFactoryMap; return &objectFactoryMap; diff --git a/src/tglobal.h b/src/tglobal.h index 205f55f8d..e93c188d8 100644 --- a/src/tglobal.h +++ b/src/tglobal.h @@ -237,6 +237,7 @@ inline const TAbstractController *constCurrentController() { return currentContr T_CORE_EXPORT TDatabaseContext *currentDatabaseContext(); T_CORE_EXPORT QSqlDatabase ¤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; diff --git a/src/tsendbuffer.cpp b/src/tsendbuffer.cpp index 3b4204d05..77e5a5b0d 100644 --- a/src/tsendbuffer.cpp +++ b/src/tsendbuffer.cpp @@ -40,7 +40,6 @@ TSendBuffer::TSendBuffer(const QByteArray &header) : 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/tsqldatabasepool.cpp b/src/tsqldatabasepool.cpp index 3d1f33243..0b2c8dec2 100644 --- a/src/tsqldatabasepool.cpp +++ b/src/tsqldatabasepool.cpp @@ -94,7 +94,7 @@ void TSqlDatabasePool::init() lastCachedTime = new TAtomic[Tf::app()->sqlDatabaseSettingsCount()]; availableNames = new QStack[Tf::app()->sqlDatabaseSettingsCount()]; bool aval = false; - tSystemDebug("SQL database available"); + tSystemDebug("SQL database available. maxConnects:{}", maxConnects); // Adds databases previously for (int j = 0; j < Tf::app()->sqlDatabaseSettingsCount(); ++j) { diff --git a/src/turingcoroutine.h b/src/turingcoroutine.h index c3b4af132..9ad68bfd4 100644 --- a/src/turingcoroutine.h +++ b/src/turingcoroutine.h @@ -3,9 +3,13 @@ #include #include +class TUringCoroutine; +struct Task; +/* struct Task { struct promise_type { + TUringCoroutine *self {nullptr}; Task get_return_object() { return {}; } std::suspend_never initial_suspend() noexcept { return {}; } std::suspend_never final_suspend() noexcept { return {}; } @@ -13,23 +17,22 @@ struct Task { void unhandled_exception() { std::terminate(); } }; }; - +*/ class TUringCoroutine : public TActionContext { public: - TUringCoroutine() : TActionContext() {} - virtual ~TUringCoroutine() {} + TUringCoroutine(int socketDescriptor) : + TActionContext(), _sd(socketDescriptor) {} + virtual ~TUringCoroutine(); - Task start(int socketDescriptor); - // static TActionRoutine *currentProcess(); - // static bool isChildProcess(); - //QByteArray response() const { return _response; } - //static TActionContext *currentContext(); + Task start(); + //static TUringCoroutine *currentRoutine(); protected: virtual int64_t writeResponse(THttpResponseHeader &, QIODevice *) override; private: + int _sd {0}; QByteArray _response; - QFile _file; + QString _fileName; }; diff --git a/src/turingcoroutine_linux.cpp b/src/turingcoroutine_linux.cpp index 2821761af..ca5ba49bc 100644 --- a/src/turingcoroutine_linux.cpp +++ b/src/turingcoroutine_linux.cpp @@ -1,6 +1,5 @@ #include "turingcoroutine.h" #include "turingserver.h" -//#include "tactionroutine.h" #include "TSystemGlobal" #include "TAppSettings" #include "THttpRequest" @@ -9,8 +8,13 @@ #include #include #include +#include + constexpr uint READ_THRESHOLD_LENGTH = 4 * 1024 * 1024; // bytes +//constexpr uint SEND_CHUNK_LENGTH = 16 * 1024; // bytes + +static TUringCoroutine *gCurrentRoutine; class AsyncRecv : public TAwaitBase { @@ -18,9 +22,8 @@ class AsyncRecv : public TAwaitBase { AsyncRecv(int fd, void* buffer, size_t length, int msecs) : _fd(fd), _buf(buffer), _len(length), _msecs(msecs) { } - void await_suspend(std::coroutine_handle<> handle) + void await_suspend(std::coroutine_handle handle) { - //std::print("== AsyncRecv \n"); _handle = handle; if (TUringServer::instance()->addRecv(_fd, _buf, _len, _msecs, this) < 0) { tSystemError("addRecv error: {}", strerror(errno)); @@ -42,32 +45,63 @@ class AsyncSend : public TAwaitBase { AsyncSend(int fd, const void *buf, size_t len) : _fd(fd), _buf(buf), _len(len) { } - void await_suspend(std::coroutine_handle<> handle) + void await_suspend(std::coroutine_handle handle) { - //std::print("== AsyncSend \n"); _handle = handle; - if (TUringServer::instance()->addSend(_fd, _buf, _len, this) < 0) { + int res = TUringServer::instance()->addSendZc(_fd, _buf, _len, this); + // res = TUringServer::instance()->addSend(_fd, _buf, _len, this); + + if (res < 0) { tSystemError("addSend error: {}", strerror(errno)); } } - inline int await_resume() { return _cqeres; } + inline int await_resume() + { + tSystemDebug("await_resume : _len:{} _cqeflags:{} _cqeres:{}", _len, _cqeflags, _cqeres); + return (_cqeflags == IORING_CQE_F_NOTIF) ? _len : _cqeres; + } private: int _fd {0}; const void* _buf {nullptr}; size_t _len {0}; + //bool _zero_copy {true}; }; +/* +class AsyncSendFile : public TAwaitBase { +public: + AsyncSendFile(int sd, int fd, int offset, size_t sliceLen) : + _sd(sd), _fd(fd), _offset(offset), _sliceLen(sliceLen) { } + + void await_suspend(std::coroutine_handle<> handle) + { + //std::print("== AsyncSend \n"); + _handle = handle; + if (TUringServer::instance()->addSendFile(_sd, _fd, _offset, _sliceLen, this) < 0) { + tSystemError("AsyncSendFile error: {}", strerror(errno)); + } + } + + inline int await_resume() { return _cqeres; } + +private: + int _sd {0}; + int _fd {0}; + int _offset {0}; + size_t _sliceLen {0}; +}; +*/ template class AsyncFunction : public TAwaitBase { public: - explicit AsyncFunction(std::function f) : - TAwaitBase(), _func(std::move(f)) {} + explicit AsyncFunction(std::function f, TUringCoroutine *parent) : + TAwaitBase(parent), _func(std::move(f)) {} ~AsyncFunction() { if (_fd > 0) ::close(_fd); } - void await_suspend(std::coroutine_handle<> handle) + void await_suspend(std::coroutine_handle handle) { _handle = handle; _fd = eventfd(0, (EFD_NONBLOCK | EFD_CLOEXEC)); @@ -106,51 +140,76 @@ class ScopeExitFunction { }; -//QStack _processingContext; -// QSet _activeContexts; +class CurrentRoutineScope { +public: + CurrentRoutineScope(TUringCoroutine *&slot, TUringCoroutine *now) : + _slot(slot), _prev(slot) + { + _slot = now; + } + ~CurrentRoutineScope() { _slot = _prev; } +private: + TUringCoroutine *&_slot; + TUringCoroutine *_prev; +}; + + +TUringCoroutine::~TUringCoroutine() +{ + tSystemDebug("~TUringCoroutine: sd:{}", _sd); + if (_sd > 0) { + ::close(_sd); + } +} + -// TActionContext *TUringCoroutine::currentContext() +// TUringCoroutine *TUringCoroutine::currentRoutine() // { -// return _activeContexts. +// // tSystemDebug("currentRoutine: {}", (uint64_t)gCurrentRoutine); +// // return gCurrentRoutine; +// return nullptr; // } -Task TUringCoroutine::start(int sd) +Task TUringCoroutine::start() { static const int64_t systemLimitBodyBytes = Tf::appSettings()->value(Tf::LimitRequestBody).toLongLong() * 2; static int keepAlivetimeout = Tf::appSettings()->value(Tf::HttpKeepAliveTimeout).toInt(); - //_activeContexts.insert(this); - //ScopeExitFunction remove([&]{ _activeContexts.remove(this); }); - - //std::print("accept {}\n", sd); - ScopeExitFunction closing([sd]{ if (sd > 0) ::close(sd); }); - int res; - int lengthToRead = -1; - - // ソケット受信 - QByteArray readBuffer; - readBuffer.reserve(1024); - QByteArray headerBuffer; - TTemporaryFile fileBuffer; -tSystemDebug("###1"); - while (lengthToRead) { - int len = co_await AsyncRecv(sd, readBuffer.data(), 1024, 5000); -tSystemDebug("###2"); - if (len <= 0) { - if (len < 0) { -tSystemDebug("###3"); - tSystemError("Recv error fd:{} error:{}\n", sd, strerror(-len)); - } else { - tSystemError("Recv peer closed fd:{}\n", sd); + CurrentRoutineScope scope(gCurrentRoutine, this); + ScopeExitFunction closing([this]{ + TUringServer::instance()->registerForGC(this); + }); + + int timeout = 5000; + while (true) { + //int res; + int64_t lengthToRead = INT64_MAX; + int64_t readLength = 0; + constexpr int64_t bufsize = 8 * 1024; + + // ソケット受信 + 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) { + if (len < 0) { + tSystemError("Recv error fd:{} error:{}", _sd, strerror(-len)); + } else { + tSystemWarn("Recv peer closed fd:{}", _sd); + } + co_return; } - co_return; - } -tSystemDebug("###4"); - readBuffer.resize(readBuffer.size() + len); - if (lengthToRead < 0) { -tSystemDebug("###5"); + readLength += len; + readBuffer.resize(readLength); + tSystemDebug("readBuffer size:{}", readBuffer.size()); + int idx = readBuffer.indexOf(Tf::CRLFCRLF); if (idx > 0) { THttpRequestHeader header(readBuffer); @@ -160,12 +219,10 @@ tSystemDebug("###5"); } lengthToRead = std::max(idx + 4 + header.contentLength() - (int64_t)readBuffer.length(), (int64_t)0); -tSystemDebug("###6 lengthToRead:{}", lengthToRead); 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 -tSystemDebug("###7"); if (!fileBuffer.open()) { throw RuntimeException(QLatin1String("temporary file open error: ") + fileBuffer.fileTemplate(), __FILE__, __LINE__); } @@ -176,103 +233,87 @@ tSystemDebug("###7"); throw RuntimeException(QLatin1String("write error: ") + fileBuffer.fileName(), __FILE__, __LINE__); } } -tSystemDebug("###8"); readBuffer.resize(0); - } else { -tSystemDebug("###9 lengthToRead:{}", lengthToRead); - if (lengthToRead > 0) { - readBuffer.reserve((idx + 4 + header.contentLength()) * 1.1); - } - } - } else { -tSystemDebug("###10"); - if (readBuffer.size() > readBuffer.capacity() * 0.8) { -tSystemDebug("###11"); - readBuffer.reserve(readBuffer.capacity() * 2); } } - } else if (lengthToRead > 0) { -tSystemDebug("###12"); - lengthToRead = std::max(lengthToRead - len, 0); - } else { -tSystemDebug("###13"); - // do nothing - break; } - } -tSystemDebug("###14 readBuffer: {}", readBuffer); - //std::string s(buf, buf + res); - //std::print("[RECV] recv len:{} {}\n", res, s); - //std::print("[Main] ({}) Received ({} bytes): {}\n", tmp, len, s); + auto request = THttpRequest::generate(readBuffer, QHostAddress("localhost"), this); + execute(request); + int res = co_await AsyncSend(_sd, _response.data(), _response.length()); + if (res <= 0) { + tSystemError("Send error fd={} res={}\n", _sd, res); + co_return; + } - //auto context = std::make_unique(); - //QList THttpRequest::generate(QByteArray &byteArray, const QHostAddress &address, TActionContext *context); - auto request = THttpRequest::generate(readBuffer, QHostAddress("localhost"), this); + // file + if (!_fileName.isEmpty()) { + //int64_t fileSize = QFileInfo(_fileName).size(); + int fd = ::open(qUtf8Printable(_fileName), O_RDONLY | O_CLOEXEC); + if (fd < 0) { + tSystemDebug("File open error: {}", _fileName); + Tf::warn("File open error: {}", _fileName); + co_return; + } -tSystemDebug("###15 : {}", request.header().toByteArray()); -tSystemDebug("###15.1 : {}", request.header().cookie("TFSESSION")); - //THttpRequest request; - execute(request); -tSystemDebug("###16"); + ScopeExitFunction fd_closing([fd]{ ::close(fd); }); - res = co_await AsyncSend(sd, _response.data(), _response.length()); - if (res <= 0) { - tSystemError("Send error fd={} res={}\n", sd, res); - co_return; - } + const int64_t file_size = lseek(fd, 0, SEEK_END); + if (file_size < 0) { + tSystemDebug("lseek error: {}", strerror(errno)); + co_return; + } + lseek(fd, 0, SEEK_SET); - // file - if (_file.exists()) { - if (!_file.isOpen()) { - if (!_file.open(QIODevice::ReadOnly)) { - Tf::warn("open failed"); + void *mapped = mmap(nullptr, file_size, 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 < file_size) { + int64_t send_len = std::min(file_size - sent_len, slice_len); + res = co_await AsyncSend(_sd, mapped + sent_len, send_len); + if (res <= 0) { + if (res == -EAGAIN || res == -ENOMEM) { + continue; + } + tSystemError("Send error fd={} res={}", _sd, res); + break; + } + sent_len += res; + //tSystemDebug("### AsyncSend res:{} sent_len:{}", res, sent_len); + } + + munmap(mapped, file_size); + _fileName.resize(0); } - // AsyncSend sender2(sd, _file.data().data(), _file.length()); - // res = co_await sender2; - // if (res <= 0) { - // tSystemError("Send error fd={} res={}\n", sd, res); - // co_return; - // } + if (keepAlivetimeout > 0) { + timeout = keepAlivetimeout; + } } - - //std::print("send ({}) len:{}\n", tmp, res); - //std::print("[SEND] fd={} res={}\n", sd, res); } -// TActionContext *TUringCoroutine::currentContext() -// { -// if (!_processingContext.isEmpty()) { -// return _processingContext.top(); -// } -// return nullptr; -// } - - int64_t TUringCoroutine::writeResponse(THttpResponseHeader &header, QIODevice *body) { if (keepAliveTimeout() > 0) { header.setRawHeader(QByteArrayLiteral("Connection"), QByteArrayLiteral("Keep-Alive")); } - // Writes HTTP header _response = header.toByteArray(); if (body) { - if (body->inherits("QBuffer")) { - _response += body->readAll(); - } else if (body->inherits("QFile")) { - //_file = *dynamic_cast(body); - - // TODO TODO TODO TODO TODO TODO TODO TODO + if (auto *buf = dynamic_cast(body); buf) { + _response += buf->buffer(); + } else if (auto *file = dynamic_cast(body); file) { + _fileName = file->fileName(); } else { tSystemError("Invalid body [{}:{}]", __FILE__, __LINE__); - _response += body->readAll(); } } return 0; diff --git a/src/turingserver.h b/src/turingserver.h index fd195a96f..f0b9503ce 100644 --- a/src/turingserver.h +++ b/src/turingserver.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +20,40 @@ class THttpSendBuffer; class TEpollSocket; class TActionWorker; class TActionController; +class TUringCoroutine; + + +struct Task { + struct promise_type { + TUringCoroutine *self {nullptr}; + Task get_return_object() + { + return Task{ std::coroutine_handle::from_promise(*this) }; + } + //Task get_return_object() { return {}; } + std::suspend_never initial_suspend() noexcept { return {}; } + std::suspend_never final_suspend() noexcept { return {}; } + void return_void() noexcept {} + void unhandled_exception() { std::terminate(); } + + // static inline int alloc_count = 0; + // static inline int free_count = 0; + // void* operator new(std::size_t n) + // { + // ++alloc_count; + // tSystemDebug("[alloc] count: {}", alloc_count); + // return ::operator new(n); + // } + // void operator delete(void* p, std::size_t n) { + // ++free_count; + // tSystemDebug("[free] count: {}", free_count); + // ::operator delete(p); + // } + }; + + explicit Task(std::coroutine_handle h) : handle(h) {} + std::coroutine_handle handle; +}; class TAwaitBase { @@ -28,12 +63,14 @@ class TAwaitBase { inline void clear() { _cqeres = 0; + _cqeflags = 0; _sqecounter = 1; } - std::coroutine_handle<> _handle{}; - int _cqeres{0}; - int _sqecounter{1}; + std::coroutine_handle _handle{}; + int _cqeres {0}; + int _cqeflags {0}; + int _sqecounter {1}; }; @@ -48,18 +85,18 @@ class T_CORE_EXPORT TUringServer : public TDatabaseContextThread, public TApplic void stop() override; void setAutoReloadingEnabled(bool enable) override; bool isAutoReloadingEnabled() override; - //int processEvents(int maxMilliSeconds); TActionContext *currentContext() const; TActionController *currentController() const; + void registerForGC(TUringCoroutine *); - //static void instantiate(int listeningSocket); static TUringServer *instance(int listeningSocket = 0); - // - int addAccept(int fd, TAwaitBase* await = nullptr); - int addRecv(int fd, void* buf, size_t len, int msecs = 0, TAwaitBase* await = nullptr); - int addSend(int fd, const void* buf, size_t len, TAwaitBase* await = nullptr); - int addEvent(int fd, TAwaitBase* await = nullptr); + int addAccept(int sd, TAwaitBase* await = nullptr); + int addRecv(int sd, void* buf, size_t len, int msecs = 0, TAwaitBase* await = nullptr); + int addSend(int sd, const void* buf, size_t len, TAwaitBase* await = nullptr); + int addSendZc(int sd, const void* buf, size_t len, TAwaitBase* await = nullptr); + //int addSendFile(int sd, int fd, int offset, size_t slice_len, TAwaitBase* await = nullptr); + int addEvent(int sd, TAwaitBase* await = nullptr); protected: void run() override; @@ -68,30 +105,12 @@ class T_CORE_EXPORT TUringServer : public TDatabaseContextThread, public TApplic io_uring _ring{}; bool _stopped {false}; int _listenSocket {0}; - //QBasicTimer reloadTimer; bool _autoReload {false}; - //mutable QStack _processingSocketStack; - //mutable QSet _garbageSockets; + TUringCoroutine *_currentCoroutine {nullptr}; + std::list _garbage; friend class TEpollSocket; + T_DISABLE_COPY(TUringServer) T_DISABLE_MOVE(TUringServer) }; - -/* - * WorkerStarter class declaration - * This object creates worker threads in the main event loop. - */ -/* -class TWorkerStarter : public QObject -{ - Q_OBJECT -public: - TWorkerStarter(QObject *parent = 0) : QObject(parent) { } - virtual ~TWorkerStarter(); - -public slots: - void startWorker(TEpollSocket *); -}; -*/ - diff --git a/src/turingserver_linux.cpp b/src/turingserver_linux.cpp index 3e65c9f89..ebe3e73dd 100644 --- a/src/turingserver_linux.cpp +++ b/src/turingserver_linux.cpp @@ -19,42 +19,12 @@ #include #include #include -//#include -//#include -//#include #include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include + constexpr int SEND_BUF_SIZE = 8 * 1024; constexpr int RECV_BUF_SIZE = 16 * 1024; - -//namespace { - -// TUringServer *uringServer = nullptr; - -// void cleanup() -// { -// delete uringServer; -// uringServer = nullptr; -// } -// } - - -// void TUringServer::instantiate(int listeningSocket) -// { -// if (!uringServer) { -// uringServer = new TUringServer(listeningSocket); -// qAddPostRoutine(::cleanup); -// } -// } +constexpr int SPLICE_LEN = 1024 * 1024; TUringServer *TUringServer::instance(int listeningSocket) @@ -69,11 +39,6 @@ TUringServer *TUringServer::instance(int listeningSocket) instance = std::make_unique(listeningSocket); }); return instance.get(); - - // if (Q_UNLIKELY(!uringServer)) { - // tFatal("Call TUringServer::instantiate() function first"); - // } - // return uringServer; } @@ -143,116 +108,32 @@ bool TUringServer::start(bool debugMode) return true; } -/* -int TUringServer::processEvents(int maxMilliSeconds) -{ - TEpoll::instance()->dispatchEvents(); - - // Poll Sending/Receiving/Incoming - maxMilliSeconds = std::max(maxMilliSeconds, 0); - int res = TEpoll::instance()->wait(maxMilliSeconds); - if (res < 0) { - return res; - } - - TEpollSocket *sock; - while ((sock = TEpoll::instance()->next())) { - - int cltfd = sock->socketDescriptor(); - if (cltfd == listenSocket && cltfd > 0) { - if (_processingSocketStack.count() > 64) { // Not accept in deep context - continue; - } - - TEpollSocket *acceptedSock = TEpollHttpSocket::accept(listenSocket); - if (Q_LIKELY(acceptedSock)) { - if (!acceptedSock->watch()) { - acceptedSock->dispose(); - } - } - continue; - - } else { - if (TEpoll::instance()->canSend()) { - if (sock->state() == Tf::SocketState::Connecting) { - sock->_state = Tf::SocketState::Connected; - continue; - } - - // Send data - int len = TEpoll::instance()->send(sock); - if (Q_UNLIKELY(len < 0)) { - TEpoll::instance()->deletePoll(sock); - sock->dispose(); - continue; - } - } - - if (TEpoll::instance()->canReceive()) { - try { - // Receive data - int len = TEpoll::instance()->recv(sock); - if (Q_UNLIKELY(len < 0)) { - TEpoll::instance()->deletePoll(sock); - sock->dispose(); - continue; - } - } catch (ClientErrorException &e) { - Tf::warn("Caught ClientErrorException: status code:{}", e.statusCode()); - tSystemWarn("Caught ClientErrorException: status code:{}", e.statusCode()); - TEpoll::instance()->deletePoll(sock); - sock->dispose(); - continue; - } - - if (sock->canReadRequest()) { - _processingSocketStack.push(sock); - sock->process(); - _processingSocketStack.pop(); - } - } - } - } - - // Garbage - if (!_garbageSockets.isEmpty()) { - auto set = _garbageSockets; - for (auto ptr : set) { - if (!ptr->isProcessing()) { - delete ptr; // Remove it from garbageSockets-set in the destructor - } - } - } - - return res; -} -*/ +std::mutex mtx; // グローバルミューテックス void TUringServer::run() { setNoDeleyOption(_listenSocket); - // if (newerLibraryExists()) { - // tSystemInfo("Detect new library of application. Reloading the libraries."); - // Tf::app()->exit(127); - // } - - - //std::cout << "# Event Loop\n"; - //_stop = false; - TAwaitBase accepter; instance()->addAccept(_listenSocket, &accepter); - struct __kernel_timespec ts { + __kernel_timespec ts = { .tv_sec = 2, // 2秒 .tv_nsec = 0 }; // --- イベントループ --- while (!_stopped) { - tSystemDebug("-----------------------------------"); + if (_garbage.size() > 0) { + // Garbage collection + std::lock_guard lock(mtx); + for (auto it = _garbage.begin(); it != _garbage.end(); ++it) { + delete *it; + } + _garbage.clear(); + } + 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); }); @@ -278,12 +159,12 @@ void TUringServer::run() if (user_data) { auto* await = static_cast(user_data); if (await == &accepter) { - // マルチショット accept 結果 + // Accepts if (cqe->res >= 0) { - // コルーチンを起動 - //handleClient(cqe->res); - auto routine = std::make_unique(); - routine->start(cqe->res); + // Starts coroutine + auto *coro = new TUringCoroutine(cqe->res); + Task task = coro->start(); + task.handle.promise().self = coro; await->clear(); // clear } else { int err = -cqe->res; @@ -296,8 +177,7 @@ void TUringServer::run() case EINVAL: case EBADF: case ENOTSOCK: - // listen_fd が閉じられた → 終了処理 - tSystemError("Listen socket invalid, terminating. error: {}\n", strerror(err)); + tSystemError("Listen socket invalid, terminating. error: {}", strerror(err)); stop(); break; default: @@ -308,22 +188,22 @@ void TUringServer::run() } if (!(cqe->flags & IORING_CQE_F_MORE)) { - //std::print(std::cerr, "flags & IORING_CQE_F_MORE\n"); if (addAccept(_listenSocket, &accepter) < 0) { tSystemError("addAccept error: {}", strerror(errno)); } } } else { - if (!await->_cqeres) { - await->_cqeres = cqe->res; - } - // recv/send - //std::print("cqe->res:{} cqe->flags: {}\n", await->_cqeres, cqe->flags); - //std::print("#[Loop] resume res:{}\n", await->_cqeres); - if (--await->_sqecounter == 0) { - if (await->_handle && !await->_handle.done()) { - //std::print("[Loop] resume res:{}\n", await->_cqeres); + //tSystemDebug("cqe->res:{} cqe->flags: {}", await->_cqeres, cqe->flags); + if (cqe->flags != IORING_CQE_F_MORE) { + if (!await->_cqeres) { + await->_cqeres = cqe->res; + await->_cqeflags = cqe->flags; + } + + if (--await->_sqecounter == 0 && await->_handle && !await->_handle.done()) { + _currentCoroutine = await->_handle.promise().self; await->_handle.resume(); + _currentCoroutine = nullptr; } } } @@ -362,14 +242,9 @@ bool TUringServer::isAutoReloadingEnabled() TActionContext *TUringServer::currentContext() const { - return nullptr; -// return TURingCoroutine::currentContext(); - -// // if (!_processingSocketStack.isEmpty()) { -// // auto *worker = _processingSocketStack.top(); -// // return worker; -// // } -// // return nullptr; +// return _currentRoutine; +// //return TUringCoroutine::currentRoutine(); + return _currentCoroutine; } @@ -393,12 +268,16 @@ void TUringServer::timerEvent(QTimerEvent *event) } */ -// アクセプト +// Accept int TUringServer::addAccept(int fd, TAwaitBase* await) { - //std::print("addAccept fd: {}\n", fd); io_uring_sqe* sqe = io_uring_get_sqe(&_ring); - io_uring_prep_multishot_accept(sqe, fd, nullptr, nullptr, (SOCK_CLOEXEC | SOCK_NONBLOCK)); // マルチショット + 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); @@ -409,8 +288,12 @@ int TUringServer::addAccept(int fd, TAwaitBase* await) // 受信 int TUringServer::addRecv(int fd, void* buf, size_t len, int msecs, TAwaitBase* await) { - //std::print("addRecv fd: {}\n", fd); 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(); @@ -420,9 +303,11 @@ int TUringServer::addRecv(int fd, void* buf, size_t len, int msecs, TAwaitBase* if (msecs > 0) { sqe->flags |= IOSQE_IO_LINK; io_uring_sqe* sqe2 = io_uring_get_sqe(&_ring); - __kernel_timespec ts; - ts.tv_sec = msecs / 1000; - ts.tv_nsec = (msecs % 1000) * 1'000'000; + 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++; @@ -432,10 +317,34 @@ int TUringServer::addRecv(int fd, void* buf, size_t len, int msecs, TAwaitBase* return io_uring_submit(&_ring); } +// +// Prepare a send request +// int TUringServer::addSend(int fd, const void* buf, size_t len, TAwaitBase* await) { - //std::print("addSend fd: {}\n", fd); 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) +{ + 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(); @@ -444,28 +353,56 @@ int TUringServer::addSend(int fd, const void* buf, size_t len, TAwaitBase* await return io_uring_submit(&_ring); } -// int addPoll(int fd, int mask, int msecs, TAwaitBase* await) +/* +int TUringServer::addSendFile(int sd, int fd, int offset, size_t slice_len, TAwaitBase* await) +{ + int pipefd[2]; + if (pipe2(pipefd, O_NONBLOCK | O_CLOEXEC) < 0) { + tSystemError("pipe2 error [{}:{}]", __FILE__, __LINE__); + return -1; + } + + // 1. splice: file -> pipe + io_uring_sqe *sqe = io_uring_get_sqe(&_ring); + io_uring_prep_splice(sqe, fd, offset, pipefd[1], -1, slice_len, 0); + if (await) { + await->clear(); + io_uring_sqe_set_data(sqe, await); + } + + sqe->flags |= IOSQE_IO_LINK; + // 2. splice: pipe -> sd + io_uring_sqe *sqe2 = io_uring_get_sqe(&_ring); + io_uring_prep_splice(sqe, pipefd[0], -1, sd, -1, slice_len, 0); + if (await) { + await->_sqecounter++; + io_uring_sqe_set_data(sqe2, await); + } + return io_uring_submit(&_ring); +} +*/ + + +// int TUringServer::addSendFile(int sd, int fd, int offset, size_t slice_len, TAwaitBase* await) // { -// io_uring_sqe* sqe1 = io_uring_get_sqe(&_ring); -// io_uring_prep_poll_add(sqe1, fd, mask); -// io_uring_sqe_set_data(sqe1, await); - -// if (msecs > 0) { -// sqe1->flags |= IOSQE_IO_LINK; -// io_uring_sqe* sqe2 = io_uring_get_sqe(&_ring); -// __kernel_timespec ts; -// ts.tv_sec = msecs / 1000; -// ts.tv_nsec = (msecs % 1000) * 1'000'000; -// io_uring_prep_link_timeout(sqe2, &ts, 0); -// io_uring_sqe_set_data(sqe2, await); -// if (await) { -// await->_sqecounter = 2; -// } +// 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); -// return io_uring_submit(&_ring); +// 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::addEvent(int fd, TAwaitBase* await) { @@ -477,3 +414,11 @@ int TUringServer::addEvent(int fd, TAwaitBase* await) } return io_uring_submit(&_ring); } + + + +void TUringServer::registerForGC(TUringCoroutine *coroutine) +{ + std::lock_guard lock(mtx); // 排他 + _garbage.push_back(coroutine); +} diff --git a/src/twebapplication.cpp b/src/twebapplication.cpp index 22e65760e..168ce6476 100644 --- a/src/twebapplication.cpp +++ b/src/twebapplication.cpp @@ -432,6 +432,10 @@ int TWebApplication::maxNumberOfThreadsPerAppServer() const maxNum = 128; break; + case MultiProcessingModule::Uring: + maxNum = 128; // TODO TODO TODO + break; + default: break; } From ea46cf757a195aece54ddc366f265dff1321c86a Mon Sep 17 00:00:00 2001 From: aoyama Date: Sun, 9 Nov 2025 11:52:56 +0900 Subject: [PATCH 04/16] update --- src/turingcoroutine_linux.cpp | 28 ++++++++++++++++++---------- src/turingserver_linux.cpp | 29 +++++++++++++++++++++-------- 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/turingcoroutine_linux.cpp b/src/turingcoroutine_linux.cpp index ca5ba49bc..2d8f49e07 100644 --- a/src/turingcoroutine_linux.cpp +++ b/src/turingcoroutine_linux.cpp @@ -30,7 +30,11 @@ class AsyncRecv : public TAwaitBase { } } - inline int await_resume() { return _cqeres; } + inline int await_resume() + { + //tSystemDebug("await_resume : _len:{} _cqeflags:{} _cqeres:{}", _len, _cqeflags, _cqeres); + return _cqeres; + } private: int _fd {0}; @@ -182,7 +186,7 @@ Task TUringCoroutine::start() }); int timeout = 5000; - while (true) { + while (timeout > 0) { //int res; int64_t lengthToRead = INT64_MAX; int64_t readLength = 0; @@ -196,13 +200,15 @@ Task TUringCoroutine::start() 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) { - if (len < 0) { - tSystemError("Recv error fd:{} error:{}", _sd, strerror(-len)); - } else { - tSystemWarn("Recv peer closed fd:{}", _sd); - } + if (len < 0) { + // timeout or error + tSystemError("Recv error fd:{} error:{}", _sd, strerror(-len)); + co_return; + } + if (!len) { + tSystemWarn("Recv peer closed fd:{}", _sd); co_return; } @@ -276,7 +282,7 @@ Task TUringCoroutine::start() int64_t sent_len = 0; while (sent_len < file_size) { int64_t send_len = std::min(file_size - sent_len, slice_len); - res = co_await AsyncSend(_sd, mapped + sent_len, send_len); + res = co_await AsyncSend(_sd, (char*)mapped + sent_len, send_len); if (res <= 0) { if (res == -EAGAIN || res == -ENOMEM) { continue; @@ -293,7 +299,9 @@ Task TUringCoroutine::start() } if (keepAlivetimeout > 0) { - timeout = keepAlivetimeout; + timeout = keepAlivetimeout * 1000; // msecs + } else { + break; } } } diff --git a/src/turingserver_linux.cpp b/src/turingserver_linux.cpp index ebe3e73dd..f2bf9f409 100644 --- a/src/turingserver_linux.cpp +++ b/src/turingserver_linux.cpp @@ -22,8 +22,8 @@ #include -constexpr int SEND_BUF_SIZE = 8 * 1024; -constexpr int RECV_BUF_SIZE = 16 * 1024; +constexpr int SEND_BUF_SIZE = 128 * 1024; +constexpr int RECV_BUF_SIZE = 128 * 1024; constexpr int SPLICE_LEN = 1024 * 1024; @@ -42,7 +42,19 @@ TUringServer *TUringServer::instance(int listeningSocket) } -static void setNoDeleyOption(int fd) +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; @@ -66,7 +78,6 @@ static void setNoDeleyOption(int fd) } } - TUringServer::TUringServer(int listeningSocket, QObject *parent) : TDatabaseContextThread(parent), TApplicationServerBase(), @@ -113,7 +124,7 @@ std::mutex mtx; // グローバルミューテックス void TUringServer::run() { - setNoDeleyOption(_listenSocket); + setDefferAcceptOption(_listenSocket); TAwaitBase accepter; instance()->addAccept(_listenSocket, &accepter); @@ -162,7 +173,9 @@ void TUringServer::run() // Accepts if (cqe->res >= 0) { // Starts coroutine - auto *coro = new TUringCoroutine(cqe->res); + int fd = cqe->res; + setBufferOption(fd); + auto *coro = new TUringCoroutine(fd); Task task = coro->start(); task.handle.promise().self = coro; await->clear(); // clear @@ -193,9 +206,9 @@ void TUringServer::run() } } } else { - //tSystemDebug("cqe->res:{} cqe->flags: {}", await->_cqeres, cqe->flags); + tSystemDebug("cqe->res:{} cqe->flags: {}", await->_cqeres, cqe->flags); if (cqe->flags != IORING_CQE_F_MORE) { - if (!await->_cqeres) { + if (!await->_cqeres && !await->_cqeflags) { await->_cqeres = cqe->res; await->_cqeflags = cqe->flags; } From 90501dacb822da8cfadae8cb4e99f6f66f60cc8a Mon Sep 17 00:00:00 2001 From: aoyama Date: Sat, 15 Nov 2025 22:57:59 +0900 Subject: [PATCH 05/16] update --- include/TSqlDatabase | 1 + include/headers.pri | 2 + include/tsqldatabase.h | 1 + src/taccesslog.h | 3 +- src/tactionroutine.cpp | 118 ------------------------------ src/tactionroutine.h | 45 ------------ src/tkvsdatabase.cpp | 31 ++------ src/tkvsdatabase.h | 14 ++-- src/tkvsdatabasepool.cpp | 11 +-- src/tkvsdatabasepool.h | 37 +++++++++- src/tmultiplexingserver_linux.cpp | 15 +--- src/tpublisher.cpp | 8 +- src/tsqldatabasepool.cpp | 6 +- src/tsystembus.cpp | 6 +- src/turingcoroutine_linux.cpp | 40 +--------- src/turingserver.h | 35 +++------ src/turingserver_linux.cpp | 54 ++++++-------- src/turlroute.cpp | 8 +- 18 files changed, 113 insertions(+), 322 deletions(-) create mode 100644 include/TSqlDatabase create mode 100644 include/tsqldatabase.h delete mode 100644 src/tactionroutine.cpp delete mode 100644 src/tactionroutine.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 4448b0570..c2ecd1982 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 @@ -151,6 +152,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 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/taccesslog.h b/src/taccesslog.h index 68e382f12..c68449700 100644 --- a/src/taccesslog.h +++ b/src/taccesslog.h @@ -3,7 +3,6 @@ #include #include #include -#include #include @@ -70,7 +69,7 @@ class T_CORE_EXPORT TAccessLogger { void setResponseBytes(int bytes) { if (_accessLog) { - _accessLog->responseBytes = bytes; + _accessLog->responseBytes = bytes; } } diff --git a/src/tactionroutine.cpp b/src/tactionroutine.cpp deleted file mode 100644 index 122c582d2..000000000 --- a/src/tactionroutine.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/* Copyright (c) 2010, 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 "tactionroutine.h" -//#include "TWebApplication" -//#include "TSqlDatabasePool" - -// static TActionRoutineManager *manager = 0; // For parent process -// TActionRoutine *TActionRoutine::currentActionRoutine = 0; // For child process - - -// TActionRoutineManager::TActionRoutineManager() : QObject() -// { -// timer.start(1000, this); -// } - - -// TActionRoutineManager::~TActionRoutineManager() -// { } - - -// TActionRoutineManager *TActionRoutineManager::instance() -// { -// return manager; -// } - - -// static void cleanupManager() -// { -// if (manager) { -// delete manager; -// manager = 0; -// } -// } - - -TActionRoutine::TActionRoutine() : - TActionContext() -{ - // if (!manager) { - // manager = new TActionRoutineManager(); - // qAddPostRoutine(cleanupManager); - // } - // setParent(manager); -} - - -TActionRoutine::~TActionRoutine() -{ -// closeDatabase(); -} - - -THttpResponse TActionRoutine::start(THttpRequest &request) -{ - execute(request); - - return THttpResponse(); -} - -// bool TActionRoutine::openDatabase() -// { -// QString env = tWebApp->databaseEnvironment(); -// QString type = TSqlDatabasePool::driverType(env); -// if (type.isEmpty()) { -// return false; -// } - -// sqlDatabase = QSqlDatabase::addDatabase(type); -// TSqlDatabasePool::openDatabase(sqlDatabase, env); -// return sqlDatabase.isValid(); -// } - - -// void TActionRoutine::closeDatabase() -// { -// if (sqlDatabase.isValid()) { -// sqlDatabase.rollback(); -// sqlDatabase.close(); -// sqlDatabase = QSqlDatabase(); // Sets an invalid object -// } -// } - - -// void TActionRoutine::emitError(int socketError) -// { -// emit error(socketError); -// } - - -// TActionRoutine *TActionRoutine::currentProcess() -// { -// return currentActionRoutine; -// } - - -// bool TActionRoutine::isChildProcess() -// { -// return (bool)currentActionRoutine; -// } - - -// void TActionRoutine::terminate(int status) -// { -// tSystemDebug("Child process({}) teminated. status:{}", childPid, status); -// emit finished(); -// } - - -// void TActionRoutine::kill(int sig) -// { -// tSystemDebug("Child process({}) killed. signal:{}", childPid, sig); -// emit finished(); -// } diff --git a/src/tactionroutine.h b/src/tactionroutine.h deleted file mode 100644 index b1df256cd..000000000 --- a/src/tactionroutine.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include - - -class T_CORE_EXPORT TActionRoutine : public TActionContext -{ -public: - TActionRoutine(); - virtual ~TActionRoutine(); - - THttpResponse start(THttpRequest &request); - // static TActionRoutine *currentProcess(); - // static bool isChildProcess(); - -protected: - virtual int64_t writeResponse(THttpResponseHeader &, QIODevice *) override; - - -// virtual bool openDatabase(); -// virtual void closeDatabase(); -// virtual void emitError(int socketError); - -// protected slots: -// void terminate(int status); -// void kill(int sig); - -// signals: -// void started(); // for the parent -// void forked(); // for the child -// void finished(); // for the parent and the child -// void error(int socketError); - -// private: -// int childPid; -// static TActionRoutine *currentActionRoutine; - -// friend class TActionRoutineManager; - Q_DISABLE_COPY(TActionRoutine) -}; diff --git a/src/tkvsdatabase.cpp b/src/tkvsdatabase.cpp index 7fff3fc10..dee348928 100644 --- a/src/tkvsdatabase.cpp +++ b/src/tkvsdatabase.cpp @@ -26,7 +26,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 +34,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(); } @@ -73,7 +73,7 @@ TKvsDatabase TKvsDatabase::database(const QString &connectionName) auto *dict = databaseDict(); QReadLocker locker(&dict->lock); - const TKvsDatabaseData &d = (*dict)[connectionName]; + const TKvsDatabaseSettings &d = (*dict)[connectionName]; return TKvsDatabase(d.connectionName, d.driver); } @@ -89,7 +89,7 @@ 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); @@ -109,7 +109,7 @@ void TKvsDatabase::removeDatabase(const QString &connectionName) } -TKvsDatabaseData TKvsDatabase::settings(const QString &connectionName) +TKvsDatabaseSettings TKvsDatabase::settings(const QString &connectionName) { auto *dict = databaseDict(); QReadLocker locker(&dict->lock); @@ -117,13 +117,6 @@ 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) @@ -131,21 +124,13 @@ TKvsDatabase::TKvsDatabase(const QString &connectionName, TKvsDriver *driver) : } -TKvsDatabase::TKvsDatabase(const TKvsDatabaseData &data) : +TKvsDatabase::TKvsDatabase(const TKvsDatabaseSettings &data) : connectName(data.connectionName), drv(data.driver) { } -// TKvsDatabase &TKvsDatabase::operator=(const TKvsDatabase &other) -// { -// connectName = other.connectName; -// drv = other.drv; -// return *this; -// } - - bool TKvsDatabase::isValid() const { return (bool)driver(); @@ -156,7 +141,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; } diff --git a/src/tkvsdatabase.h b/src/tkvsdatabase.h index c7fdba5d9..54ace109f 100644 --- a/src/tkvsdatabase.h +++ b/src/tkvsdatabase.h @@ -3,16 +3,14 @@ #include class TKvsDriver; -class TKvsDatabaseData; +class TKvsDatabaseSettings; class T_CORE_EXPORT TKvsDatabase { public: TKvsDatabase() { } - //TKvsDatabase(const TKvsDatabase &other); - TKvsDatabase(TKvsDatabase &&) = default; ~TKvsDatabase() { } - //TKvsDatabase &operator=(const TKvsDatabase &other); + TKvsDatabase(TKvsDatabase &&) = default; TKvsDatabase &operator=(TKvsDatabase &&) = default; QString driverName() const; @@ -45,19 +43,19 @@ class T_CORE_EXPORT TKvsDatabase { static TKvsDatabase database(const QString &connectionName = defaultConnection); static TKvsDatabase 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}; 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; @@ -69,5 +67,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..97d1b9ffa 100644 --- a/src/tkvsdatabasepool.cpp +++ b/src/tkvsdatabasepool.cpp @@ -17,6 +17,7 @@ #include #include #include +#include /*! \class TKvsDatabasePool @@ -53,13 +54,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(); } @@ -279,11 +280,11 @@ bool TKvsDatabasePool::setDatabaseSettings(TKvsDatabase &database, Tf::KvsEngine } -TKvsDatabaseData TKvsDatabasePool::getDatabaseSettings(Tf::KvsEngine engine) const +TKvsDatabaseSettings TKvsDatabasePool::getDatabaseSettings(Tf::KvsEngine engine) const { QMutexLocker locker(&_mutex); - TKvsDatabaseData settrings; + TKvsDatabaseSettings settrings; auto &stack = availableNames[(int)engine]; QString name; diff --git a/src/tkvsdatabasepool.h b/src/tkvsdatabasepool.h index f8c116722..8003a1878 100644 --- a/src/tkvsdatabasepool.h +++ b/src/tkvsdatabasepool.h @@ -15,13 +15,48 @@ template class TStack; class T_CORE_EXPORT TKvsDatabasePool : public QObject { Q_OBJECT public: + using TKvsDBConn = std::unique_ptr; + ~TKvsDatabasePool(); TKvsDatabase database(Tf::KvsEngine engine); void pool(TKvsDatabase &database); - TKvsDatabaseData getDatabaseSettings(Tf::KvsEngine engine) const; + TKvsDatabaseSettings getDatabaseSettings(Tf::KvsEngine engine) const; static TKvsDatabasePool *instance(); + // TKvsDatabase handle + class Handle { + public: + Handle(TKvsDatabase *ptr) : _ptr(ptr) {} + Handle(const Handle &) = delete; + Handle &operator=(const Handle &) = delete; + Handle(Handle &&other) noexcept + { + _ptr = other._ptr; + other._ptr = nullptr; + } + + Handle &operator=(Handle &&other) noexcept + { + _ptr = other._ptr; + other._ptr = nullptr; + return *this; + } + + ~Handle() + { + if (_ptr) { + TKvsDatabasePool::instance()->pool(*_ptr); + } + } + + TKvsDatabase *operator->() { return _ptr; } + TKvsDatabase &operator*() { return *_ptr; } + + private: + TKvsDatabase *_ptr {nullptr}; + }; + protected: void init(); bool setDatabaseSettings(TKvsDatabase &database, Tf::KvsEngine engine) const; 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 5d027ed4e..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(); } diff --git a/src/tsqldatabasepool.cpp b/src/tsqldatabasepool.cpp index 0b2c8dec2..012f2af2c 100644 --- a/src/tsqldatabasepool.cpp +++ b/src/tsqldatabasepool.cpp @@ -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(); } 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/turingcoroutine_linux.cpp b/src/turingcoroutine_linux.cpp index 2d8f49e07..28f1b2e6f 100644 --- a/src/turingcoroutine_linux.cpp +++ b/src/turingcoroutine_linux.cpp @@ -10,11 +10,7 @@ #include #include - constexpr uint READ_THRESHOLD_LENGTH = 4 * 1024 * 1024; // bytes -//constexpr uint SEND_CHUNK_LENGTH = 16 * 1024; // bytes - -static TUringCoroutine *gCurrentRoutine; class AsyncRecv : public TAwaitBase { @@ -70,33 +66,8 @@ class AsyncSend : public TAwaitBase { int _fd {0}; const void* _buf {nullptr}; size_t _len {0}; - //bool _zero_copy {true}; }; -/* -class AsyncSendFile : public TAwaitBase { -public: - AsyncSendFile(int sd, int fd, int offset, size_t sliceLen) : - _sd(sd), _fd(fd), _offset(offset), _sliceLen(sliceLen) { } - - void await_suspend(std::coroutine_handle<> handle) - { - //std::print("== AsyncSend \n"); - _handle = handle; - if (TUringServer::instance()->addSendFile(_sd, _fd, _offset, _sliceLen, this) < 0) { - tSystemError("AsyncSendFile error: {}", strerror(errno)); - } - } - - inline int await_resume() { return _cqeres; } - -private: - int _sd {0}; - int _fd {0}; - int _offset {0}; - size_t _sliceLen {0}; -}; -*/ template class AsyncFunction : public TAwaitBase { @@ -167,20 +138,12 @@ TUringCoroutine::~TUringCoroutine() } -// TUringCoroutine *TUringCoroutine::currentRoutine() -// { -// // tSystemDebug("currentRoutine: {}", (uint64_t)gCurrentRoutine); -// // return gCurrentRoutine; -// return nullptr; -// } - - Task 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; - CurrentRoutineScope scope(gCurrentRoutine, this); ScopeExitFunction closing([this]{ TUringServer::instance()->registerForGC(this); }); @@ -190,7 +153,6 @@ Task TUringCoroutine::start() //int res; int64_t lengthToRead = INT64_MAX; int64_t readLength = 0; - constexpr int64_t bufsize = 8 * 1024; // ソケット受信 QByteArray readBuffer; diff --git a/src/turingserver.h b/src/turingserver.h index f0b9503ce..241c5e253 100644 --- a/src/turingserver.h +++ b/src/turingserver.h @@ -23,32 +23,19 @@ class TActionController; class TUringCoroutine; -struct Task { - struct promise_type { +class Task { +public: + class promise_type { + public: TUringCoroutine *self {nullptr}; Task get_return_object() { return Task{ std::coroutine_handle::from_promise(*this) }; } - //Task get_return_object() { return {}; } std::suspend_never initial_suspend() noexcept { return {}; } std::suspend_never final_suspend() noexcept { return {}; } void return_void() noexcept {} void unhandled_exception() { std::terminate(); } - - // static inline int alloc_count = 0; - // static inline int free_count = 0; - // void* operator new(std::size_t n) - // { - // ++alloc_count; - // tSystemDebug("[alloc] count: {}", alloc_count); - // return ::operator new(n); - // } - // void operator delete(void* p, std::size_t n) { - // ++free_count; - // tSystemDebug("[free] count: {}", free_count); - // ::operator delete(p); - // } }; explicit Task(std::coroutine_handle h) : handle(h) {} @@ -88,21 +75,19 @@ class T_CORE_EXPORT TUringServer : public TDatabaseContextThread, public TApplic TActionContext *currentContext() const; TActionController *currentController() const; void registerForGC(TUringCoroutine *); - static TUringServer *instance(int listeningSocket = 0); - int addAccept(int sd, TAwaitBase* await = nullptr); - int addRecv(int sd, void* buf, size_t len, int msecs = 0, TAwaitBase* await = nullptr); - int addSend(int sd, const void* buf, size_t len, TAwaitBase* await = nullptr); - int addSendZc(int sd, const void* buf, size_t len, TAwaitBase* await = nullptr); - //int addSendFile(int sd, int fd, int offset, size_t slice_len, TAwaitBase* await = nullptr); - int addEvent(int sd, TAwaitBase* await = nullptr); + 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 addEvent(int sd, TAwaitBase *await = nullptr) const; protected: void run() override; private: - io_uring _ring{}; + mutable io_uring _ring {}; bool _stopped {false}; int _listenSocket {0}; bool _autoReload {false}; diff --git a/src/turingserver_linux.cpp b/src/turingserver_linux.cpp index f2bf9f409..f783c7de2 100644 --- a/src/turingserver_linux.cpp +++ b/src/turingserver_linux.cpp @@ -15,13 +15,11 @@ #include #include #include -//#include #include #include #include #include - constexpr int SEND_BUF_SIZE = 128 * 1024; constexpr int RECV_BUF_SIZE = 128 * 1024; constexpr int SPLICE_LEN = 1024 * 1024; @@ -120,8 +118,6 @@ bool TUringServer::start(bool debugMode) } -std::mutex mtx; // グローバルミューテックス - void TUringServer::run() { setDefferAcceptOption(_listenSocket); @@ -138,14 +134,13 @@ void TUringServer::run() while (!_stopped) { if (_garbage.size() > 0) { // Garbage collection - std::lock_guard lock(mtx); for (auto it = _garbage.begin(); it != _garbage.end(); ++it) { delete *it; } _garbage.clear(); } - io_uring_cqe* cqe = nullptr; + 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); }); @@ -166,9 +161,9 @@ void TUringServer::run() break; } - void* user_data = io_uring_cqe_get_data(cqe); + void *user_data = io_uring_cqe_get_data(cqe); if (user_data) { - auto* await = static_cast(user_data); + auto *await = static_cast(user_data); if (await == &accepter) { // Accepts if (cqe->res >= 0) { @@ -238,25 +233,17 @@ void TUringServer::stop() void TUringServer::setAutoReloadingEnabled(bool enable) { _autoReload = enable; - // if (enable) { - // reloadTimer.start(500, this); - // } else { - // reloadTimer.stop(); - // } } bool TUringServer::isAutoReloadingEnabled() { return _autoReload; - //return reloadTimer.isActive(); } TActionContext *TUringServer::currentContext() const { -// return _currentRoutine; -// //return TUringCoroutine::currentRoutine(); return _currentCoroutine; } @@ -281,10 +268,12 @@ void TUringServer::timerEvent(QTimerEvent *event) } */ -// Accept -int TUringServer::addAccept(int fd, TAwaitBase* await) +// +// Prepare a accept request +// +int TUringServer::addAccept(int fd, TAwaitBase* await) const { - io_uring_sqe* sqe = io_uring_get_sqe(&_ring); + io_uring_sqe *sqe = io_uring_get_sqe(&_ring); if (!sqe) { tSystemError("io_uring_get_sqe error: {} [{}:{}]", strerror(errno), __FILE__, __LINE__); return -1; @@ -298,10 +287,12 @@ int TUringServer::addAccept(int fd, TAwaitBase* await) return io_uring_submit(&_ring); } -// 受信 -int TUringServer::addRecv(int fd, void* buf, size_t len, int msecs, TAwaitBase* await) +// +// 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); + io_uring_sqe *sqe = io_uring_get_sqe(&_ring); if (!sqe) { tSystemError("io_uring_get_sqe error: {} [{}:{}]", strerror(errno), __FILE__, __LINE__); return -1; @@ -315,7 +306,7 @@ int TUringServer::addRecv(int fd, void* buf, size_t len, int msecs, TAwaitBase* if (msecs > 0) { sqe->flags |= IOSQE_IO_LINK; - io_uring_sqe* sqe2 = io_uring_get_sqe(&_ring); + io_uring_sqe *sqe2 = io_uring_get_sqe(&_ring); if (!sqe2) { tSystemError("io_uring_get_sqe error: {} [{}:{}]", strerror(errno), __FILE__, __LINE__); return -1; @@ -333,9 +324,9 @@ int TUringServer::addRecv(int fd, void* buf, size_t len, int msecs, TAwaitBase* // // Prepare a send request // -int TUringServer::addSend(int fd, const void* buf, size_t len, TAwaitBase* await) +int TUringServer::addSend(int fd, const void* buf, size_t len, TAwaitBase* await) const { - io_uring_sqe* sqe = io_uring_get_sqe(&_ring); + io_uring_sqe *sqe = io_uring_get_sqe(&_ring); if (!sqe) { tSystemError("io_uring_get_sqe error: {} [{}:{}]", strerror(errno), __FILE__, __LINE__); return -1; @@ -351,9 +342,9 @@ int TUringServer::addSend(int fd, const void* buf, size_t len, TAwaitBase* await // // Prepare a zerocopy send request // -int TUringServer::addSendZc(int fd, const void* buf, size_t len, TAwaitBase* await) +int TUringServer::addSendZc(int fd, const void* buf, size_t len, TAwaitBase* await) const { - io_uring_sqe* sqe = io_uring_get_sqe(&_ring); + io_uring_sqe *sqe = io_uring_get_sqe(&_ring); if (!sqe) { tSystemError("io_uring_get_sqe error: {} [{}:{}]", strerror(errno), __FILE__, __LINE__); return -1; @@ -416,10 +407,12 @@ int TUringServer::addSendFile(int sd, int fd, int offset, size_t slice_len, TAwa -// 状態変数待ち -int TUringServer::addEvent(int fd, TAwaitBase* await) +// +// Prepare a event request +// +int TUringServer::addEvent(int fd, TAwaitBase* await) const { - io_uring_sqe* sqe = io_uring_get_sqe(&_ring); + io_uring_sqe *sqe = io_uring_get_sqe(&_ring); io_uring_prep_poll_add(sqe, fd, POLL_IN); if (await) { await->clear(); @@ -432,6 +425,5 @@ int TUringServer::addEvent(int fd, TAwaitBase* await) void TUringServer::registerForGC(TUringCoroutine *coroutine) { - std::lock_guard lock(mtx); // 排他 _garbage.push_back(coroutine); } diff --git a/src/turlroute.cpp b/src/turlroute.cpp index c97e6537d..c29106556 100644 --- a/src/turlroute.cpp +++ b/src/turlroute.cpp @@ -30,12 +30,12 @@ const QMap directiveHash = { 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; } From 076aaff314f74e3814b8be91b937532218acfb93 Mon Sep 17 00:00:00 2001 From: aoyama Date: Sun, 16 Nov 2025 22:33:39 +0900 Subject: [PATCH 06/16] temp --- src/tcachesqlitestore.cpp | 8 +- src/tdatabasecontext.cpp | 38 ++++---- src/tdatabasecontext.h | 11 +-- src/tglobal.cpp | 2 +- src/tglobal.h | 4 +- src/tkvsdatabasepool.h | 27 ++---- src/tsqldatabase.cpp | 43 +++++---- src/tsqldatabase.h | 72 +++++++++++---- src/tsqldatabasepool.cpp | 180 ++++++++++++++++++++++++++++++++++++-- src/tsqldatabasepool.h | 40 +++++++-- src/tsqlobject.cpp | 40 +++++---- src/tsqlobject.h | 7 +- src/tsqlormapper.h | 2 +- src/tsqlquery.cpp | 25 +++--- src/tsqlquery.h | 3 +- src/tsqltransaction.cpp | 34 +++---- src/tsqltransaction.h | 8 +- 17 files changed, 388 insertions(+), 156 deletions(-) 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 d26eb7ac1..1028f29a9 100644 --- a/src/tdatabasecontext.cpp +++ b/src/tdatabasecontext.cpp @@ -39,12 +39,13 @@ TDatabaseContext::TDatabaseContext() // sqlDatabases(), // kvsDatabases() { - if (sqlDatabases.size() < (size_t)Tf::app()->sqlDatabaseSettingsCount()) { - sqlDatabases.resize(Tf::app()->sqlDatabaseSettingsCount()); + const int count = Tf::app()->sqlDatabaseSettingsCount(); + if (sqlDatabases.size() < (size_t)count) { + sqlDatabases.resize(count); } - if (kvsDatabases.size() < (size_t)Tf::app()->sqlDatabaseSettingsCount()) { - kvsDatabases.resize(Tf::app()->sqlDatabaseSettingsCount()); + if (kvsDatabases.size() < (size_t)count) { + kvsDatabases.resize(count); } } @@ -56,14 +57,10 @@ TDatabaseContext::~TDatabaseContext() } -QSqlDatabase &TDatabaseContext::getSqlDatabase(int id) +TSqlDatabase &TDatabaseContext::getSqlDatabase(int id) { - // if (sqlDatabases.size() < (size_t)Tf::app()->sqlDatabaseSettingsCount()) { - // sqlDatabases.resize(Tf::app()->sqlDatabaseSettingsCount()); - // } - if (id < 0) { - return invalidDb; // invalid database + throw RuntimeException("error database id", __FILE__, __LINE__);throw RuntimeException("error database id", __FILE__, __LINE__); } if (id >= Tf::app()->sqlDatabaseSettingsCount()) { @@ -71,26 +68,25 @@ QSqlDatabase &TDatabaseContext::getSqlDatabase(int id) } TSqlTransaction &tx = sqlDatabases[id]; - QSqlDatabase &db = tx.database(); + TSqlDatabase::Handle &handle = tx.database(); - if (db.isValid() && tx.isActive()) { - return db; + if (handle && handle->isValid() && tx.isActive()) { + 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); } while (++n < 2); // try two times idleElapsed = (uint)std::time(nullptr); - return db; + return *handle; } @@ -153,7 +149,7 @@ void TDatabaseContext::commitTransactions() // tx.commit(); // TSqlDatabasePool::instance()->pool(tx.database()); it->commit(); - TSqlDatabasePool::instance()->pool(it->database()); + //TSqlDatabasePool::instance()->pool(it->database()); もともとはpoolで戻していたが必要か? } } @@ -169,7 +165,7 @@ bool TDatabaseContext::commitTransaction(int id) TSqlTransaction &tx = sqlDatabases[id]; res = tx.commit(); - TSqlDatabasePool::instance()->pool(sqlDatabases[id].database()); + //TSqlDatabasePool::instance()->pool(sqlDatabases[id].database()); return res; } @@ -181,7 +177,7 @@ void TDatabaseContext::rollbackTransactions() // tx.rollback(); // TSqlDatabasePool::instance()->pool(tx.database(), true); it->rollback(); - TSqlDatabasePool::instance()->pool(it->database(), true); + //TSqlDatabasePool::instance()->pool(it->database(), true); } } @@ -195,7 +191,7 @@ bool TDatabaseContext::rollbackTransaction(int id) return res; } res = sqlDatabases[id].rollback(); - TSqlDatabasePool::instance()->pool(sqlDatabases[id].database(), true); + //TSqlDatabasePool::instance()->pool(sqlDatabases[id].database(), true); return res; } diff --git a/src/tdatabasecontext.h b/src/tdatabasecontext.h index 6c95ed0c6..bfb58384a 100644 --- a/src/tdatabasecontext.h +++ b/src/tdatabasecontext.h @@ -1,8 +1,10 @@ #pragma once //#include -#include +#include #include #include +#include "tsqldatabasepool.h" +#include class QSqlDatabase; class TKvsDatabase; @@ -16,7 +18,7 @@ class T_CORE_EXPORT TDatabaseContext { TDatabaseContext(TDatabaseContext &&) = default; TDatabaseContext &operator=(TDatabaseContext &&) = default; - QSqlDatabase &getSqlDatabase(int id = 0); + TSqlDatabase &getSqlDatabase(int id = 0); TKvsDatabase &getKvsDatabase(Tf::KvsEngine engine); void setTransactionEnabled(bool enable, int id = 0); @@ -34,10 +36,6 @@ class T_CORE_EXPORT TDatabaseContext { void releaseKvsDatabases(); void releaseSqlDatabases(); - // QMap sqlDatabases; - // QMap kvsDatabases; - //std::map sqlDatabases; - //std::map kvsDatabases; std::vector sqlDatabases; std::vector kvsDatabases; @@ -46,6 +44,5 @@ class T_CORE_EXPORT TDatabaseContext { TCache *cachep {nullptr}; T_DISABLE_COPY(TDatabaseContext) - //T_DISABLE_MOVE(TDatabaseContext) }; diff --git a/src/tglobal.cpp b/src/tglobal.cpp index 50795ebf5..80d68a857 100644 --- a/src/tglobal.cpp +++ b/src/tglobal.cpp @@ -204,7 +204,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 e93c188d8..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,7 +235,7 @@ 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; diff --git a/src/tkvsdatabasepool.h b/src/tkvsdatabasepool.h index 8003a1878..ee689a2db 100644 --- a/src/tkvsdatabasepool.h +++ b/src/tkvsdatabasepool.h @@ -27,34 +27,25 @@ class T_CORE_EXPORT TKvsDatabasePool : public QObject { // TKvsDatabase handle class Handle { public: - Handle(TKvsDatabase *ptr) : _ptr(ptr) {} + Handle() = default; + Handle(TKvsDBConn conn) : _conn(std::move(conn)) {} Handle(const Handle &) = delete; Handle &operator=(const Handle &) = delete; - Handle(Handle &&other) noexcept - { - _ptr = other._ptr; - other._ptr = nullptr; - } - - Handle &operator=(Handle &&other) noexcept - { - _ptr = other._ptr; - other._ptr = nullptr; - return *this; - } + Handle(Handle &&other) noexcept = default; + Handle &operator=(Handle &&other) noexcept = default; ~Handle() { - if (_ptr) { - TKvsDatabasePool::instance()->pool(*_ptr); + if (_conn) { + TKvsDatabasePool::instance()->pool(*(_conn.get())); } } - TKvsDatabase *operator->() { return _ptr; } - TKvsDatabase &operator*() { return *_ptr; } + TKvsDatabase *operator->() { return _conn.get(); } + TKvsDatabase &operator*() { return *(_conn.get()); } private: - TKvsDatabase *_ptr {nullptr}; + TKvsDBConn _conn; }; protected: diff --git a/src/tsqldatabase.cpp b/src/tsqldatabase.cpp index 93bb7be29..5f35d3d02 100644 --- a/src/tsqldatabase.cpp +++ b/src/tsqldatabase.cpp @@ -6,14 +6,15 @@ */ #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; }; @@ -32,32 +33,34 @@ void TSqlDatabase::setDriverExtension(TSqlDriverExtension *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]; + return std::unique_ptr{new TSqlDatabase{QSqlDatabase::database(connectionName)}}; } else { - return defaultDatabase; + return std::unique_ptr{new TSqlDatabase{TSqlDatabase{}}}; } } -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 +68,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 +92,12 @@ bool TSqlDatabase::isPreparedStatementSupported() const { return _driverExtension && _driverExtension->isPreparedStatementSupported(); } + + +TSqlDatabase::Handle::~Handle() +{ + tSystemDebug("Handle::~Handle"); + if (_conn) { + TSqlDatabasePool::instance()->pool(std::move(_conn)); + } +} diff --git a/src/tsqldatabase.h b/src/tsqldatabase.h index d2b0dbd09..b1e94780b 100644 --- a/src/tsqldatabase.h +++ b/src/tsqldatabase.h @@ -9,6 +9,8 @@ class TSqlDriverExtension; class T_CORE_EXPORT TSqlDatabase { public: + using SqlDbPtr = std::unique_ptr; + enum DbmsType { UnknownDbms = QSqlDriver::UnknownDbms, MSSqlServer = QSqlDriver::MSSqlServer, @@ -21,10 +23,29 @@ class T_CORE_EXPORT TSqlDatabase { DB2 = QSqlDriver::DB2 }; - explicit TSqlDatabase(const QSqlDatabase &database = QSqlDatabase()); - TSqlDatabase(const TSqlDatabase &other); + // TKvsDatabase handle + class Handle { + public: + Handle() = default; + Handle(SqlDbPtr conn) : _conn(std::move(conn)) {} + Handle(const Handle &) = delete; + Handle &operator=(const Handle &) = delete; + Handle(Handle &&other) noexcept = default; + Handle &operator=(Handle &&other) noexcept = default; + ~Handle(); + + TSqlDatabase *operator->() { return _conn.get(); } + TSqlDatabase &operator*() { return *(_conn.get()); } + explicit operator bool() const noexcept { return (bool)_conn; } + + private: + SqlDbPtr _conn; + }; + + + TSqlDatabase(TSqlDatabase &&other); ~TSqlDatabase() { } - TSqlDatabase &operator=(const TSqlDatabase &other); + TSqlDatabase &operator=(TSqlDatabase &&other); DbmsType dbmsType() const; bool isValid() const { return _sqlDatabase.isValid(); } @@ -40,12 +61,13 @@ class T_CORE_EXPORT TSqlDatabase { const TSqlDriverExtension *driverExtension() const { return _driverExtension; } 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: + explicit TSqlDatabase(const QSqlDatabase &database = QSqlDatabase()); TSqlDriverExtension *driverExtension() { return _driverExtension; } void setDriverExtension(TSqlDriverExtension *extension); @@ -55,6 +77,8 @@ class T_CORE_EXPORT TSqlDatabase { TSqlDriverExtension *_driverExtension {nullptr}; friend class TSqlDatabasePool; + friend class TSqlObject; + T_DISABLE_COPY(TSqlDatabase) }; @@ -63,20 +87,36 @@ inline TSqlDatabase::TSqlDatabase(const QSqlDatabase &database) : { } -inline TSqlDatabase::TSqlDatabase(const TSqlDatabase &other) : - _sqlDatabase(other._sqlDatabase), - _postOpenStatements(other._postOpenStatements), - _enableUpsert(other._enableUpsert), - _driverExtension(other._driverExtension) +// inline TSqlDatabase::TSqlDatabase(const TSqlDatabase &other) : +// _sqlDatabase(other._sqlDatabase), +// _postOpenStatements(other._postOpenStatements), +// _enableUpsert(other._enableUpsert), +// _driverExtension(other._driverExtension) +// { +// } + +inline TSqlDatabase::TSqlDatabase(TSqlDatabase &&other) { + *this = std::move(other); } -inline TSqlDatabase &TSqlDatabase::operator=(const TSqlDatabase &other) +// inline TSqlDatabase &TSqlDatabase::operator=(const TSqlDatabase &other) +// { +// _sqlDatabase = other._sqlDatabase; +// _postOpenStatements = other._postOpenStatements; +// _enableUpsert = other._enableUpsert; +// _driverExtension = other._driverExtension; +// return *this; +// } + +inline TSqlDatabase &TSqlDatabase::operator=(TSqlDatabase &&other) { - _sqlDatabase = other._sqlDatabase; - _postOpenStatements = other._postOpenStatements; - _enableUpsert = other._enableUpsert; - _driverExtension = other._driverExtension; + if (this != &other) { + _sqlDatabase = other._sqlDatabase; + _postOpenStatements = other._postOpenStatements; + _enableUpsert = other._enableUpsert; + _driverExtension = other._driverExtension; + other._driverExtension = nullptr; + } return *this; } - diff --git a/src/tsqldatabasepool.cpp b/src/tsqldatabasepool.cpp index 012f2af2c..f780b5a36 100644 --- a/src/tsqldatabasepool.cpp +++ b/src/tsqldatabasepool.cpp @@ -42,6 +42,7 @@ TSqlDatabasePool::TSqlDatabasePool() : TSqlDatabasePool::~TSqlDatabasePool() { +#if 0 QMutexLocker locker(&_mutex); timer.stop(); @@ -65,6 +66,29 @@ TSqlDatabasePool::~TSqlDatabasePool() delete[] cachedDatabase; delete[] lastCachedTime; delete[] availableNames; +#else + QMutexLocker locker(&_mutex); + timer.stop(); + + for (int j = 0; j < Tf::app()->sqlDatabaseSettingsCount(); ++j) { + auto &cache = cachedDatabases[j]; + QString name; + while (!cache.empty()) { + auto db = cache.pop(); + name = db->connectionName(); + closeDatabase(*db); + TSqlDatabase::removeDatabase(name); + } + + auto &stack = availableDatabases[j]; + while (!stack.empty()) { + auto db = stack.pop(); + TSqlDatabase::removeDatabase(db->sqlDatabase().connectionName()); + } + } + + delete[] lastCachedTime; +#endif } @@ -89,7 +113,7 @@ void TSqlDatabasePool::init() } QMutexLocker locker(&_mutex); - +#if 0 cachedDatabase = new QStack[Tf::app()->sqlDatabaseSettingsCount()]; lastCachedTime = new TAtomic[Tf::app()->sqlDatabaseSettingsCount()]; availableNames = new QStack[Tf::app()->sqlDatabaseSettingsCount()]; @@ -117,6 +141,42 @@ void TSqlDatabasePool::init() tSystemDebug("Add Database successfully. name:{}", db.connectionName()); } } +#endif + + const int dbcount = Tf::app()->sqlDatabaseSettingsCount(); + bool aval = false; + cachedDatabases.resize(dbcount); + availableDatabases.resize(dbcount); + lastCachedTime = new TAtomic[dbcount]; + tSystemDebug("SQL database available. maxConnects:{}", maxConnects); + + // Adds databases previously + for (int j = 0; j < dbcount; ++j) { + QString type = driverType(j); + if (type.isEmpty()) { + continue; + } + aval = true; + + auto &stack = availableDatabases[j]; + for (int i = 0; i < maxConnects; ++i) { + 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); + tSystemDebug("Add Database successfully. name:{}", db->connectionName()); + stack.push(std::move(db)); // push onto stack + } + } if (aval) { // Starts the timer to close extra-connection @@ -124,12 +184,12 @@ void TSqlDatabasePool::init() } } - +/* QSqlDatabase TSqlDatabasePool::database(int databaseId) { QMutexLocker locker(&_mutex); - if (Q_LIKELY(databaseId >= 0 && databaseId < Tf::app()->sqlDatabaseSettingsCount())) { + if (databaseId >= 0 && databaseId < Tf::app()->sqlDatabaseSettingsCount()) { auto &cache = cachedDatabase[databaseId]; auto &stack = availableNames[databaseId]; @@ -175,6 +235,58 @@ QSqlDatabase TSqlDatabasePool::database(int databaseId) } throw RuntimeException("No pooled connection", __FILE__, __LINE__); } +*/ + +TSqlDatabase::Handle TSqlDatabasePool::database(int databaseId) +{ + QMutexLocker locker(&_mutex); + + if (databaseId >= 0 && databaseId < Tf::app()->sqlDatabaseSettingsCount()) { + auto &cache = cachedDatabases[databaseId]; + auto &stack = availableDatabases[databaseId]; + + for (;;) { + QString name; + if (!cache.empty()) { + SqlDbPtr tdbptr {std::move(cache.top())}; + cache.pop(); + if (tdbptr->sqlDatabase().isOpen()) { + tSystemDebug("Gets cached database: {}", tdbptr->connectionName()); + return TSqlDatabase::Handle(std::move(tdbptr)); + } else { + tSystemError("Pooled database is not open: {} [{}:{}]", tdbptr->connectionName(), __FILE__, __LINE__); + stack.push(std::move(tdbptr)); + continue; + } + } + + if (!stack.empty()) { + SqlDbPtr tdbptr {std::move(stack.top())}; + stack.pop(); + if (!openDatabase(*tdbptr)) { + Tf::error("Database open error. Invalid database settings, or maximum number of SQL connection exceeded."); + tSystemError("SQL database open error: {}", tdbptr->sqlDatabase().connectionName()); + stack.push(std::move(tdbptr)); + return TSqlDatabase::Handle(); + } + + tSystemDebug("SQL database opened successfully (env:{})", Tf::app()->databaseEnvironment()); + tSystemDebug("Gets database: {}", tdbptr->sqlDatabase().connectionName()); + + // Executes setup-queries + if (!tdbptr->postOpenStatements().isEmpty()) { + TSqlQuery query(tdbptr->sqlDatabase()); + for (QString st : tdbptr->postOpenStatements()) { + st = st.trimmed(); + query.exec(st); + } + } + return TSqlDatabase::Handle(std::move(tdbptr)); + } + } + } + throw RuntimeException("No pooled connection", __FILE__, __LINE__); +} bool TSqlDatabasePool::setDatabaseSettings(TSqlDatabase &database, int databaseId) @@ -242,7 +354,7 @@ bool TSqlDatabasePool::setDatabaseSettings(TSqlDatabase &database, int databaseI return true; } - +/* void TSqlDatabasePool::pool(QSqlDatabase &database, bool forceClose) { QMutexLocker locker(&_mutex); @@ -272,6 +384,36 @@ void TSqlDatabasePool::pool(QSqlDatabase &database, bool forceClose) } database = QSqlDatabase(); // Sets an invalid object } +*/ + +void TSqlDatabasePool::pool(SqlDbPtr dbptr, bool forceClose) +{ + QMutexLocker locker(&_mutex); + + if (dbptr->isValid()) { + int databaseId = getDatabaseId(dbptr->sqlDatabase()); + + if (databaseId >= 0 && databaseId < Tf::app()->sqlDatabaseSettingsCount()) { + if (forceClose) { + tSystemWarn("Force close database: {}", dbptr->connectionName()); + closeDatabase(*dbptr); + availableDatabases[databaseId].push(std::move(dbptr)); + } else { + if (dbptr->sqlDatabase().isOpen()) { + // pool + tSystemDebug("Pooled cached connection, name: {}", dbptr->connectionName()); + cachedDatabases[databaseId].push(std::move(dbptr)); + lastCachedTime[databaseId].store((uint)std::time(nullptr)); + } else { + tSystemWarn("Pooled database, name: {}", dbptr->connectionName()); + availableDatabases[databaseId].push(std::move(dbptr)); + } + } + } else { + tSystemError("Pooled invalid database [{}:{}]", __FILE__, __LINE__); + } + } +} void TSqlDatabasePool::timerEvent(QTimerEvent *event) @@ -280,6 +422,7 @@ void TSqlDatabasePool::timerEvent(QTimerEvent *event) 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 +437,22 @@ 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(); + closeDatabase(*db); + availableDatabases[i].push(std::move(db)); + } + } +#endif } else { QObject::timerEvent(event); } @@ -323,7 +482,6 @@ void TSqlDatabasePool::closeDatabase(TSqlDatabase &database) QMutexLocker locker(&_mutex); QSqlDatabase db = database.sqlDatabase(); - int id = getDatabaseId(db); QString name = db.connectionName(); db.close(); @@ -334,16 +492,20 @@ void TSqlDatabasePool::closeDatabase(TSqlDatabase &database) database.setDriverExtension(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..d3a2ba318 100644 --- a/src/tsqldatabasepool.h +++ b/src/tsqldatabasepool.h @@ -1,27 +1,50 @@ #pragma once #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; + + template + class MoveStack : public std::deque + { + public: + MoveStack() = default; + MoveStack(const MoveStack &) = delete; + MoveStack &operator=(const MoveStack &) = delete; + MoveStack(MoveStack &&) noexcept = default; + MoveStack &operator=(MoveStack &&) noexcept = default; + void push(T value) { std::deque::push_back(std::move(value)); } + T pop() + { + T v = std::move(std::deque::back()); + std::deque::pop_back(); + return v; + } + T &top() { return std::deque::back(); } + const T &top() const { return std::deque::back(); } + }; + ~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 +53,18 @@ 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..ec60686e9 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,14 @@ void TSqlObject::syncToSqlRecord() } -QSqlDatabase &TSqlObject::getDatabase() +TSqlDatabase &TSqlObject::getDatabase() { +#if 0 if (!_database.isValid()) { _database = Tf::currentSqlDatabase(databaseId()); } return _database; +#else + return Tf::currentSqlDatabase(databaseId()); +#endif } diff --git a/src/tsqlobject.h b/src/tsqlobject.h index 21fc3558a..139ce7e90 100644 --- a/src/tsqlobject.h +++ b/src/tsqlobject.h @@ -7,6 +7,7 @@ #include #include #include +#include #include @@ -37,10 +38,12 @@ class T_CORE_EXPORT TSqlObject : public TModelObject, public QSqlRecord { protected: void syncToSqlRecord(); void syncToObject(); - QSqlDatabase &getDatabase(); + //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..a04f8d43e 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,21 +39,21 @@ 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); + _active = _database->sqlDatabase().transaction(); + _connectionName = _database->sqlDatabase().connectionName(); + int id = TSqlDatabasePool::getDatabaseId(_database->sqlDatabase()); if (Q_LIKELY(_active)) { Tf::traceQueryLog(time.elapsed(), "[BEGIN] [databaseId:{}] {}", id, _connectionName); } else { @@ -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 91c20bbef..a69041079 100644 --- a/src/tsqltransaction.h +++ b/src/tsqltransaction.h @@ -10,13 +10,11 @@ class T_CORE_EXPORT TSqlTransaction { public: TSqlTransaction(); - //TSqlTransaction(const TSqlTransaction &other) = default; - TSqlTransaction(TSqlTransaction &&) = default; ~TSqlTransaction(); - //TSqlTransaction &operator=(const TSqlTransaction &) = default; + TSqlTransaction(TSqlTransaction &&) = default; TSqlTransaction &operator=(TSqlTransaction &&) = default; - QSqlDatabase &database() { return _database; } + TSqlDatabase::Handle &database() { return _database; } bool begin(); bool commit(); bool rollback(); @@ -25,7 +23,7 @@ class T_CORE_EXPORT TSqlTransaction { void setDisabled(bool disable); private: - QSqlDatabase _database; + TSqlDatabase::Handle _database; bool _enabled {true}; bool _active {false}; QString _connectionName; From afc4fe04763a9ead5687fda2be4aded5ac63f901 Mon Sep 17 00:00:00 2001 From: aoyama Date: Sun, 16 Nov 2025 22:57:19 +0900 Subject: [PATCH 07/16] temp --- src/tsqldatabase.cpp | 31 +++++++++++++++++++++++-- src/tsqldatabase.h | 50 ++++------------------------------------ src/tsqldatabasepool.cpp | 5 ++-- src/tsqldatabasepool.h | 4 +--- 4 files changed, 38 insertions(+), 52 deletions(-) diff --git a/src/tsqldatabase.cpp b/src/tsqldatabase.cpp index 5f35d3d02..7bb831bd2 100644 --- a/src/tsqldatabase.cpp +++ b/src/tsqldatabase.cpp @@ -21,15 +21,42 @@ class TDatabaseDict : public QSet { 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 = other._sqlDatabase; + _postOpenStatements = 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); } diff --git a/src/tsqldatabase.h b/src/tsqldatabase.h index b1e94780b..713a7796f 100644 --- a/src/tsqldatabase.h +++ b/src/tsqldatabase.h @@ -43,8 +43,8 @@ class T_CORE_EXPORT TSqlDatabase { }; + ~TSqlDatabase(); TSqlDatabase(TSqlDatabase &&other); - ~TSqlDatabase() { } TSqlDatabase &operator=(TSqlDatabase &&other); DbmsType dbmsType() const; @@ -58,7 +58,7 @@ 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 std::unique_ptr database(const QString &connectionName = QLatin1String(defaultConnection)); @@ -68,55 +68,15 @@ class T_CORE_EXPORT TSqlDatabase { private: explicit TSqlDatabase(const QSqlDatabase &database = QSqlDatabase()); - TSqlDriverExtension *driverExtension() { return _driverExtension; } - void setDriverExtension(TSqlDriverExtension *extension); + 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(TSqlDatabase &&other) -{ - *this = std::move(other); -} - -// inline TSqlDatabase &TSqlDatabase::operator=(const TSqlDatabase &other) -// { -// _sqlDatabase = other._sqlDatabase; -// _postOpenStatements = other._postOpenStatements; -// _enableUpsert = other._enableUpsert; -// _driverExtension = other._driverExtension; -// return *this; -// } - -inline TSqlDatabase &TSqlDatabase::operator=(TSqlDatabase &&other) -{ - if (this != &other) { - _sqlDatabase = other._sqlDatabase; - _postOpenStatements = other._postOpenStatements; - _enableUpsert = other._enableUpsert; - _driverExtension = other._driverExtension; - other._driverExtension = nullptr; - } - return *this; -} diff --git a/src/tsqldatabasepool.cpp b/src/tsqldatabasepool.cpp index f780b5a36..edaf7e7dc 100644 --- a/src/tsqldatabasepool.cpp +++ b/src/tsqldatabasepool.cpp @@ -8,6 +8,7 @@ #include "tsqldatabasepool.h" #include "tsqldatabase.h" #include "tsqldriverextensionfactory.h" +#include "tsqldriverextension.h" #include "tsystemglobal.h" #include "tstack.h" #include @@ -470,7 +471,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; @@ -489,7 +490,7 @@ 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); } diff --git a/src/tsqldatabasepool.h b/src/tsqldatabasepool.h index d3a2ba318..78f661a1f 100644 --- a/src/tsqldatabasepool.h +++ b/src/tsqldatabasepool.h @@ -19,8 +19,7 @@ class T_CORE_EXPORT TSqlDatabasePool : public QObject { using SqlDbPtr = std::unique_ptr; template - class MoveStack : public std::deque - { + class MoveStack : public std::deque { public: MoveStack() = default; MoveStack(const MoveStack &) = delete; @@ -60,7 +59,6 @@ class T_CORE_EXPORT TSqlDatabasePool : public QObject { TAtomic *lastCachedTime {nullptr}; int maxConnects {0}; QBasicTimer timer; - std::vector> availableDatabases; std::vector> cachedDatabases; From 0cef30f780204452a0071aa58bbe39f8d7d9340f Mon Sep 17 00:00:00 2001 From: aoyama Date: Fri, 21 Nov 2025 21:35:00 +0900 Subject: [PATCH 08/16] updated --- src/tdatabasecontext.cpp | 78 ++++++------ src/tdatabasecontext.h | 7 +- src/tglobal.cpp | 1 - src/tkvsdatabase.cpp | 101 +++++++++++----- src/tkvsdatabase.h | 44 +++++-- src/tkvsdatabasepool.cpp | 221 +++++++++++++++++++++++++++++++++- src/tkvsdatabasepool.h | 57 ++++----- src/tsqldatabase.cpp | 24 +++- src/tsqldatabase.h | 16 +-- src/tsqldatabasepool.cpp | 43 ++++--- src/tsqldatabasepool.h | 2 +- src/tsqlobject.cpp | 7 -- src/tsqlobject.h | 1 - src/tsqltransaction.cpp | 2 +- src/turingcoroutine.h | 1 - src/turingcoroutine_linux.cpp | 6 +- src/turingserver_linux.cpp | 1 - 17 files changed, 440 insertions(+), 172 deletions(-) diff --git a/src/tdatabasecontext.cpp b/src/tdatabasecontext.cpp index 1028f29a9..f5d0df627 100644 --- a/src/tdatabasecontext.cpp +++ b/src/tdatabasecontext.cpp @@ -9,25 +9,22 @@ #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; } -// std::map TDatabaseContext::sqlDatabases; -// std::map TDatabaseContext::kvsDatabases; - /*! \class TDatabaseContext \brief The TDatabaseContext class is the base class of contexts for @@ -35,17 +32,14 @@ QSqlDatabase invalidDb; */ TDatabaseContext::TDatabaseContext() -//: - // sqlDatabases(), - // kvsDatabases() { const int count = Tf::app()->sqlDatabaseSettingsCount(); - if (sqlDatabases.size() < (size_t)count) { - sqlDatabases.resize(count); + if (sqlTransactions.size() < (size_t)count) { + sqlTransactions.resize(count); } - if (kvsDatabases.size() < (size_t)count) { - kvsDatabases.resize(count); + if (kvsDatabases.size() < (size_t)Tf::KvsEngine::Num) { + kvsDatabases.resize((size_t)Tf::KvsEngine::Num); } } @@ -60,17 +54,17 @@ TDatabaseContext::~TDatabaseContext() TSqlDatabase &TDatabaseContext::getSqlDatabase(int id) { if (id < 0) { - throw RuntimeException("error database id", __FILE__, __LINE__);throw RuntimeException("error database id", __FILE__, __LINE__); + throw RuntimeException("error database id", __FILE__, __LINE__); } if (id >= Tf::app()->sqlDatabaseSettingsCount()) { throw RuntimeException("error database id", __FILE__, __LINE__); } - TSqlTransaction &tx = sqlDatabases[id]; + TSqlTransaction &tx = sqlTransactions[id]; TSqlDatabase::Handle &handle = tx.database(); - if (handle && handle->isValid() && tx.isActive()) { + if (handle && handle->isValid()) { return *handle; } @@ -83,6 +77,8 @@ TSqlDatabase &TDatabaseContext::getSqlDatabase(int id) if (tx.begin()) { break; } + + handle = std::move(TSqlDatabase::Handle{}); } while (++n < 2); // try two times idleElapsed = (uint)std::time(nullptr); @@ -93,29 +89,34 @@ TSqlDatabase &TDatabaseContext::getSqlDatabase(int id) void TDatabaseContext::releaseSqlDatabases() { rollbackTransactions(); - sqlDatabases.clear(); + + for (auto &tx : sqlTransactions) { + if (tx.database()) { + tx.database() = TSqlDatabase::Handle{}; + } + } } TKvsDatabase &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 (auto it = kvsDatabases.begin(); it != kvsDatabases.end(); ++it) { - // TKvsDatabasePool::instance()->pool(it->second); - TKvsDatabasePool::instance()->pool(*it); + for (auto &handle : kvsDatabases) { + if (handle) { + handle = TKvsDatabase::Handle{}; + } } - kvsDatabases.clear(); } @@ -137,19 +138,14 @@ void TDatabaseContext::setTransactionEnabled(bool enable, int id) Tf::error("Invalid database ID: {}", id); return; } -tSystemDebug("### setTransactionEnabled : id:{}", id); - return sqlDatabases[id].setEnabled(enable); + return sqlTransactions[id].setEnabled(enable); } void TDatabaseContext::commitTransactions() { - for (auto it = sqlDatabases.begin(); it != sqlDatabases.end(); ++it) { - // TSqlTransaction &tx = it->second; - // tx.commit(); - // TSqlDatabasePool::instance()->pool(tx.database()); + for (auto it = sqlTransactions.begin(); it != sqlTransactions.end(); ++it) { it->commit(); - //TSqlDatabasePool::instance()->pool(it->database()); もともとはpoolで戻していたが必要か? } } @@ -158,26 +154,21 @@ bool TDatabaseContext::commitTransaction(int id) { bool res = false; - if (id < 0 || id >= (int)sqlDatabases.size()) { + 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 (auto it = sqlDatabases.begin(); it != sqlDatabases.end(); ++it) { - // TSqlTransaction &tx = it->second; - // tx.rollback(); - // TSqlDatabasePool::instance()->pool(tx.database(), true); + for (auto it = sqlTransactions.begin(); it != sqlTransactions.end(); ++it) { it->rollback(); - //TSqlDatabasePool::instance()->pool(it->database(), true); } } @@ -186,12 +177,11 @@ bool TDatabaseContext::rollbackTransaction(int id) { bool res = false; - if (id < 0 || id >= (int)sqlDatabases.size()) { + 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; } diff --git a/src/tdatabasecontext.h b/src/tdatabasecontext.h index bfb58384a..4480e7cc9 100644 --- a/src/tdatabasecontext.h +++ b/src/tdatabasecontext.h @@ -1,13 +1,12 @@ #pragma once -//#include #include #include +#include #include #include "tsqldatabasepool.h" #include class QSqlDatabase; -class TKvsDatabase; class TCache; @@ -36,8 +35,8 @@ class T_CORE_EXPORT TDatabaseContext { void releaseKvsDatabases(); void releaseSqlDatabases(); - std::vector sqlDatabases; - std::vector kvsDatabases; + std::vector sqlTransactions; + std::vector kvsDatabases; private: uint idleElapsed {0}; diff --git a/src/tglobal.cpp b/src/tglobal.cpp index 80d68a857..b797e2cac 100644 --- a/src/tglobal.cpp +++ b/src/tglobal.cpp @@ -211,7 +211,6 @@ TSqlDatabase &Tf::currentSqlDatabase(int id) noexcept QMap> *Tf::objectFactories() noexcept -//std::map> *Tf::objectFactories() noexcept { static QMap> objectFactoryMap; return &objectFactoryMap; diff --git a/src/tkvsdatabase.cpp b/src/tkvsdatabase.cpp index dee348928..0af38d0f2 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 @@ -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); + if (!dict->contains(connectionName)) { + tSystemWarn("No such KVS database: {}", connectionName); + return std::unique_ptr{new TKvsDatabase{}}; + } + const TKvsDatabaseSettings &d = (*dict)[connectionName]; - return TKvsDatabase(d.connectionName, d.driver); + 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); @@ -93,7 +99,7 @@ TKvsDatabase TKvsDatabase::addDatabase(const QString &driver, const QString &con data.connectionName = connectionName; data.driver = createDriver(driver); // creates a driver dict->insert(connectionName, data); - return TKvsDatabase(data); + return true; } @@ -105,7 +111,7 @@ void TKvsDatabase::removeDatabase(const QString &connectionName) TKvsDatabase db(dict->take(connectionName)); db.close(); - delete db.drv; + delete db._driver; } @@ -118,15 +124,15 @@ TKvsDatabaseSettings TKvsDatabase::settings(const QString &connectionName) TKvsDatabase::TKvsDatabase(const QString &connectionName, TKvsDriver *driver) : - connectName(connectionName), - drv(driver) + _connectName(connectionName), + _driver(driver) { } TKvsDatabase::TKvsDatabase(const TKvsDatabaseSettings &data) : - connectName(data.connectionName), - drv(data.driver) + _connectName(data.connectionName), + _driver(data.driver) { } @@ -141,7 +147,7 @@ bool TKvsDatabase::open() { auto *dict = databaseDict(); QReadLocker locker(&dict->lock); - const TKvsDatabaseSettings &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; } @@ -176,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; } } @@ -194,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; } } @@ -212,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; } } @@ -230,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; } } @@ -248,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; } } @@ -266,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; } } @@ -284,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; } } @@ -304,3 +310,34 @@ void TKvsDatabase::moveToThread(QThread *targetThread) driver()->moveToThread(targetThread); } } + + +TKvsDatabase &TKvsDatabase::operator=(TKvsDatabase &&other) +{ + if (this != &other) { + _connectName = 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 54ace109f..dda619cdf 100644 --- a/src/tkvsdatabase.h +++ b/src/tkvsdatabase.h @@ -1,6 +1,7 @@ #pragma once #include #include +#include class TKvsDriver; class TKvsDatabaseSettings; @@ -8,13 +9,34 @@ class TKvsDatabaseSettings; class T_CORE_EXPORT TKvsDatabase { public: - TKvsDatabase() { } - ~TKvsDatabase() { } - TKvsDatabase(TKvsDatabase &&) = default; - TKvsDatabase &operator=(TKvsDatabase &&) = default; + using KvsDbPtr = std::unique_ptr; + + class Handle { + public: + Handle() = default; + Handle(KvsDbPtr ptr) : _dbptr(std::move(ptr)) {} + Handle(const Handle &) = delete; + Handle &operator=(const Handle &) = delete; + Handle(Handle &&other) noexcept = default; + 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,19 +57,19 @@ 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 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 TKvsDatabaseSettings &data); diff --git a/src/tkvsdatabasepool.cpp b/src/tkvsdatabasepool.cpp index 97d1b9ffa..c872c9779 100644 --- a/src/tkvsdatabasepool.cpp +++ b/src/tkvsdatabasepool.cpp @@ -7,7 +7,6 @@ #include "tkvsdatabasepool.h" #include "tfnamespace.h" -#include "tsqldatabasepool.h" #include "tsystemglobal.h" #include "tstack.h" #include @@ -72,6 +71,7 @@ TKvsDatabasePool::TKvsDatabasePool() : TKvsDatabasePool::~TKvsDatabasePool() { +#if 0 QMutexLocker locker(&_mutex); timer.stop(); @@ -98,11 +98,40 @@ TKvsDatabasePool::~TKvsDatabasePool() delete[] cachedDatabase; delete[] lastCachedTime; delete[] availableNames; +#else + QMutexLocker locker(&_mutex); + timer.stop(); + + for (int eng = 0; eng < (int)Tf::KvsEngine::Num; eng++) { + if (!Tf::app()->isKvsAvailable((Tf::KvsEngine)eng)) { + continue; + } + + auto &cache = cachedDatabases[eng]; + QString name; + while (!cache.empty()) { + auto db = cache.pop(); + name = db->connectionName(); + db->close(); + TKvsDatabase::removeDatabase(name); + } + + auto &stack = availableDatabases[eng]; + while (!stack.empty()) { + auto db = stack.pop(); + name = db->connectionName(); + TKvsDatabase::removeDatabase(name); + } + } + + delete[] lastCachedTime; +#endif } void TKvsDatabasePool::init() { +#if 0 if (cachedDatabase) { return; } @@ -145,11 +174,58 @@ void TKvsDatabasePool::init() // Starts the timer to close extra-connection timer.start(10000, this); } +#else + if (lastCachedTime) { + return; + } + + QMutexLocker locker(&_mutex); + + cachedDatabases.resize((int)Tf::KvsEngine::Num); + availableDatabases.resize((int)Tf::KvsEngine::Num); + lastCachedTime = new TAtomic[(int)Tf::KvsEngine::Num]; + bool aval = false; + + // Adds databases previously + for (auto it = kvsEngineHash()->begin(); it != kvsEngineHash()->end(); ++it) { + Tf::KvsEngine engine = it.key(); + const QString &drv = it.value(); + + if (!Tf::app()->isKvsAvailable(engine)) { + tSystemDebug("KVS database not available. engine:{}", (int)engine); + continue; + } else { + aval = true; + tSystemDebug("KVS database available. engine:{}", (int)engine); + } + + auto &stack = availableDatabases[(int)engine]; + for (int i = 0; i < maxConnects; ++i) { + 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); + tSystemDebug("Add KVS successfully. name:{}", db->connectionName()); + stack.push(std::move(db)); // push onto stack + } + } + + if (aval) { + // Starts the timer to close extra-connection + timer.start(10000, this); + } +#endif } -TKvsDatabase TKvsDatabasePool::database(Tf::KvsEngine engine) +TKvsDatabase::Handle TKvsDatabasePool::database(Tf::KvsEngine engine) { +#if 0 QMutexLocker locker(&_mutex); if (!Tf::app()->isKvsAvailable(engine)) { @@ -226,6 +302,82 @@ TKvsDatabase TKvsDatabasePool::database(Tf::KvsEngine engine) } throw RuntimeException("No pooled connection", __FILE__, __LINE__); +#else + QMutexLocker locker(&_mutex); + + if (!Tf::app()->isKvsAvailable(engine)) { + switch (engine) { + case Tf::KvsEngine::MongoDB: + tSystemError("MongoDB not available. Check the settings file."); + break; + + case Tf::KvsEngine::Redis: + tSystemError("Redis not available. Check the settings file."); + break; + + case Tf::KvsEngine::Memcached: + tSystemError("Memcached not available. Check the settings file."); + break; + + case Tf::KvsEngine::SharedMemory: + tSystemWarn("SharedMemory not available. Check the settings file."); + break; + + case Tf::KvsEngine::CacheKvs: + tSystemError("CacheKvs not available. Check the settings file."); + break; + + default: + throw RuntimeException("No such KVS engine", __FILE__, __LINE__); + break; + } + return TKvsDatabase::Handle(); + } + + auto &cache = cachedDatabases[(int)engine]; + auto &stack = availableDatabases[(int)engine]; + + for (;;) { + QString name; + if (!cache.empty()) { + KvsDbPtr dbptr {cache.pop()}; + 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: {} [{}:{}]", dbptr->connectionName(), __FILE__, __LINE__); + stack.push(std::move(dbptr)); + continue; + } + } + + if (!stack.empty()) { + KvsDbPtr dbptr {stack.pop()}; + dbptr->moveToThread(QThread::currentThread()); // move to thread + + 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(), dbptr->connectionName(), dbptr->databaseName()); + tSystemDebug("Gets KVS database: {}", dbptr->connectionName()); + + // 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__); +#endif } @@ -282,6 +434,7 @@ bool TKvsDatabasePool::setDatabaseSettings(TKvsDatabase &database, Tf::KvsEngine TKvsDatabaseSettings TKvsDatabasePool::getDatabaseSettings(Tf::KvsEngine engine) const { +#if 0 QMutexLocker locker(&_mutex); TKvsDatabaseSettings settrings; @@ -291,17 +444,32 @@ TKvsDatabaseSettings TKvsDatabasePool::getDatabaseSettings(Tf::KvsEngine engine) for (;;) { if (Q_LIKELY(!stack.isEmpty())) { name = stack.pop(); - return TKvsDatabase::settings(name); stack.push(name); + return TKvsDatabase::settings(name); } } throw RuntimeException("No pooled connection", __FILE__, __LINE__); +#else + QMutexLocker locker(&_mutex); + + TKvsDatabaseSettings settrings; + auto &stack = availableDatabases[(int)engine]; + + if (!stack.empty()) { + auto &db = stack.top(); + QString name = db->connectionName(); + return TKvsDatabase::settings(name); + } + + throw RuntimeException("No pooled connection", __FILE__, __LINE__); +#endif } -void TKvsDatabasePool::pool(TKvsDatabase &database) +void TKvsDatabasePool::pool(KvsDbPtr dbptr) { +#if 0 QMutexLocker locker(&_mutex); if (Q_LIKELY(database.isValid())) { @@ -321,11 +489,32 @@ void TKvsDatabasePool::pool(TKvsDatabase &database) } } database = TKvsDatabase(); // Sets an invalid object +#else + QMutexLocker locker(&_mutex); + + if (dbptr->isValid()) { + bool ok; + int engine = dbptr->connectionName().left(2).toInt(&ok); + if (!ok) { + throw RuntimeException("No such KVS engine", __FILE__, __LINE__); + } + + 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)); + } else { + tSystemWarn("Closed KVS database connection, name: {}", dbptr->connectionName()); + availableDatabases[engine].push(std::move(dbptr)); + } + } +#endif } void TKvsDatabasePool::timerEvent(QTimerEvent *event) { +#if 0 if (event->timerId() == timer.timerId()) { QMutexLocker locker(&_mutex); QString name; @@ -348,6 +537,30 @@ void TKvsDatabasePool::timerEvent(QTimerEvent *event) } else { QObject::timerEvent(event); } +#else + if (event->timerId() == timer.timerId()) { + QMutexLocker locker(&_mutex); + QString name; + + // Closes extra-connection + for (int e = 0; e < kvsEngineHash()->count(); e++) { + if (!Tf::app()->isKvsAvailable((Tf::KvsEngine)e)) { + continue; + } + + auto &cache = cachedDatabases[e]; + while (lastCachedTime[e].load() < (uint)std::time(nullptr) - 30 + && !cache.empty()) { + auto db = cache.pop(); + db->close(); + tSystemDebug("Closed KVS database connection, name: {}", db->connectionName()); + availableDatabases[e].push(std::move(db)); + } + } + } else { + QObject::timerEvent(event); + } +#endif } diff --git a/src/tkvsdatabasepool.h b/src/tkvsdatabasepool.h index ee689a2db..73cf0e241 100644 --- a/src/tkvsdatabasepool.h +++ b/src/tkvsdatabasepool.h @@ -7,46 +7,39 @@ #include #include #include - -class QSettings; -template class TStack; +#include +#include class T_CORE_EXPORT TKvsDatabasePool : public QObject { Q_OBJECT public: - using TKvsDBConn = std::unique_ptr; - - ~TKvsDatabasePool(); - TKvsDatabase database(Tf::KvsEngine engine); - void pool(TKvsDatabase &database); - TKvsDatabaseSettings getDatabaseSettings(Tf::KvsEngine engine) const; - - static TKvsDatabasePool *instance(); + using KvsDbPtr = std::unique_ptr; - // TKvsDatabase handle - class Handle { + template + class MoveStack : public std::deque { public: - Handle() = default; - Handle(TKvsDBConn conn) : _conn(std::move(conn)) {} - Handle(const Handle &) = delete; - Handle &operator=(const Handle &) = delete; - Handle(Handle &&other) noexcept = default; - Handle &operator=(Handle &&other) noexcept = default; - - ~Handle() + MoveStack() = default; + MoveStack(const MoveStack &) = delete; + MoveStack &operator=(const MoveStack &) = delete; + MoveStack(MoveStack &&) noexcept = default; + MoveStack &operator=(MoveStack &&) noexcept = default; + void push(T value) { std::deque::push_back(std::move(value)); } + T pop() { - if (_conn) { - TKvsDatabasePool::instance()->pool(*(_conn.get())); - } + T v = std::move(std::deque::back()); + std::deque::pop_back(); + return v; } + T &top() { return std::deque::back(); } + const T &top() const { return std::deque::back(); } + }; - TKvsDatabase *operator->() { return _conn.get(); } - TKvsDatabase &operator*() { return *(_conn.get()); } + ~TKvsDatabasePool(); + TKvsDatabase::Handle database(Tf::KvsEngine engine); + TKvsDatabaseSettings getDatabaseSettings(Tf::KvsEngine engine) const; - private: - TKvsDBConn _conn; - }; + static TKvsDatabasePool *instance(); protected: void init(); @@ -57,14 +50,16 @@ class T_CORE_EXPORT TKvsDatabasePool : public QObject { private: TKvsDatabasePool(); + void pool(KvsDbPtr dbptr); 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(TKvsDatabasePool) T_DISABLE_MOVE(TKvsDatabasePool) + friend class TKvsDatabase; }; diff --git a/src/tsqldatabase.cpp b/src/tsqldatabase.cpp index 7bb831bd2..c904e2e5e 100644 --- a/src/tsqldatabase.cpp +++ b/src/tsqldatabase.cpp @@ -65,11 +65,12 @@ std::unique_ptr TSqlDatabase::database(const QString &connectionNa auto *dict = dbDict(); QReadLocker locker(&dict->lock); - if (dict->contains(connectionName)) { - return std::unique_ptr{new TSqlDatabase{QSqlDatabase::database(connectionName)}}; - } else { + 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)}}; } @@ -123,8 +124,19 @@ bool TSqlDatabase::isPreparedStatementSupported() const TSqlDatabase::Handle::~Handle() { - tSystemDebug("Handle::~Handle"); - if (_conn) { - TSqlDatabasePool::instance()->pool(std::move(_conn)); + 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 713a7796f..330495304 100644 --- a/src/tsqldatabase.h +++ b/src/tsqldatabase.h @@ -1,8 +1,10 @@ #pragma once +#include +#include #include #include #include -#include + class TSqlDriverExtension; @@ -27,19 +29,19 @@ class T_CORE_EXPORT TSqlDatabase { class Handle { public: Handle() = default; - Handle(SqlDbPtr conn) : _conn(std::move(conn)) {} + 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 = default; + Handle &operator=(Handle &&other) noexcept; ~Handle(); - TSqlDatabase *operator->() { return _conn.get(); } - TSqlDatabase &operator*() { return *(_conn.get()); } - explicit operator bool() const noexcept { return (bool)_conn; } + TSqlDatabase *operator->() { return _dbptr.get(); } + TSqlDatabase &operator*() { return *(_dbptr.get()); } + explicit operator bool() const noexcept { return static_cast(_dbptr); } private: - SqlDbPtr _conn; + SqlDbPtr _dbptr; }; diff --git a/src/tsqldatabasepool.cpp b/src/tsqldatabasepool.cpp index edaf7e7dc..ea6efed8c 100644 --- a/src/tsqldatabasepool.cpp +++ b/src/tsqldatabasepool.cpp @@ -144,12 +144,16 @@ void TSqlDatabasePool::init() } #endif + if (lastCachedTime) { + return; + } + const int dbcount = Tf::app()->sqlDatabaseSettingsCount(); - bool aval = false; cachedDatabases.resize(dbcount); availableDatabases.resize(dbcount); lastCachedTime = new TAtomic[dbcount]; tSystemDebug("SQL database available. maxConnects:{}", maxConnects); + bool aval = false; // Adds databases previously for (int j = 0; j < dbcount; ++j) { @@ -246,44 +250,45 @@ TSqlDatabase::Handle TSqlDatabasePool::database(int databaseId) auto &cache = cachedDatabases[databaseId]; auto &stack = availableDatabases[databaseId]; - for (;;) { + while (true) { QString name; if (!cache.empty()) { - SqlDbPtr tdbptr {std::move(cache.top())}; - cache.pop(); - if (tdbptr->sqlDatabase().isOpen()) { - tSystemDebug("Gets cached database: {}", tdbptr->connectionName()); - return TSqlDatabase::Handle(std::move(tdbptr)); + SqlDbPtr sqlptr {cache.pop()}; + if (sqlptr->sqlDatabase().isOpen()) { + tSystemDebug("Gets cached database: {}", sqlptr->connectionName()); + return TSqlDatabase::Handle(std::move(sqlptr)); } else { - tSystemError("Pooled database is not open: {} [{}:{}]", tdbptr->connectionName(), __FILE__, __LINE__); - stack.push(std::move(tdbptr)); + tSystemError("Pooled database is not open: {} [{}:{}]", sqlptr->connectionName(), __FILE__, __LINE__); + stack.push(std::move(sqlptr)); continue; } } if (!stack.empty()) { - SqlDbPtr tdbptr {std::move(stack.top())}; - stack.pop(); - if (!openDatabase(*tdbptr)) { + SqlDbPtr sqlptr {stack.pop()}; + if (!openDatabase(*sqlptr)) { Tf::error("Database open error. Invalid database settings, or maximum number of SQL connection exceeded."); - tSystemError("SQL database open error: {}", tdbptr->sqlDatabase().connectionName()); - stack.push(std::move(tdbptr)); + tSystemError("SQL database open error: {}", sqlptr->sqlDatabase().connectionName()); + stack.push(std::move(sqlptr)); return TSqlDatabase::Handle(); } tSystemDebug("SQL database opened successfully (env:{})", Tf::app()->databaseEnvironment()); - tSystemDebug("Gets database: {}", tdbptr->sqlDatabase().connectionName()); + tSystemDebug("Gets database: {}", sqlptr->sqlDatabase().connectionName()); // Executes setup-queries - if (!tdbptr->postOpenStatements().isEmpty()) { - TSqlQuery query(tdbptr->sqlDatabase()); - for (QString st : tdbptr->postOpenStatements()) { + if (!sqlptr->postOpenStatements().isEmpty()) { + TSqlQuery query(sqlptr->sqlDatabase()); + for (QString st : sqlptr->postOpenStatements()) { st = st.trimmed(); query.exec(st); } } - return TSqlDatabase::Handle(std::move(tdbptr)); + return TSqlDatabase::Handle(std::move(sqlptr)); } + + tSystemError("Empty available databases [{}:{}]", __FILE__, __LINE__); + break; } } throw RuntimeException("No pooled connection", __FILE__, __LINE__); diff --git a/src/tsqldatabasepool.h b/src/tsqldatabasepool.h index 78f661a1f..dd376c0ef 100644 --- a/src/tsqldatabasepool.h +++ b/src/tsqldatabasepool.h @@ -10,7 +10,7 @@ #include #include #include -#include +#include class T_CORE_EXPORT TSqlDatabasePool : public QObject { diff --git a/src/tsqlobject.cpp b/src/tsqlobject.cpp index ec60686e9..959d3f616 100644 --- a/src/tsqlobject.cpp +++ b/src/tsqlobject.cpp @@ -541,12 +541,5 @@ void TSqlObject::syncToSqlRecord() TSqlDatabase &TSqlObject::getDatabase() { -#if 0 - if (!_database.isValid()) { - _database = Tf::currentSqlDatabase(databaseId()); - } - return _database; -#else return Tf::currentSqlDatabase(databaseId()); -#endif } diff --git a/src/tsqlobject.h b/src/tsqlobject.h index 139ce7e90..ef56f3504 100644 --- a/src/tsqlobject.h +++ b/src/tsqlobject.h @@ -38,7 +38,6 @@ class T_CORE_EXPORT TSqlObject : public TModelObject, public QSqlRecord { protected: void syncToSqlRecord(); void syncToObject(); - //QSqlDatabase &getDatabase(); TSqlDatabase &getDatabase(); QSqlError sqlError; diff --git a/src/tsqltransaction.cpp b/src/tsqltransaction.cpp index a04f8d43e..e9f803b38 100644 --- a/src/tsqltransaction.cpp +++ b/src/tsqltransaction.cpp @@ -54,7 +54,7 @@ bool TSqlTransaction::begin() _active = _database->sqlDatabase().transaction(); _connectionName = _database->sqlDatabase().connectionName(); int id = TSqlDatabasePool::getDatabaseId(_database->sqlDatabase()); - if (Q_LIKELY(_active)) { + if (_active) { Tf::traceQueryLog(time.elapsed(), "[BEGIN] [databaseId:{}] {}", id, _connectionName); } else { Tf::traceQueryLog(time.elapsed(), "[BEGIN Failed] [databaseId:{}] {}", id, _connectionName); diff --git a/src/turingcoroutine.h b/src/turingcoroutine.h index 9ad68bfd4..eed25639d 100644 --- a/src/turingcoroutine.h +++ b/src/turingcoroutine.h @@ -26,7 +26,6 @@ class TUringCoroutine : public TActionContext { virtual ~TUringCoroutine(); Task start(); - //static TUringCoroutine *currentRoutine(); protected: virtual int64_t writeResponse(THttpResponseHeader &, QIODevice *) override; diff --git a/src/turingcoroutine_linux.cpp b/src/turingcoroutine_linux.cpp index 28f1b2e6f..79d5b2793 100644 --- a/src/turingcoroutine_linux.cpp +++ b/src/turingcoroutine_linux.cpp @@ -166,7 +166,11 @@ Task TUringCoroutine::start() int len = co_await AsyncRecv(_sd, readBuffer.data() + readLength, buflen, timeout); if (len < 0) { // timeout or error - tSystemError("Recv error fd:{} error:{}", _sd, strerror(-len)); + if (len == -ETIME) { + tSystemDebug("Recv timer expired fd:{}", _sd); + } else { + tSystemError("Recv error fd:{} error:{}", _sd, strerror(-len)); + } co_return; } if (!len) { diff --git a/src/turingserver_linux.cpp b/src/turingserver_linux.cpp index f783c7de2..d4f062b5f 100644 --- a/src/turingserver_linux.cpp +++ b/src/turingserver_linux.cpp @@ -422,7 +422,6 @@ int TUringServer::addEvent(int fd, TAwaitBase* await) const } - void TUringServer::registerForGC(TUringCoroutine *coroutine) { _garbage.push_back(coroutine); From 9f30f8ac95b144e4318a781254f1c246f4de2f1f Mon Sep 17 00:00:00 2001 From: aoyama Date: Sat, 22 Nov 2025 09:35:08 +0900 Subject: [PATCH 09/16] updated --- src/tactionthread.cpp | 7 ------- src/thttprequest.cpp | 14 +------------- src/thttprequest.h | 6 +----- src/tsqldatabasepool.cpp | 28 +--------------------------- 4 files changed, 3 insertions(+), 52 deletions(-) diff --git a/src/tactionthread.cpp b/src/tactionthread.cpp index 4ac345d04..884d5b5a2 100644 --- a/src/tactionthread.cpp +++ b/src/tactionthread.cpp @@ -185,16 +185,9 @@ void TActionThread::emitError(int socketError) THttpRequest TActionThread::readRequest(THttpSocket *socket) { - //THttpRequest req; - for (;;) { if (socket->waitForReadyReadRequest(500)) { return socket->read(); - // if (!req.isEmpty()) { - // return req; - // } else { - // break; - // } } if (Q_UNLIKELY(socket->state() != QAbstractSocket::ConnectedState)) { diff --git a/src/thttprequest.cpp b/src/thttprequest.cpp index 0559e84ba..76c297884 100644 --- a/src/thttprequest.cpp +++ b/src/thttprequest.cpp @@ -43,7 +43,6 @@ static bool httpMethodOverride() */ THttpRequestData::THttpRequestData(const THttpRequestData &other) : - //QSharedData(other), header(other.header), bodyArray(other.bodyArray), queryItems(other.queryItems), @@ -65,24 +64,13 @@ THttpRequestData::THttpRequestData(const THttpRequestData &other) : */ THttpRequest::THttpRequest() : d(std::make_unique()) - //d(new THttpRequestData) { } -/*! - \fn THttpRequest::THttpRequest(const THttpRequest &other) - Copy constructor. -*/ -// THttpRequest::THttpRequest(const THttpRequest &other) : -// d(other.d) -// { -// } - /*! 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; @@ -96,7 +84,7 @@ 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(new THttpRequestData{}) { d->header = THttpRequestHeader(header); d->clientAddress = clientAddress; diff --git a/src/thttprequest.h b/src/thttprequest.h index 9ff9171c7..81e4e3b5a 100644 --- a/src/thttprequest.h +++ b/src/thttprequest.h @@ -36,11 +36,9 @@ class T_CORE_EXPORT 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(THttpRequest&&) = default; THttpRequest &operator=(THttpRequest &&) = default; @@ -96,13 +94,11 @@ 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; + T_DISABLE_COPY(THttpRequest) }; Q_DECLARE_METATYPE(THttpRequest) - diff --git a/src/tsqldatabasepool.cpp b/src/tsqldatabasepool.cpp index ea6efed8c..9092b8da6 100644 --- a/src/tsqldatabasepool.cpp +++ b/src/tsqldatabasepool.cpp @@ -26,7 +26,7 @@ constexpr auto CONN_NAME_FORMAT = "rdb%02d_%d"; TSqlDatabasePool *TSqlDatabasePool::instance() { static std::unique_ptr databasePool = []() { - std::unique_ptr pool { new TSqlDatabasePool }; + std::unique_ptr pool{new TSqlDatabasePool}; pool->maxConnects = Tf::app()->maxNumberOfThreadsPerAppServer(); pool->init(); return pool; @@ -43,31 +43,6 @@ TSqlDatabasePool::TSqlDatabasePool() : TSqlDatabasePool::~TSqlDatabasePool() { -#if 0 - QMutexLocker locker(&_mutex); - timer.stop(); - - for (int j = 0; j < Tf::app()->sqlDatabaseSettingsCount(); ++j) { - auto &cache = cachedDatabase[j]; - QString name; - while (!cache.isEmpty()) { - name = cache.pop(); - auto &db = TSqlDatabase::database(name); - closeDatabase(db); - TSqlDatabase::removeDatabase(name); - } - - auto &stack = availableNames[j]; - while (!stack.isEmpty()) { - name = stack.pop(); - TSqlDatabase::removeDatabase(name); - } - } - - delete[] cachedDatabase; - delete[] lastCachedTime; - delete[] availableNames; -#else QMutexLocker locker(&_mutex); timer.stop(); @@ -89,7 +64,6 @@ TSqlDatabasePool::~TSqlDatabasePool() } delete[] lastCachedTime; -#endif } From 1eae0f7e43a64e63c491a528e3b9a2cb1d115345 Mon Sep 17 00:00:00 2001 From: aoyama Date: Sun, 23 Nov 2025 17:35:51 +0900 Subject: [PATCH 10/16] update --- include/headers.pri | 2 + src/tatomic.h | 9 +- src/tatomicptr.h | 10 +- src/tcachesharedmemorystore.cpp | 8 +- src/tdatabasecontext.cpp | 4 +- src/tdatabasecontext.h | 2 +- src/thazardobject.h | 3 +- src/thazardptr.cpp | 2 +- src/tkvsdatabase.cpp | 4 +- src/tkvsdatabasepool.cpp | 270 ++----------------------- src/tkvsdatabasepool.h | 30 +-- src/tmemcached.cpp | 12 +- src/tmemcached.h | 2 +- src/tmongoquery.cpp | 53 ++--- src/tmongoquery.h | 2 +- src/tqueue.h | 8 +- src/tredis.cpp | 12 +- src/tredis.h | 2 +- src/tsharedmemorykvs.cpp | 12 +- src/tsharedmemorykvs.h | 2 +- src/tsqldatabasepool.cpp | 144 ++----------- src/tsqldatabasepool.h | 28 +-- src/tstack.h | 85 +++++--- src/tthreadapplicationserver.cpp | 4 +- src/tthreadapplicationserver.h | 2 +- src/tthreadapplicationserver_linux.cpp | 7 +- src/tthreadapplicationserver_qt.cpp | 4 +- 27 files changed, 186 insertions(+), 537 deletions(-) diff --git a/include/headers.pri b/include/headers.pri index c2ecd1982..ca1142475 100644 --- a/include/headers.pri +++ b/include/headers.pri @@ -100,6 +100,7 @@ HEADER_CLASSES += ../include/TMemcached HEADER_CLASSES += ../include/TMemcachedDriver HEADER_CLASSES += ../include/TStdoutSystemLogger HEADER_CLASSES += ../include/TStdErrSystemLogger +HEADER_CLASSES += ../include/TStack HEADER_FILES = tabstractactioncontext.h HEADER_FILES += tabstractmodel.h @@ -204,6 +205,7 @@ HEADER_FILES += tmemcacheddriver.h HEADER_FILES += tsystemlogger.h HEADER_FILES += tstdoutsystemlogger.h HEADER_FILES += tstderrsystemlogger.h +HEADER_FILES += tstack.h HEADER_FILES += turingserver.h HEADER_FILES += turingcoroutine.h 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/tdatabasecontext.cpp b/src/tdatabasecontext.cpp index f5d0df627..278cc986a 100644 --- a/src/tdatabasecontext.cpp +++ b/src/tdatabasecontext.cpp @@ -98,7 +98,7 @@ void TDatabaseContext::releaseSqlDatabases() } -TKvsDatabase &TDatabaseContext::getKvsDatabase(Tf::KvsEngine engine) +TKvsDatabase::Handle &TDatabaseContext::getKvsDatabase(Tf::KvsEngine engine) { auto &handle = kvsDatabases[(int)engine]; if (!handle || !handle->isValid()) { @@ -106,7 +106,7 @@ TKvsDatabase &TDatabaseContext::getKvsDatabase(Tf::KvsEngine engine) } idleElapsed = (uint)std::time(nullptr); - return *handle; + return handle; } diff --git a/src/tdatabasecontext.h b/src/tdatabasecontext.h index 4480e7cc9..79977fb41 100644 --- a/src/tdatabasecontext.h +++ b/src/tdatabasecontext.h @@ -18,7 +18,7 @@ class T_CORE_EXPORT TDatabaseContext { TDatabaseContext &operator=(TDatabaseContext &&) = default; TSqlDatabase &getSqlDatabase(int id = 0); - TKvsDatabase &getKvsDatabase(Tf::KvsEngine engine); + TKvsDatabase::Handle &getKvsDatabase(Tf::KvsEngine engine); void setTransactionEnabled(bool enable, int id = 0); void release(); 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/tkvsdatabase.cpp b/src/tkvsdatabase.cpp index 0af38d0f2..72c9878fd 100644 --- a/src/tkvsdatabase.cpp +++ b/src/tkvsdatabase.cpp @@ -35,7 +35,7 @@ class TKvsDatabaseDict : public QMap { static TKvsDatabaseDict *databaseDict() { - static std::unique_ptr dict { new TKvsDatabaseDict }; + static std::unique_ptr dict{new TKvsDatabaseDict{}}; return dict.get(); } @@ -318,8 +318,8 @@ TKvsDatabase &TKvsDatabase::operator=(TKvsDatabase &&other) _connectName = other._connectName; _driver = other._driver; other._driver = nullptr; - return *this; } + return *this; } diff --git a/src/tkvsdatabasepool.cpp b/src/tkvsdatabasepool.cpp index c872c9779..6b98906af 100644 --- a/src/tkvsdatabasepool.cpp +++ b/src/tkvsdatabasepool.cpp @@ -54,7 +54,7 @@ static KvsEngineHash *kvsEngineHash() TKvsDatabasePool *TKvsDatabasePool::instance() { static std::unique_ptr databasePool = []() { - std::unique_ptr pool { new TKvsDatabasePool }; + std::unique_ptr pool{new TKvsDatabasePool{}}; pool->maxConnects = Tf::app()->maxNumberOfThreadsPerAppServer(); pool->init(); return pool; @@ -71,35 +71,7 @@ TKvsDatabasePool::TKvsDatabasePool() : TKvsDatabasePool::~TKvsDatabasePool() { -#if 0 - QMutexLocker locker(&_mutex); - timer.stop(); - - for (int eng = 0; eng < (int)Tf::KvsEngine::Num; eng++) { - if (!Tf::app()->isKvsAvailable((Tf::KvsEngine)eng)) { - continue; - } - - auto &cache = cachedDatabase[eng]; - QString name; - while (!cache.isEmpty()) { - name = cache.pop(); - TKvsDatabase::database(name).close(); - TKvsDatabase::removeDatabase(name); - } - - auto &stack = availableNames[eng]; - while (!stack.isEmpty()) { - name = stack.pop(); - TKvsDatabase::removeDatabase(name); - } - } - - delete[] cachedDatabase; - delete[] lastCachedTime; - delete[] availableNames; -#else - QMutexLocker locker(&_mutex); + //QMutexLocker locker(&_mutex); timer.stop(); for (int eng = 0; eng < (int)Tf::KvsEngine::Num; eng++) { @@ -125,61 +97,16 @@ TKvsDatabasePool::~TKvsDatabasePool() } delete[] lastCachedTime; -#endif } void TKvsDatabasePool::init() { -#if 0 - if (cachedDatabase) { - return; - } - - QMutexLocker locker(&_mutex); - - cachedDatabase = new QStack[(int)Tf::KvsEngine::Num]; - lastCachedTime = new TAtomic[(int)Tf::KvsEngine::Num]; - availableNames = new QStack[(int)Tf::KvsEngine::Num]; - bool aval = false; - - // Adds databases previously - for (auto it = kvsEngineHash()->begin(); it != kvsEngineHash()->end(); ++it) { - Tf::KvsEngine engine = it.key(); - const QString &drv = it.value(); - - if (!Tf::app()->isKvsAvailable(engine)) { - tSystemDebug("KVS database not available. engine:{}", (int)engine); - continue; - } else { - aval = true; - tSystemDebug("KVS database available. engine:{}", (int)engine); - } - - auto &stack = availableNames[(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()) { - 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()); - } - } - - if (aval) { - // Starts the timer to close extra-connection - timer.start(10000, this); - } -#else if (lastCachedTime) { return; } - QMutexLocker locker(&_mutex); + //QMutexLocker locker(&_mutex); cachedDatabases.resize((int)Tf::KvsEngine::Num); availableDatabases.resize((int)Tf::KvsEngine::Num); @@ -219,91 +146,12 @@ void TKvsDatabasePool::init() // Starts the timer to close extra-connection timer.start(10000, this); } -#endif } TKvsDatabase::Handle TKvsDatabasePool::database(Tf::KvsEngine engine) { -#if 0 - QMutexLocker locker(&_mutex); - - if (!Tf::app()->isKvsAvailable(engine)) { - switch (engine) { - case Tf::KvsEngine::MongoDB: - tSystemError("MongoDB not available. Check the settings file."); - break; - - case Tf::KvsEngine::Redis: - tSystemError("Redis not available. Check the settings file."); - break; - - case Tf::KvsEngine::Memcached: - tSystemError("Memcached not available. Check the settings file."); - break; - - case Tf::KvsEngine::SharedMemory: - tSystemWarn("SharedMemory not available. Check the settings file."); - break; - - case Tf::KvsEngine::CacheKvs: - tSystemError("CacheKvs not available. Check the settings file."); - break; - - default: - throw RuntimeException("No such KVS engine", __FILE__, __LINE__); - break; - } - return TKvsDatabase(); - } - - auto &cache = cachedDatabase[(int)engine]; - auto &stack = availableNames[(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; - } else { - tSystemError("Pooled database is not open: {} [{}:{}]", db.connectionName(), __FILE__, __LINE__); - stack.push(name); - continue; - } - } - - if (Q_LIKELY(!stack.isEmpty())) { - name = stack.pop(); - auto db = TKvsDatabase::database(name); - db.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(); - } - - 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); - } - } - - return db; - } - } - - throw RuntimeException("No pooled connection", __FILE__, __LINE__); -#else - QMutexLocker locker(&_mutex); + //QMutexLocker locker(&_mutex); if (!Tf::app()->isKvsAvailable(engine)) { switch (engine) { @@ -337,23 +185,28 @@ TKvsDatabase::Handle TKvsDatabasePool::database(Tf::KvsEngine engine) auto &cache = cachedDatabases[(int)engine]; auto &stack = availableDatabases[(int)engine]; - for (;;) { + while (true) { QString name; if (!cache.empty()) { KvsDbPtr dbptr {cache.pop()}; - 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: {} [{}:{}]", dbptr->connectionName(), __FILE__, __LINE__); - stack.push(std::move(dbptr)); - continue; + 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: {} [{}:{}]", dbptr->connectionName(), __FILE__, __LINE__); + stack.push(std::move(dbptr)); + continue; + } } } if (!stack.empty()) { KvsDbPtr dbptr {stack.pop()}; + if (!dbptr) { + break; + } dbptr->moveToThread(QThread::currentThread()); // move to thread if (!dbptr->open()) { @@ -377,7 +230,6 @@ TKvsDatabase::Handle TKvsDatabasePool::database(Tf::KvsEngine engine) } throw RuntimeException("No pooled connection", __FILE__, __LINE__); -#endif } @@ -432,65 +284,9 @@ bool TKvsDatabasePool::setDatabaseSettings(TKvsDatabase &database, Tf::KvsEngine } -TKvsDatabaseSettings TKvsDatabasePool::getDatabaseSettings(Tf::KvsEngine engine) const -{ -#if 0 - QMutexLocker locker(&_mutex); - - TKvsDatabaseSettings settrings; - auto &stack = availableNames[(int)engine]; - - QString name; - for (;;) { - if (Q_LIKELY(!stack.isEmpty())) { - name = stack.pop(); - stack.push(name); - return TKvsDatabase::settings(name); - } - } - - throw RuntimeException("No pooled connection", __FILE__, __LINE__); -#else - QMutexLocker locker(&_mutex); - - TKvsDatabaseSettings settrings; - auto &stack = availableDatabases[(int)engine]; - - if (!stack.empty()) { - auto &db = stack.top(); - QString name = db->connectionName(); - return TKvsDatabase::settings(name); - } - - throw RuntimeException("No pooled connection", __FILE__, __LINE__); -#endif -} - - void TKvsDatabasePool::pool(KvsDbPtr dbptr) { -#if 0 - QMutexLocker locker(&_mutex); - - if (Q_LIKELY(database.isValid())) { - bool ok; - int engine = database.connectionName().left(2).toInt(&ok); - if (Q_UNLIKELY(!ok)) { - throw RuntimeException("No such KVS engine", __FILE__, __LINE__); - } - - if (database.isOpen()) { - cachedDatabase[engine].push(database.connectionName()); - 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()); - } - } - database = TKvsDatabase(); // Sets an invalid object -#else - QMutexLocker locker(&_mutex); + //QMutexLocker locker(&_mutex); if (dbptr->isValid()) { bool ok; @@ -508,38 +304,13 @@ void TKvsDatabasePool::pool(KvsDbPtr dbptr) availableDatabases[engine].push(std::move(dbptr)); } } -#endif } void TKvsDatabasePool::timerEvent(QTimerEvent *event) { -#if 0 - if (event->timerId() == timer.timerId()) { - QMutexLocker locker(&_mutex); - QString name; - - // Closes extra-connection - for (int e = 0; e < kvsEngineHash()->count(); e++) { - if (!Tf::app()->isKvsAvailable((Tf::KvsEngine)e)) { - continue; - } - - auto &cache = cachedDatabase[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); - } - } - } else { - QObject::timerEvent(event); - } -#else if (event->timerId() == timer.timerId()) { - QMutexLocker locker(&_mutex); + //QMutexLocker locker(&_mutex); QString name; // Closes extra-connection @@ -560,7 +331,6 @@ void TKvsDatabasePool::timerEvent(QTimerEvent *event) } else { QObject::timerEvent(event); } -#endif } diff --git a/src/tkvsdatabasepool.h b/src/tkvsdatabasepool.h index 73cf0e241..adcb17803 100644 --- a/src/tkvsdatabasepool.h +++ b/src/tkvsdatabasepool.h @@ -1,6 +1,7 @@ #pragma once #include #include +#include #include #include #include @@ -16,29 +17,8 @@ class T_CORE_EXPORT TKvsDatabasePool : public QObject { public: using KvsDbPtr = std::unique_ptr; - template - class MoveStack : public std::deque { - public: - MoveStack() = default; - MoveStack(const MoveStack &) = delete; - MoveStack &operator=(const MoveStack &) = delete; - MoveStack(MoveStack &&) noexcept = default; - MoveStack &operator=(MoveStack &&) noexcept = default; - void push(T value) { std::deque::push_back(std::move(value)); } - T pop() - { - T v = std::move(std::deque::back()); - std::deque::pop_back(); - return v; - } - T &top() { return std::deque::back(); } - const T &top() const { return std::deque::back(); } - }; - ~TKvsDatabasePool(); TKvsDatabase::Handle database(Tf::KvsEngine engine); - TKvsDatabaseSettings getDatabaseSettings(Tf::KvsEngine engine) const; - static TKvsDatabasePool *instance(); protected: @@ -52,12 +32,14 @@ class T_CORE_EXPORT TKvsDatabasePool : public QObject { TKvsDatabasePool(); void pool(KvsDbPtr dbptr); - mutable QRecursiveMutex _mutex; + //mutable QRecursiveMutex _mutex; TAtomic *lastCachedTime {nullptr}; int maxConnects {0}; QBasicTimer timer; - std::vector> availableDatabases; - std::vector> cachedDatabases; + //std::vector> availableDatabases; + //std::vector> cachedDatabases; + std::vector> availableDatabases; + std::vector> cachedDatabases; T_DISABLE_COPY(TKvsDatabasePool) T_DISABLE_MOVE(TKvsDatabasePool) 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 7697bc2b1..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 a865975c9..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(Tf::KvsEngine engine, const QString &collection) : */ 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 4fe6c43cc..0c1c75e10 100644 --- a/src/tmongoquery.h +++ b/src/tmongoquery.h @@ -44,7 +44,7 @@ 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}; diff --git a/src/tqueue.h b/src/tqueue.h index dbf17df41..6cca6f58a 100644 --- a/src/tqueue.h +++ b/src/tqueue.h @@ -17,8 +17,11 @@ class TQueue { 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}; @@ -128,4 +131,3 @@ inline bool TQueue::head(T &val) Tf::hazardPtrForQueue().clear(); return (bool)next; } - 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 5dde9e2e1..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/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 b744ae1d1..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/tsqldatabasepool.cpp b/src/tsqldatabasepool.cpp index 9092b8da6..81012fb08 100644 --- a/src/tsqldatabasepool.cpp +++ b/src/tsqldatabasepool.cpp @@ -43,7 +43,7 @@ TSqlDatabasePool::TSqlDatabasePool() : TSqlDatabasePool::~TSqlDatabasePool() { - QMutexLocker locker(&_mutex); + //QMutexLocker locker(&_mutex); timer.stop(); for (int j = 0; j < Tf::app()->sqlDatabaseSettingsCount(); ++j) { @@ -87,36 +87,7 @@ void TSqlDatabasePool::init() return; } - QMutexLocker locker(&_mutex); -#if 0 - cachedDatabase = new QStack[Tf::app()->sqlDatabaseSettingsCount()]; - lastCachedTime = new TAtomic[Tf::app()->sqlDatabaseSettingsCount()]; - availableNames = new QStack[Tf::app()->sqlDatabaseSettingsCount()]; - bool aval = false; - tSystemDebug("SQL database available. maxConnects:{}", maxConnects); - - // Adds databases previously - for (int j = 0; j < Tf::app()->sqlDatabaseSettingsCount(); ++j) { - QString type = driverType(j); - if (type.isEmpty()) { - continue; - } - aval = true; - - auto &stack = availableNames[j]; - for (int i = 0; i < maxConnects; ++i) { - TSqlDatabase &db = TSqlDatabase::addDatabase(type, QString::asprintf(CONN_NAME_FORMAT, j, i)); - 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()); - } - } -#endif + //QMutexLocker locker(&_mutex); if (lastCachedTime) { return; @@ -163,62 +134,10 @@ void TSqlDatabasePool::init() } } -/* -QSqlDatabase TSqlDatabasePool::database(int databaseId) -{ - QMutexLocker locker(&_mutex); - - if (databaseId >= 0 && databaseId < Tf::app()->sqlDatabaseSettingsCount()) { - auto &cache = cachedDatabase[databaseId]; - auto &stack = availableNames[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; - } - } - - 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(); - } - - tSystemDebug("SQL database opened successfully (env:{})", Tf::app()->databaseEnvironment()); - tSystemDebug("Gets database: {}", tdb.sqlDatabase().connectionName()); - - // 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(); - } - } - } - throw RuntimeException("No pooled connection", __FILE__, __LINE__); -} -*/ TSqlDatabase::Handle TSqlDatabasePool::database(int databaseId) { - QMutexLocker locker(&_mutex); + //QMutexLocker locker(&_mutex); if (databaseId >= 0 && databaseId < Tf::app()->sqlDatabaseSettingsCount()) { auto &cache = cachedDatabases[databaseId]; @@ -228,18 +147,24 @@ TSqlDatabase::Handle TSqlDatabasePool::database(int databaseId) QString name; if (!cache.empty()) { SqlDbPtr sqlptr {cache.pop()}; - 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 (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 (!stack.empty()) { SqlDbPtr sqlptr {stack.pop()}; + if (!sqlptr) { + break; + } + 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()); @@ -334,41 +259,10 @@ bool TSqlDatabasePool::setDatabaseSettings(TSqlDatabase &database, int databaseI return true; } -/* -void TSqlDatabasePool::pool(QSqlDatabase &database, bool forceClose) -{ - QMutexLocker locker(&_mutex); - - if (database.isValid()) { - int databaseId = getDatabaseId(database); - - if (databaseId >= 0 && databaseId < Tf::app()->sqlDatabaseSettingsCount()) { - if (forceClose) { - tSystemWarn("Force close database: {}", database.connectionName()); - TSqlDatabase &db = TSqlDatabase::database(database.connectionName()); - closeDatabase(db); - } else { - if (database.isOpen()) { - // pool - cachedDatabase[databaseId].push(database.connectionName()); - 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()); - } - } - } else { - tSystemError("Pooled invalid database [{}:{}]", __FILE__, __LINE__); - } - } - database = QSqlDatabase(); // Sets an invalid object -} -*/ void TSqlDatabasePool::pool(SqlDbPtr dbptr, bool forceClose) { - QMutexLocker locker(&_mutex); + //QMutexLocker locker(&_mutex); if (dbptr->isValid()) { int databaseId = getDatabaseId(dbptr->sqlDatabase()); @@ -399,7 +293,7 @@ void TSqlDatabasePool::pool(SqlDbPtr dbptr, bool forceClose) void TSqlDatabasePool::timerEvent(QTimerEvent *event) { if (event->timerId() == timer.timerId()) { - QMutexLocker locker(&_mutex); + //QMutexLocker locker(&_mutex); QString name; #if 0 @@ -459,7 +353,7 @@ bool TSqlDatabasePool::openDatabase(TSqlDatabase &database) void TSqlDatabasePool::closeDatabase(TSqlDatabase &database) { - QMutexLocker locker(&_mutex); + //QMutexLocker locker(&_mutex); QSqlDatabase db = database.sqlDatabase(); QString name = db.connectionName(); diff --git a/src/tsqldatabasepool.h b/src/tsqldatabasepool.h index dd376c0ef..052f0e78c 100644 --- a/src/tsqldatabasepool.h +++ b/src/tsqldatabasepool.h @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -18,25 +19,6 @@ class T_CORE_EXPORT TSqlDatabasePool : public QObject { public: using SqlDbPtr = std::unique_ptr; - template - class MoveStack : public std::deque { - public: - MoveStack() = default; - MoveStack(const MoveStack &) = delete; - MoveStack &operator=(const MoveStack &) = delete; - MoveStack(MoveStack &&) noexcept = default; - MoveStack &operator=(MoveStack &&) noexcept = default; - void push(T value) { std::deque::push_back(std::move(value)); } - T pop() - { - T v = std::move(std::deque::back()); - std::deque::pop_back(); - return v; - } - T &top() { return std::deque::back(); } - const T &top() const { return std::deque::back(); } - }; - ~TSqlDatabasePool(); TSqlDatabase::Handle database(int databaseId = 0); @@ -55,12 +37,14 @@ class T_CORE_EXPORT TSqlDatabasePool : public QObject { void pool(SqlDbPtr dbptr, bool forceClose = false); TSqlDatabasePool(); - mutable QRecursiveMutex _mutex; + //mutable QRecursiveMutex _mutex; TAtomic *lastCachedTime {nullptr}; int maxConnects {0}; QBasicTimer timer; - std::vector> availableDatabases; - std::vector> cachedDatabases; + // std::vector> availableDatabases; + // std::vector> cachedDatabases; + std::vector> availableDatabases; + std::vector> cachedDatabases; T_DISABLE_COPY(TSqlDatabasePool) T_DISABLE_MOVE(TSqlDatabasePool) diff --git a/src/tstack.h b/src/tstack.h index 7d2b30474..024678fc8 100644 --- a/src/tstack.h +++ b/src/tstack.h @@ -4,6 +4,7 @@ #include "thazardobject.h" #include "thazardptr.h" #include "tdeclexport.h" +#include namespace Tf { @@ -11,35 +12,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); + T pop(bool *ok = nullptr); 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 +{ + stkHead = other.stkHead; + counter.store(other.counter.load()); +} + + +template +inline void TStack::push(T val) { - auto *pnode = new Node(val); + auto *pnode = new Node(std::move(val)); do { pnode->next = stkHead.load(); } while (!stkHead.compareExchange(pnode->next, pnode)); @@ -48,8 +67,9 @@ inline void TStack::push(const T &val) template -inline bool TStack::pop(T &val) +inline T TStack::pop(bool *ok) { + T val; Node *pnode; while ((pnode = Tf::hazardPtrForStack().guard(&stkHead))) { if (stkHead.compareExchange(pnode, pnode->next)) { @@ -59,25 +79,40 @@ inline bool TStack::pop(T &val) if (pnode) { counter--; - val = pnode->value; + val = std::move(pnode->value); pnode->next = nullptr; pnode->deleteLater(); + + if (ok) { + *ok = true; + } + } else { + if (ok) { + *ok = false; + } } 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; +class TMoveStack : public std::deque { +public: + TMoveStack() = default; + TMoveStack(const TMoveStack &) = delete; + TMoveStack &operator=(const TMoveStack &) = delete; + TMoveStack(TMoveStack &&) noexcept = default; + TMoveStack &operator=(TMoveStack &&) noexcept = default; + void push(T value) { std::deque::push_back(std::move(value)); } + T pop() + { + T v = std::move(std::deque::back()); + std::deque::pop_back(); + return v; } - Tf::hazardPtrForStack().clear(); - return (bool)pnode; -} - + T &top() { return std::deque::back(); } + const T &top() const { return std::deque::back(); } +}; +*/ diff --git a/src/tthreadapplicationserver.cpp b/src/tthreadapplicationserver.cpp index 7602fb2d5..bd9924ac4 100644 --- a/src/tthreadapplicationserver.cpp +++ b/src/tthreadapplicationserver.cpp @@ -17,10 +17,10 @@ an web application server for thread. */ -TStack *TThreadApplicationServer::threadPoolPtr() +TStack &TThreadApplicationServer::threadPoolPtr() { static TStack threadPool; - return &threadPool; + return threadPool; } diff --git a/src/tthreadapplicationserver.h b/src/tthreadapplicationserver.h index baea8bd1d..69f30e430 100644 --- a/src/tthreadapplicationserver.h +++ b/src/tthreadapplicationserver.h @@ -52,7 +52,7 @@ class T_CORE_EXPORT TThreadApplicationServer : public QThread, public TApplicati void run() override; private: - static TStack *threadPoolPtr(); + static TStack &threadPoolPtr(); TThreadApplicationServer(int listeningSocket, QObject *parent = nullptr); int listenSocket {0}; diff --git a/src/tthreadapplicationserver_linux.cpp b/src/tthreadapplicationserver_linux.cpp index 2413ff95c..fe5176f9c 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); } } @@ -109,8 +109,9 @@ void TThreadApplicationServer::run() if (socketDescriptor > 0) { tSystemDebug("incomingConnection sd:{} thread count:{} max:{}", socketDescriptor, TActionThread::threadCount(), maxThreads); TActionThread *thread; + bool ok; - while (!threadPoolPtr()->pop(thread)) { + while (!((thread = threadPoolPtr().pop(&ok)) && ok)) { std::this_thread::yield(); Tf::msleep(1); } 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); From 2648b99b7b282f29526e4be4cdc8aebff29d2709 Mon Sep 17 00:00:00 2001 From: aoyama Date: Mon, 24 Nov 2025 19:39:15 +0900 Subject: [PATCH 11/16] temp --- src/tglobal.cpp | 37 ++++--- src/thttpheader.cpp | 94 ++++++++-------- src/thttpheader.h | 30 +++-- src/thttprequest.cpp | 200 ++++++++++++++++++++++------------ src/thttprequest.h | 44 +++++--- src/thttputility.cpp | 19 ++++ src/tinternetmessageheader.h | 11 +- src/turingcoroutine_linux.cpp | 9 +- src/turingserver_linux.cpp | 31 ------ 9 files changed, 271 insertions(+), 204 deletions(-) diff --git a/src/tglobal.cpp b/src/tglobal.cpp index b797e2cac..eacccfd3d 100644 --- a/src/tglobal.cpp +++ b/src/tglobal.cpp @@ -158,44 +158,47 @@ TDatabaseContext *Tf::currentDatabaseContext() { TDatabaseContext *context; - if (Tf::app()->multiProcessingModule() == TWebApplication::MultiProcessingModule::Epoll) { -#ifdef Q_OS_LINUX - context = TMultiplexingServer::instance()->currentWorker(); + switch (Tf::app()->multiProcessingModule()) { + case TWebApplication::MultiProcessingModule::Thread: + context = TDatabaseContext::currentDatabaseContext(); if (context) { return context; } - context = Tf::app()->mainDatabaseContext(); + context = dynamic_cast(QThread::currentThread()); if (context) { return context; } -#else - tFatal("Unsupported MPM: epoll"); -#endif - } + break; - if (Tf::app()->multiProcessingModule() == TWebApplication::MultiProcessingModule::Uring) { + case TWebApplication::MultiProcessingModule::Epoll: #ifdef Q_OS_LINUX - context = TUringServer::instance()->currentContext(); + context = TMultiplexingServer::instance()->currentWorker(); if (context) { return context; } +#else + tFatal("Unsupported MPM: epoll"); +#endif + break; - context = Tf::app()->mainDatabaseContext(); + case TWebApplication::MultiProcessingModule::Uring: +#ifdef Q_OS_LINUX + context = TUringServer::instance()->currentContext(); if (context) { return context; } #else - tFatal("Unsupported MPM: epoll"); + tFatal("Unsupported MPM: uring"); #endif - } + break; - context = TDatabaseContext::currentDatabaseContext(); - if (context) { - return context; + default: + tFatal("Unsupported MPM"); + break; } - context = dynamic_cast(QThread::currentThread()); + context = Tf::app()->mainDatabaseContext(); if (context) { return context; } diff --git a/src/thttpheader.cpp b/src/thttpheader.cpp index 10d4f2943..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. @@ -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 1cfc5bfb1..77fc6d658 100644 --- a/src/thttpheader.h +++ b/src/thttpheader.h @@ -6,11 +6,13 @@ 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 +25,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; } @@ -44,14 +48,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; 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 76c297884..1f1a949c6 100644 --- a/src/thttprequest.cpp +++ b/src/thttprequest.cpp @@ -42,16 +42,16 @@ static bool httpMethodOverride() \brief The THttpRequestData class is for shared THttpRequest data objects. */ -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) -{ -} +// 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,8 +64,7 @@ THttpRequestData::THttpRequestData(const THttpRequestData &other) : */ THttpRequest::THttpRequest() : d(std::make_unique()) -{ -} +{} /*! Constructor with the header \a header and the body \a body. @@ -75,8 +74,10 @@ THttpRequest::THttpRequest(const THttpRequestHeader &header, const QByteArray &b { 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); } /*! @@ -84,51 +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 @@ -225,7 +198,7 @@ bool THttpRequest::hasItem(const QString &name, const QListqueryItems); + return hasItem(name, queryItemList()); } /*! @@ -255,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()); } @@ -277,7 +250,7 @@ QStringList THttpRequest::allItemValues(const QString &name, const QListqueryItems); + return allItemValues(name, queryItemList()); } /*! @@ -300,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()); } /*! @@ -309,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()); } @@ -328,7 +301,7 @@ QVariantMap THttpRequest::itemMap(const QList> &items) */ QVariantMap THttpRequest::queryItems() const { - return itemMap(d->queryItems); + return itemMap(queryItemList()); } /*! @@ -344,7 +317,7 @@ QVariantMap THttpRequest::queryItems() const */ bool THttpRequest::hasFormItem(const QString &name) const { - return hasItem(name, d->formItems); + return hasItem(name, formItemList()); } /*! @@ -363,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()); } /*! @@ -373,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()); } /*! @@ -417,7 +390,7 @@ QVariantList THttpRequest::itemVariantList(const QString &key, const QListformItems); + return itemVariantList(key, formItemList()); } @@ -465,7 +438,7 @@ QVariantMap THttpRequest::itemMap(const QString &key, const QListformItems); + return itemMap(key, formItemList()); } /*! @@ -473,7 +446,7 @@ QVariantMap THttpRequest::formItems(const QString &key) const */ QVariantMap THttpRequest::formItems() const { - return itemMap(d->formItems); + return itemMap(formItemList()); } @@ -486,19 +459,19 @@ void THttpRequest::parseBody(const QByteArray &body, const THttpRequestHeader &h 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); } @@ -510,7 +483,7 @@ void THttpRequest::parseBody(const QByteArray &body, const THttpRequestHeader &h QString query = QString::fromLatin1(data.value(1)); if (!query.isEmpty()) { - d->queryItems = THttpRequest::fromQuery(query); + queryItemList() = THttpRequest::fromQuery(query); } break; } @@ -575,8 +548,8 @@ QList THttpRequest::cookies() const */ QVariantMap THttpRequest::allParameters() const { - auto params = d->queryItems; - params << d->formItems; + auto params = queryItemList(); + params << formItemList(); return itemMap(params); } @@ -661,11 +634,11 @@ QHostAddress THttpRequest::originatingClientAddress() const QIODevice *THttpRequest::rawBody() { if (!bodyDevice) { - if (!d->multipartFormData.bodyFile.isEmpty()) { - auto p = new QFile(d->multipartFormData.bodyFile); + if (d->multipartFormData && !multipartFormData().bodyFile.isEmpty()) { + auto p = new QFile(multipartFormData().bodyFile); bodyDevice.reset(p); } else { - auto p = new QBuffer(&d->bodyArray); + auto p = new QBuffer(d->bodyArray.get()); bodyDevice.reset(p); } } @@ -673,6 +646,93 @@ QIODevice *THttpRequest::rawBody() } +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); +} + + /*! \fn const THttpRequestHeader &THttpRequest::header() const Returns the HTTP header of the request. diff --git a/src/thttprequest.h b/src/thttprequest.h index 81e4e3b5a..d773e1f1d 100644 --- a/src/thttprequest.h +++ b/src/thttprequest.h @@ -16,20 +16,22 @@ 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) }; @@ -38,9 +40,11 @@ class T_CORE_EXPORT THttpRequest { THttpRequest(); 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(THttpRequest&&) = default; + THttpRequest(const THttpRequest &) = default; + THttpRequest &operator=(const THttpRequest &) = default; + THttpRequest(THttpRequest &&) = default; THttpRequest &operator=(THttpRequest &&) = default; + virtual ~THttpRequest() = default; const THttpRequestHeader &header() const { return d->header; } Tf::HttpMethod method() const; @@ -51,7 +55,7 @@ class T_CORE_EXPORT THttpRequest { QVariantMap allParameters() const; bool isEmpty() const { return d->header.isEmpty(); } - bool hasQuery() const { return !d->queryItems.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; @@ -60,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; @@ -69,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 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); @@ -98,7 +109,6 @@ class T_CORE_EXPORT THttpRequest { std::unique_ptr bodyDevice; friend class TMultipartFormData; - T_DISABLE_COPY(THttpRequest) }; Q_DECLARE_METATYPE(THttpRequest) diff --git a/src/thttputility.cpp b/src/thttputility.cpp index 884daaedf..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 @@ -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/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/turingcoroutine_linux.cpp b/src/turingcoroutine_linux.cpp index 79d5b2793..faf2a7a63 100644 --- a/src/turingcoroutine_linux.cpp +++ b/src/turingcoroutine_linux.cpp @@ -28,7 +28,6 @@ class AsyncRecv : public TAwaitBase { inline int await_resume() { - //tSystemDebug("await_resume : _len:{} _cqeflags:{} _cqeres:{}", _len, _cqeflags, _cqeres); return _cqeres; } @@ -49,7 +48,6 @@ class AsyncSend : public TAwaitBase { { _handle = handle; int res = TUringServer::instance()->addSendZc(_fd, _buf, _len, this); - // res = TUringServer::instance()->addSend(_fd, _buf, _len, this); if (res < 0) { tSystemError("addSend error: {}", strerror(errno)); @@ -131,7 +129,7 @@ class CurrentRoutineScope { TUringCoroutine::~TUringCoroutine() { - tSystemDebug("~TUringCoroutine: sd:{}", _sd); + //tSystemDebug("~TUringCoroutine: sd:{}", _sd); if (_sd > 0) { ::close(_sd); } @@ -148,7 +146,9 @@ Task TUringCoroutine::start() TUringServer::instance()->registerForGC(this); }); + THttpRequest request; int timeout = 5000; + while (timeout > 0) { //int res; int64_t lengthToRead = INT64_MAX; @@ -210,7 +210,7 @@ Task TUringCoroutine::start() } } - auto request = THttpRequest::generate(readBuffer, QHostAddress("localhost"), this); + request = std::move(THttpRequest::generate(readBuffer, QHostAddress("localhost"), this)); execute(request); int res = co_await AsyncSend(_sd, _response.data(), _response.length()); @@ -257,7 +257,6 @@ Task TUringCoroutine::start() break; } sent_len += res; - //tSystemDebug("### AsyncSend res:{} sent_len:{}", res, sent_len); } munmap(mapped, file_size); diff --git a/src/turingserver_linux.cpp b/src/turingserver_linux.cpp index d4f062b5f..3b16d763f 100644 --- a/src/turingserver_linux.cpp +++ b/src/turingserver_linux.cpp @@ -357,36 +357,6 @@ int TUringServer::addSendZc(int fd, const void* buf, size_t len, TAwaitBase* awa return io_uring_submit(&_ring); } -/* -int TUringServer::addSendFile(int sd, int fd, int offset, size_t slice_len, TAwaitBase* await) -{ - int pipefd[2]; - if (pipe2(pipefd, O_NONBLOCK | O_CLOEXEC) < 0) { - tSystemError("pipe2 error [{}:{}]", __FILE__, __LINE__); - return -1; - } - - // 1. splice: file -> pipe - io_uring_sqe *sqe = io_uring_get_sqe(&_ring); - io_uring_prep_splice(sqe, fd, offset, pipefd[1], -1, slice_len, 0); - if (await) { - await->clear(); - io_uring_sqe_set_data(sqe, await); - } - - sqe->flags |= IOSQE_IO_LINK; - // 2. splice: pipe -> sd - io_uring_sqe *sqe2 = io_uring_get_sqe(&_ring); - io_uring_prep_splice(sqe, pipefd[0], -1, sd, -1, slice_len, 0); - if (await) { - await->_sqecounter++; - io_uring_sqe_set_data(sqe2, 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); @@ -406,7 +376,6 @@ int TUringServer::addSendFile(int sd, int fd, int offset, size_t slice_len, TAwa // } - // // Prepare a event request // From e84ec2b88937f85742a41d0e839bbe05e7ddd10f Mon Sep 17 00:00:00 2001 From: aoyama Date: Sun, 7 Dec 2025 19:26:18 +0900 Subject: [PATCH 12/16] update --- include/TLockQueue | 1 + include/TLockStack | 1 + include/headers.pri | 4 + include/tlockqueue.h | 1 + include/tlockstack.h | 1 + src/corelib.pro | 6 + src/tactioncontext.cpp | 31 ++- src/tactioncontext.h | 2 + src/tactionworker.cpp | 2 +- src/tdatabasecontext.cpp | 11 +- src/tepoll.cpp | 21 +- src/test/container/container.pro | 4 + src/test/container/main.cpp | 346 +++++++++++++++++++++++++ src/test/queue/main.cpp | 6 +- src/test/test.pri | 4 +- src/tglobal.cpp | 30 +-- src/thazardptrmanager.cpp | 2 +- src/thttpheader.h | 1 + src/thttprequest.h | 12 +- src/tkvsdatabasepool.cpp | 84 +++--- src/tkvsdatabasepool.h | 10 +- src/tkvsdriver.cpp | 6 + src/tqueue.cpp | 9 +- src/tqueue.h | 43 +-- src/trash/tfileaiowriter_unix.cpp | 4 +- src/tsession.h | 29 ++- src/tsqldatabasepool.cpp | 108 ++++---- src/tsqldatabasepool.h | 11 +- src/tstack.cpp | 10 +- src/tstack.h | 40 +-- src/tthreadapplicationserver.cpp | 4 +- src/tthreadapplicationserver.h | 6 +- src/tthreadapplicationserver_linux.cpp | 11 +- src/turingcoroutine.h | 32 +-- src/turingcoroutine_linux.cpp | 85 ++++-- src/turingserver.h | 32 ++- src/turingserver_linux.cpp | 131 ++++++++-- src/twebapplication_win.cpp | 2 +- 38 files changed, 805 insertions(+), 338 deletions(-) create mode 100644 include/TLockQueue create mode 100644 include/TLockStack create mode 100644 include/tlockqueue.h create mode 100644 include/tlockstack.h create mode 100644 src/test/container/container.pro create mode 100644 src/test/container/main.cpp 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/headers.pri b/include/headers.pri index ca1142475..cd7b87683 100644 --- a/include/headers.pri +++ b/include/headers.pri @@ -101,6 +101,8 @@ 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 @@ -206,6 +208,8 @@ 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 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/src/corelib.pro b/src/corelib.pro index 7010e3d54..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 diff --git a/src/tactioncontext.cpp b/src/tactioncontext.cpp index 6d179b317..8fb39114f 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,14 +39,18 @@ action controllers. */ + TActionContext::TActionContext() : TDatabaseContext() -{ } +{ + tSystemDebug("TActionContext::TActionContext ptr:{}", (uint64_t)this); +} TActionContext::~TActionContext() { release(); + tSystemDebug("TActionContext::~TActionContext ptr:{}", (uint64_t)this); } @@ -117,6 +129,8 @@ void TActionContext::execute(THttpRequest &request) // Call controller method TDispatcher ctlrDispatcher(route.controller); _currController = ctlrDispatcher.object(); +tSystemDebug("##################### _currController:{} this:{}", (uint64_t)_currController, (uint64_t)this); +tSystemDebug("####################1 currentDatabaseContext:{} actioncontext:{}", (uint64_t)currentDatabaseContext(), (uint64_t)dynamic_cast(currentDatabaseContext())); if (_currController) { _currController->setActionName(route.action); _currController->setArguments(route.params); @@ -498,3 +512,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 e41a4ab15..00c4e1c00 100644 --- a/src/tactioncontext.h +++ b/src/tactioncontext.h @@ -33,6 +33,8 @@ 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); diff --git a/src/tactionworker.cpp b/src/tactionworker.cpp index 9397a9791..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)) { diff --git a/src/tdatabasecontext.cpp b/src/tdatabasecontext.cpp index 278cc986a..d3c9305cb 100644 --- a/src/tdatabasecontext.cpp +++ b/src/tdatabasecontext.cpp @@ -14,14 +14,13 @@ #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; +thread_local TDatabaseContext *databaseContextPtrTls = nullptr; } @@ -194,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/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/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..118e6aaee --- /dev/null +++ b/src/test/container/main.cpp @@ -0,0 +1,346 @@ +#include +#include "tstack.h" +#include "tlockstack.h" +#include "tlockqueue.h" +#include +#include +#include +#include +#include +#include + + +template +class FCQueue { +public: + enum class State { Empty, Pending, Applied }; + enum class Op { Push, Pop }; + + struct Request { + std::atomic state {FCQueue::State::Empty}; + Op op {FCQueue::Op::Push}; + std::optional value; + }; + + explicit FCQueue() + { + while (reqs_.size() < 128) { + reqs_.push_back(new Request{}); + } + } + + ~FCQueue() + { + for (auto p : reqs_) { + delete p; + } + } + + void push(T v) + { + 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); + + while (true) { + combine(); + + if (r->state.load(std::memory_order_acquire) == State::Applied) { + r->state.store(State::Empty, std::memory_order_release); + break; + } + + std::this_thread::yield(); + } + } + + std::optional pop() + { + 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: pop logic error"); + } + + std::optional val; + r->op = Op::Pop; + r->state.store(State::Pending, std::memory_order_release); + + while (true) { + combine(); + + if (r->state.load(std::memory_order_acquire) == State::Applied) { + val = std::move(r->value); + r->state.store(State::Empty, std::memory_order_release); + break; + } + + std::this_thread::yield(); + } + + return val; + } + +private: + size_t acquireSlot() + { + static thread_local size_t mySlot = SIZE_MAX; + if (mySlot != SIZE_MAX) 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); + return mySlot; + } + + void ensure_slot(size_t idx) + { + 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; + } + + int max = slotCounter_.load(std::memory_order_acquire); + + for (int i = 0; i < max; i++) { + auto &r = reqs_[i]; + State s = r->state.load(std::memory_order_acquire); + + if (s != State::Pending) + 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); + } + + combineLock_.unlock(); + } + +private: + std::mutex combineLock_; + std::deque 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() + { + stressTest, &TLockQueue::push, &TLockQueue::pop>(); + } + + void stressTest_TLockStack() + { + stressTest, &TLockStack::push, &TLockStack::pop>(); + } +}; + +//QTEST_MAIN(TestQueue) +//TF_TEST_MAIN(TestQueue) +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/tglobal.cpp b/src/tglobal.cpp index eacccfd3d..7e8a44f7d 100644 --- a/src/tglobal.cpp +++ b/src/tglobal.cpp @@ -22,6 +22,7 @@ #include #include #include "turingserver.h" +#include "tactioncontextroutine.h" #endif #ifdef Q_OS_WIN #define NOMINMAX @@ -138,13 +139,15 @@ TAbstractController *Tf::currentController() #endif break; - case TWebApplication::MultiProcessingModule::Uring: + case TWebApplication::MultiProcessingModule::Uring: { #ifdef Q_OS_LINUX - return TUringServer::instance()->currentController(); + if (auto context = TActionContext::currentActionContext(); context) { + return context->currentController(); + } #else tFatal("Unsupported MPM: uring"); #endif - break; + break; } default: break; @@ -156,25 +159,20 @@ TAbstractController *Tf::currentController() TDatabaseContext *Tf::currentDatabaseContext() { - TDatabaseContext *context; - switch (Tf::app()->multiProcessingModule()) { case TWebApplication::MultiProcessingModule::Thread: - context = TDatabaseContext::currentDatabaseContext(); - if (context) { + if (auto context = TDatabaseContext::currentDatabaseContext(); context) { return context; } - context = dynamic_cast(QThread::currentThread()); - if (context) { + if (auto context = dynamic_cast(QThread::currentThread()); context) { return context; } break; case TWebApplication::MultiProcessingModule::Epoll: #ifdef Q_OS_LINUX - context = TMultiplexingServer::instance()->currentWorker(); - if (context) { + if (auto context = TMultiplexingServer::instance()->currentWorker(); context) { return context; } #else @@ -184,8 +182,11 @@ TDatabaseContext *Tf::currentDatabaseContext() case TWebApplication::MultiProcessingModule::Uring: #ifdef Q_OS_LINUX - context = TUringServer::instance()->currentContext(); - if (context) { + if (auto context = dynamic_cast(TActionContext::currentActionContext()); context) { + return context; + } + + if (auto context = TDatabaseContext::currentDatabaseContext(); context) { return context; } #else @@ -198,8 +199,7 @@ TDatabaseContext *Tf::currentDatabaseContext() break; } - context = Tf::app()->mainDatabaseContext(); - if (context) { + if (auto context = Tf::app()->mainDatabaseContext(); context) { return context; } 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.h b/src/thttpheader.h index 77fc6d658..6aed90159 100644 --- a/src/thttpheader.h +++ b/src/thttpheader.h @@ -1,6 +1,7 @@ #pragma once #include #include +#include class T_CORE_EXPORT THttpHeader : public TInternetMessageHeader { diff --git a/src/thttprequest.h b/src/thttprequest.h index d773e1f1d..73a011ab6 100644 --- a/src/thttprequest.h +++ b/src/thttprequest.h @@ -1,4 +1,8 @@ #pragma once +#include +#include +#include +#include #include #include #include @@ -6,10 +10,6 @@ #include #include #include -#include -#include -#include -#include #include class TActionContext; @@ -40,8 +40,8 @@ class T_CORE_EXPORT THttpRequest { THttpRequest(); THttpRequest(const THttpRequestHeader &header, const QByteArray &body, const QHostAddress &clientAddress, TActionContext *context); THttpRequest(const QByteArray &header, const QString &filePath, const QHostAddress &clientAddress, TActionContext *context); - THttpRequest(const THttpRequest &) = default; - THttpRequest &operator=(const THttpRequest &) = default; + THttpRequest(const THttpRequest &) = delete; + THttpRequest &operator=(const THttpRequest &) = delete; THttpRequest(THttpRequest &&) = default; THttpRequest &operator=(THttpRequest &&) = default; virtual ~THttpRequest() = default; diff --git a/src/tkvsdatabasepool.cpp b/src/tkvsdatabasepool.cpp index 6b98906af..dc45b7a90 100644 --- a/src/tkvsdatabasepool.cpp +++ b/src/tkvsdatabasepool.cpp @@ -8,7 +8,6 @@ #include "tkvsdatabasepool.h" #include "tfnamespace.h" #include "tsystemglobal.h" -#include "tstack.h" #include #include #include @@ -83,16 +82,20 @@ TKvsDatabasePool::~TKvsDatabasePool() QString name; while (!cache.empty()) { auto db = cache.pop(); - name = db->connectionName(); - db->close(); - TKvsDatabase::removeDatabase(name); + if (db) { + name = (*db)->connectionName(); + (*db)->close(); + TKvsDatabase::removeDatabase(name); + } } auto &stack = availableDatabases[eng]; while (!stack.empty()) { auto db = stack.pop(); - name = db->connectionName(); - TKvsDatabase::removeDatabase(name); + if (db) { + name = (*db)->connectionName(); + TKvsDatabase::removeDatabase(name); + } } } @@ -151,8 +154,6 @@ void TKvsDatabasePool::init() TKvsDatabase::Handle TKvsDatabasePool::database(Tf::KvsEngine engine) { - //QMutexLocker locker(&_mutex); - if (!Tf::app()->isKvsAvailable(engine)) { switch (engine) { case Tf::KvsEngine::MongoDB: @@ -186,47 +187,42 @@ TKvsDatabase::Handle TKvsDatabasePool::database(Tf::KvsEngine engine) auto &stack = availableDatabases[(int)engine]; while (true) { - QString name; - if (!cache.empty()) { - KvsDbPtr 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: {} [{}:{}]", dbptr->connectionName(), __FILE__, __LINE__); - stack.push(std::move(dbptr)); - continue; - } + 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: {} [{}:{}]", (*dbptr)->connectionName(), __FILE__, __LINE__); + stack.push(std::move(*dbptr)); + continue; } } - if (!stack.empty()) { - KvsDbPtr dbptr {stack.pop()}; - if (!dbptr) { - break; - } - dbptr->moveToThread(QThread::currentThread()); // move to thread + dbptr = stack.pop(); + if (!dbptr) { + break; + } + (*dbptr)->moveToThread(QThread::currentThread()); // move to thread - 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(); - } + 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(), dbptr->connectionName(), dbptr->databaseName()); - tSystemDebug("Gets KVS database: {}", dbptr->connectionName()); + tSystemDebug("KVS opened successfully env:{} connectname:{} dbname:{}", Tf::app()->databaseEnvironment(), (*dbptr)->connectionName(), (*dbptr)->databaseName()); + tSystemDebug("Gets KVS database: {}", (*dbptr)->connectionName()); - // Executes post-open statements - if (!dbptr->postOpenStatements().isEmpty()) { - for (QString st : dbptr->postOpenStatements()) { - st = st.trimmed(); - dbptr->command(st); - } + // 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)); } + return TKvsDatabase::Handle(std::move(*dbptr)); } throw RuntimeException("No pooled connection", __FILE__, __LINE__); @@ -323,9 +319,9 @@ void TKvsDatabasePool::timerEvent(QTimerEvent *event) while (lastCachedTime[e].load() < (uint)std::time(nullptr) - 30 && !cache.empty()) { auto db = cache.pop(); - db->close(); - tSystemDebug("Closed KVS database connection, name: {}", db->connectionName()); - availableDatabases[e].push(std::move(db)); + (*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 adcb17803..a89f17ff6 100644 --- a/src/tkvsdatabasepool.h +++ b/src/tkvsdatabasepool.h @@ -1,7 +1,7 @@ #pragma once #include #include -#include +#include #include #include #include @@ -36,10 +36,10 @@ class T_CORE_EXPORT TKvsDatabasePool : public QObject { TAtomic *lastCachedTime {nullptr}; int maxConnects {0}; QBasicTimer timer; - //std::vector> availableDatabases; - //std::vector> cachedDatabases; - std::vector> availableDatabases; - std::vector> cachedDatabases; + // std::vector> availableDatabases; + // std::vector> cachedDatabases; + std::vector> availableDatabases; + std::vector> cachedDatabases; T_DISABLE_COPY(TKvsDatabasePool) T_DISABLE_MOVE(TKvsDatabasePool) 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/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 6cca6f58a..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,6 +10,9 @@ T_CORE_EXPORT THazardPtr &hazardPtrForQueue(); } +// +// Non-blocking move queue +// template class TQueue { private: @@ -30,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) @@ -50,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(); @@ -76,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(); @@ -101,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; @@ -109,25 +114,5 @@ inline bool TQueue::dequeue(T &val) } } Tf::hazardPtrForQueue().clear(); - return (bool)next; -} - - -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; + return val; } 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/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/tsqldatabasepool.cpp b/src/tsqldatabasepool.cpp index 81012fb08..b228a6c5d 100644 --- a/src/tsqldatabasepool.cpp +++ b/src/tsqldatabasepool.cpp @@ -10,7 +10,6 @@ #include "tsqldriverextensionfactory.h" #include "tsqldriverextension.h" #include "tsystemglobal.h" -#include "tstack.h" #include #include #include @@ -43,23 +42,26 @@ TSqlDatabasePool::TSqlDatabasePool() : TSqlDatabasePool::~TSqlDatabasePool() { - //QMutexLocker locker(&_mutex); timer.stop(); for (int j = 0; j < Tf::app()->sqlDatabaseSettingsCount(); ++j) { auto &cache = cachedDatabases[j]; QString name; while (!cache.empty()) { - auto db = cache.pop(); - name = db->connectionName(); - closeDatabase(*db); - TSqlDatabase::removeDatabase(name); + std::optional db = cache.pop(); + if (db) { + name = (*db)->connectionName(); + closeDatabase(*(*db)); + TSqlDatabase::removeDatabase(name); + } } auto &stack = availableDatabases[j]; while (!stack.empty()) { - auto db = stack.pop(); - TSqlDatabase::removeDatabase(db->sqlDatabase().connectionName()); + std::optional db = stack.pop(); + if (db) { + TSqlDatabase::removeDatabase((*db)->sqlDatabase().connectionName()); + } } } @@ -137,60 +139,54 @@ void TSqlDatabasePool::init() TSqlDatabase::Handle TSqlDatabasePool::database(int databaseId) { - //QMutexLocker locker(&_mutex); + if (databaseId < 0 || databaseId >= Tf::app()->sqlDatabaseSettingsCount()) { + throw RuntimeException("No pooled connection", __FILE__, __LINE__); + } - if (databaseId >= 0 && databaseId < Tf::app()->sqlDatabaseSettingsCount()) { - auto &cache = cachedDatabases[databaseId]; - auto &stack = availableDatabases[databaseId]; - - while (true) { - QString name; - if (!cache.empty()) { - SqlDbPtr 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; - } - } + auto &cache = cachedDatabases[databaseId]; + auto &stack = availableDatabases[databaseId]; + + 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 (!stack.empty()) { - SqlDbPtr sqlptr {stack.pop()}; - if (!sqlptr) { - break; - } + sqlptr = stack.pop(); + if (!sqlptr) { + break; + } - 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(); - } + 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(); + } - tSystemDebug("SQL database opened successfully (env:{})", Tf::app()->databaseEnvironment()); - tSystemDebug("Gets database: {}", sqlptr->sqlDatabase().connectionName()); + 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)); + // Executes setup-queries + if (!(*sqlptr)->postOpenStatements().isEmpty()) { + TSqlQuery query((*sqlptr)->sqlDatabase()); + for (QString st : (*sqlptr)->postOpenStatements()) { + st = st.trimmed(); + query.exec(st); } - - tSystemError("Empty available databases [{}:{}]", __FILE__, __LINE__); - break; } + return TSqlDatabase::Handle(std::move(*sqlptr)); } - throw RuntimeException("No pooled connection", __FILE__, __LINE__); + + tSystemError("Empty available databases [{}:{}]", __FILE__, __LINE__); + return TSqlDatabase::Handle(); } @@ -322,8 +318,10 @@ void TSqlDatabasePool::timerEvent(QTimerEvent *event) while (lastCachedTime[i].load() < (uint)std::time(nullptr) - 30 && !cache.empty()) { auto db = cache.pop(); - closeDatabase(*db); - availableDatabases[i].push(std::move(db)); + if (db) { + closeDatabase(*(*db)); + availableDatabases[i].push(std::move(*db)); + } } } #endif diff --git a/src/tsqldatabasepool.h b/src/tsqldatabasepool.h index 052f0e78c..7eeab597f 100644 --- a/src/tsqldatabasepool.h +++ b/src/tsqldatabasepool.h @@ -2,7 +2,7 @@ #include #include #include -#include +#include #include #include #include @@ -37,14 +37,13 @@ class T_CORE_EXPORT TSqlDatabasePool : public QObject { void pool(SqlDbPtr dbptr, bool forceClose = false); TSqlDatabasePool(); - //mutable QRecursiveMutex _mutex; TAtomic *lastCachedTime {nullptr}; int maxConnects {0}; QBasicTimer timer; - // std::vector> availableDatabases; - // std::vector> cachedDatabases; - std::vector> availableDatabases; - std::vector> cachedDatabases; + // std::vector> availableDatabases; + // std::vector> cachedDatabases; + std::vector> availableDatabases; + std::vector> cachedDatabases; T_DISABLE_COPY(TSqlDatabasePool) T_DISABLE_MOVE(TSqlDatabasePool) 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 024678fc8..6fa448201 100644 --- a/src/tstack.h +++ b/src/tstack.h @@ -4,7 +4,6 @@ #include "thazardobject.h" #include "thazardptr.h" #include "tdeclexport.h" -#include namespace Tf { @@ -28,8 +27,8 @@ class TStack { Node(Node &&) = delete; Node &operator=(Node &&) = delete; }; - TAtomicPtr stkHead {nullptr}; + TAtomic counter {0}; public: @@ -40,7 +39,7 @@ class TStack { TStack &operator=(TStack &&) = delete; void push(T val); - T pop(bool *ok = nullptr); + std::optional pop(); int count() const { return counter.load(); } int size() const { return counter.load(); } bool empty() const { return counter.load() == 0; } @@ -67,10 +66,11 @@ inline void TStack::push(T val) template -inline T TStack::pop(bool *ok) +inline std::optional TStack::pop() { - T val; + std::optional val; Node *pnode; + while ((pnode = Tf::hazardPtrForStack().guard(&stkHead))) { if (stkHead.compareExchange(pnode, pnode->next)) { break; @@ -82,37 +82,7 @@ inline T TStack::pop(bool *ok) val = std::move(pnode->value); pnode->next = nullptr; pnode->deleteLater(); - - if (ok) { - *ok = true; - } - } else { - if (ok) { - *ok = false; - } } Tf::hazardPtrForStack().clear(); return val; } - - -/* -template -class TMoveStack : public std::deque { -public: - TMoveStack() = default; - TMoveStack(const TMoveStack &) = delete; - TMoveStack &operator=(const TMoveStack &) = delete; - TMoveStack(TMoveStack &&) noexcept = default; - TMoveStack &operator=(TMoveStack &&) noexcept = default; - void push(T value) { std::deque::push_back(std::move(value)); } - T pop() - { - T v = std::move(std::deque::back()); - std::deque::pop_back(); - return v; - } - T &top() { return std::deque::back(); } - const T &top() const { return std::deque::back(); } -}; -*/ diff --git a/src/tthreadapplicationserver.cpp b/src/tthreadapplicationserver.cpp index bd9924ac4..f3b567c7f 100644 --- a/src/tthreadapplicationserver.cpp +++ b/src/tthreadapplicationserver.cpp @@ -17,9 +17,9 @@ an web application server for thread. */ -TStack &TThreadApplicationServer::threadPoolPtr() +TLockStack &TThreadApplicationServer::threadPoolPtr() { - static TStack threadPool; + static TLockStack threadPool; return threadPool; } diff --git a/src/tthreadapplicationserver.h b/src/tthreadapplicationserver.h index 69f30e430..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}; @@ -52,7 +52,7 @@ 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}; diff --git a/src/tthreadapplicationserver_linux.cpp b/src/tthreadapplicationserver_linux.cpp index fe5176f9c..4296b180c 100644 --- a/src/tthreadapplicationserver_linux.cpp +++ b/src/tthreadapplicationserver_linux.cpp @@ -108,17 +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; - bool ok; + std::optional thread; - while (!((thread = threadPoolPtr().pop(&ok)) && ok)) { + 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/turingcoroutine.h b/src/turingcoroutine.h index eed25639d..ade751ba2 100644 --- a/src/turingcoroutine.h +++ b/src/turingcoroutine.h @@ -1,34 +1,28 @@ #pragma once -#include +//#include +//#include "tthreadpool.h" #include #include class TUringCoroutine; -struct Task; +class THttpRequest; +struct TUringTask; -/* -struct Task { - struct promise_type { - TUringCoroutine *self {nullptr}; - Task get_return_object() { return {}; } - std::suspend_never initial_suspend() noexcept { return {}; } - std::suspend_never final_suspend() noexcept { return {}; } - void return_void() noexcept {} - void unhandled_exception() { std::terminate(); } - }; -}; -*/ -class TUringCoroutine : public TActionContext { +//class TUringCoroutine : public TActionContext { +class TUringCoroutine { public: + // TUringCoroutine(int socketDescriptor) : + // TActionContext(), _sd(socketDescriptor) {} TUringCoroutine(int socketDescriptor) : - TActionContext(), _sd(socketDescriptor) {} + _sd(socketDescriptor) {} virtual ~TUringCoroutine(); - Task start(); + TUringTask start(); + //TAsyncTask executeRequest(THttpRequest &request); -protected: - virtual int64_t writeResponse(THttpResponseHeader &, QIODevice *) override; +// protected: +// virtual int64_t writeResponse(THttpResponseHeader &, QIODevice *) override; private: int _sd {0}; diff --git a/src/turingcoroutine_linux.cpp b/src/turingcoroutine_linux.cpp index faf2a7a63..239080236 100644 --- a/src/turingcoroutine_linux.cpp +++ b/src/turingcoroutine_linux.cpp @@ -1,5 +1,7 @@ #include "turingcoroutine.h" #include "turingserver.h" +#include "tthreadpoolawaiter.h" +#include "tactioncontextroutine.h" #include "TSystemGlobal" #include "TAppSettings" #include "THttpRequest" @@ -18,7 +20,7 @@ class AsyncRecv : public TAwaitBase { AsyncRecv(int fd, void* buffer, size_t length, int msecs) : _fd(fd), _buf(buffer), _len(length), _msecs(msecs) { } - void await_suspend(std::coroutine_handle handle) + void await_suspend(std::coroutine_handle handle) { _handle = handle; if (TUringServer::instance()->addRecv(_fd, _buf, _len, _msecs, this) < 0) { @@ -44,7 +46,7 @@ class AsyncSend : public TAwaitBase { AsyncSend(int fd, const void *buf, size_t len) : _fd(fd), _buf(buf), _len(len) { } - void await_suspend(std::coroutine_handle handle) + void await_suspend(std::coroutine_handle handle) { _handle = handle; int res = TUringServer::instance()->addSendZc(_fd, _buf, _len, this); @@ -70,11 +72,11 @@ class AsyncSend : public TAwaitBase { template class AsyncFunction : public TAwaitBase { public: - explicit AsyncFunction(std::function f, TUringCoroutine *parent) : - TAwaitBase(parent), _func(std::move(f)) {} + explicit AsyncFunction(std::function f) : + _func(std::move(f)) {} ~AsyncFunction() { if (_fd > 0) ::close(_fd); } - void await_suspend(std::coroutine_handle handle) + void await_suspend(std::coroutine_handle handle) { _handle = handle; _fd = eventfd(0, (EFD_NONBLOCK | EFD_CLOEXEC)); @@ -113,18 +115,18 @@ class ScopeExitFunction { }; -class CurrentRoutineScope { -public: - CurrentRoutineScope(TUringCoroutine *&slot, TUringCoroutine *now) : - _slot(slot), _prev(slot) - { - _slot = now; - } - ~CurrentRoutineScope() { _slot = _prev; } -private: - TUringCoroutine *&_slot; - TUringCoroutine *_prev; -}; +// class CurrentRoutineScope { +// public: +// CurrentRoutineScope(TUringCoroutine *&slot, TUringCoroutine *now) : +// _slot(slot), _prev(slot) +// { +// _slot = now; +// } +// ~CurrentRoutineScope() { _slot = _prev; } +// private: +// TUringCoroutine *&_slot; +// TUringCoroutine *_prev; +// }; TUringCoroutine::~TUringCoroutine() @@ -136,7 +138,7 @@ TUringCoroutine::~TUringCoroutine() } -Task TUringCoroutine::start() +TUringTask TUringCoroutine::start() { static const int64_t systemLimitBodyBytes = Tf::appSettings()->value(Tf::LimitRequestBody).toLongLong() * 2; static int keepAlivetimeout = Tf::appSettings()->value(Tf::HttpKeepAliveTimeout).toInt(); @@ -146,7 +148,7 @@ Task TUringCoroutine::start() TUringServer::instance()->registerForGC(this); }); - THttpRequest request; + TActionContextRoutine routine; int timeout = 5000; while (timeout > 0) { @@ -210,12 +212,16 @@ Task TUringCoroutine::start() } } - request = std::move(THttpRequest::generate(readBuffer, QHostAddress("localhost"), this)); - execute(request); + auto result = co_await TThreadPoolAwaiter([&] { + routine.start(readBuffer); + return routine.result; + }); + _response = std::move(result.response); + _fileName = std::move(result.fileName); int res = co_await AsyncSend(_sd, _response.data(), _response.length()); if (res <= 0) { - tSystemError("Send error fd={} res={}\n", _sd, res); + tSystemError("Send error fd={} res={}", _sd, res); co_return; } @@ -271,7 +277,7 @@ Task TUringCoroutine::start() } } - +/* int64_t TUringCoroutine::writeResponse(THttpResponseHeader &header, QIODevice *body) { if (keepAliveTimeout() > 0) { @@ -281,9 +287,9 @@ int64_t TUringCoroutine::writeResponse(THttpResponseHeader &header, QIODevice *b _response = header.toByteArray(); if (body) { - if (auto *buf = dynamic_cast(body); buf) { + if (auto *buf = dynamic_cast(body); buf) { // dynamic_cast is faster for QBuffer _response += buf->buffer(); - } else if (auto *file = dynamic_cast(body); file) { + } else if (auto *file = qobject_cast(body); file) { _fileName = file->fileName(); } else { tSystemError("Invalid body [{}:{}]", __FILE__, __LINE__); @@ -291,3 +297,32 @@ int64_t TUringCoroutine::writeResponse(THttpResponseHeader &header, QIODevice *b } return 0; } +*/ + +// TAsyncTask TUringCoroutine::executeRequest(THttpRequest &request) +// { + // int res = co_await TThreadPool::instance().run([this](THttpRequest &req) { + // execute(req); + // return 0; + // }, request); + // co_return res; + + + // int x = 1; + // int res = co_await TThreadPool::instance().run([this](int &d) { + // d++; + // return 0; + // }, x); + // co_return res; + + + // tSystemInfo("#### foo0"); + // int x = co_await TThreadPool::instance().run([](int a) { + // tSystemInfo("#### foo1"); + // std::this_thread::sleep_for(std::chrono::milliseconds(100)); + // tSystemInfo("#### foo2 {}", a); + // return a * 2; + // }, 3); + // tSystemInfo("#### foo3 {}", x); + // co_return x; +//} diff --git a/src/turingserver.h b/src/turingserver.h index 241c5e253..21ef413c2 100644 --- a/src/turingserver.h +++ b/src/turingserver.h @@ -9,9 +9,11 @@ #include #include #include +#include #include #include #include +#include #include class QIODevice; @@ -23,22 +25,28 @@ class TActionController; class TUringCoroutine; -class Task { +//template +class TUringTask { public: class promise_type { public: - TUringCoroutine *self {nullptr}; - Task get_return_object() + //std::conditional_t, std::monostate, T> value; + std::exception_ptr eptr; + + //TUringCoroutine *self {nullptr}; + TUringTask get_return_object() { - return Task{ std::coroutine_handle::from_promise(*this) }; + 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() { std::terminate(); } + // void unhandled_exception() { std::terminate(); } + //void return_value(const T &v) noexcept { value = v; } + void unhandled_exception() { eptr = std::current_exception(); } }; - explicit Task(std::coroutine_handle h) : handle(h) {} + explicit TUringTask(std::coroutine_handle h) : handle(h) {} std::coroutine_handle handle; }; @@ -54,7 +62,7 @@ class TAwaitBase { _sqecounter = 1; } - std::coroutine_handle _handle{}; + std::coroutine_handle _handle{}; int _cqeres {0}; int _cqeflags {0}; int _sqecounter {1}; @@ -72,8 +80,8 @@ class T_CORE_EXPORT TUringServer : public TDatabaseContextThread, public TApplic void stop() override; void setAutoReloadingEnabled(bool enable) override; bool isAutoReloadingEnabled() override; - TActionContext *currentContext() const; - TActionController *currentController() const; + //TActionContext *currentContext() const; + //TActionController *currentController() const; void registerForGC(TUringCoroutine *); static TUringServer *instance(int listeningSocket = 0); @@ -82,20 +90,22 @@ class T_CORE_EXPORT TUringServer : public TDatabaseContextThread, public TApplic 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 addEvent(int sd, 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}; - TUringCoroutine *_currentCoroutine {nullptr}; + TLockQueue> _resumeHandlers; std::list _garbage; friend class TEpollSocket; T_DISABLE_COPY(TUringServer) T_DISABLE_MOVE(TUringServer) }; - diff --git a/src/turingserver_linux.cpp b/src/turingserver_linux.cpp index 3b16d763f..25d66405c 100644 --- a/src/turingserver_linux.cpp +++ b/src/turingserver_linux.cpp @@ -12,30 +12,30 @@ #include "tsystemglobal.h" #include "turlroute.h" #include "turingcoroutine.h" -#include +#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 int SPLICE_LEN = 1024 * 1024; +constexpr uint64_t UD_NOTIFY = 0xFF00000000000000ULL; TUringServer *TUringServer::instance(int listeningSocket) { - static std::unique_ptr instance; - static std::once_flag once; - - std::call_once(once, [&]() { + static std::unique_ptr instance = [&]() { if (listeningSocket <= 0) { throw StandardException("Invalid socket", __FILE__, __LINE__); } - instance = std::make_unique(listeningSocket); - }); + return std::make_unique(listeningSocket); + }(); return instance.get(); } @@ -81,12 +81,24 @@ TUringServer::TUringServer(int listeningSocket, QObject *parent) : TApplicationServerBase(), _listenSocket(listeningSocket) { - io_uring_queue_init(8192, &_ring, 0); + io_uring_queue_init(8192 * 2, &_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); } @@ -126,20 +138,30 @@ void TUringServer::run() instance()->addAccept(_listenSocket, &accepter); __kernel_timespec ts = { - .tv_sec = 2, // 2秒 + .tv_sec = 2, // 2 secs .tv_nsec = 0 }; // --- イベントループ --- while (!_stopped) { if (_garbage.size() > 0) { - // Garbage collection - for (auto it = _garbage.begin(); it != _garbage.end(); ++it) { - delete *it; + for (auto &ptr : _garbage) { + delete ptr; } _garbage.clear(); } + // Resume handlers + auto h = _resumeHandlers.pop(); + if (h) { + uint64_t tmp; + tf_read(_notifyFd, &tmp, sizeof(tmp)); + + do { + h->resume(); + } while ((h = _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); }); @@ -163,6 +185,13 @@ void TUringServer::run() void *user_data = io_uring_cqe_get_data(cqe); if (user_data) { + if (user_data == (void*)UD_NOTIFY) { + if (!(cqe->flags & IORING_CQE_F_MORE)) { + addNotifyEvent(); + } + continue; + } + auto *await = static_cast(user_data); if (await == &accepter) { // Accepts @@ -171,8 +200,12 @@ void TUringServer::run() int fd = cqe->res; setBufferOption(fd); auto *coro = new TUringCoroutine(fd); - Task task = coro->start(); +#if 0 + TUringTask task = coro->start(); task.handle.promise().self = coro; +#else + coro->start(); +#endif await->clear(); // clear } else { int err = -cqe->res; @@ -209,9 +242,9 @@ void TUringServer::run() } if (--await->_sqecounter == 0 && await->_handle && !await->_handle.done()) { - _currentCoroutine = await->_handle.promise().self; + //_currentCoroutine = await->_handle.promise().self; await->_handle.resume(); - _currentCoroutine = nullptr; + //_currentCoroutine = nullptr; } } } @@ -219,6 +252,24 @@ void TUringServer::run() } } +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() { @@ -242,17 +293,17 @@ bool TUringServer::isAutoReloadingEnabled() } -TActionContext *TUringServer::currentContext() const -{ - return _currentCoroutine; -} +// TActionContext *TUringServer::currentContext() const +// { +// return _currentCoroutine; +// } -TActionController *TUringServer::currentController() const -{ - auto *context = currentContext(); - return (context) ? context->currentController() : nullptr; -} +// TActionController *TUringServer::currentController() const +// { +// auto *context = currentContext(); +// return (context) ? context->currentController() : nullptr; +// } /* void TUringServer::timerEvent(QTimerEvent *event) @@ -382,6 +433,11 @@ int TUringServer::addSendZc(int fd, const void* buf, size_t len, TAwaitBase* awa 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(); @@ -391,6 +447,33 @@ int TUringServer::addEvent(int fd, TAwaitBase* await) const } +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__); + } +} + + +/* +int TUringServer::addResumeHandle(std::coroutine_handle handle) 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_nop(sqe); + // io_uring_sqe_set_data(sqe, reinterpret_cast(UD_NOP_FLAG | (uint64_t)handle.address())); + // return io_uring_submit(&_ring); +} +*/ + void TUringServer::registerForGC(TUringCoroutine *coroutine) { _garbage.push_back(coroutine); 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)) { From 91c1414ae82ed993d5e73b5ca257e7522b9d9085 Mon Sep 17 00:00:00 2001 From: aoyama Date: Mon, 8 Dec 2025 14:59:12 +0900 Subject: [PATCH 13/16] update --- src/tactioncontextroutine.cpp | 47 +++++++++++++++++++++++ src/tactioncontextroutine.h | 22 +++++++++++ src/tlockqueue.cpp | 1 + src/tlockqueue.h | 49 ++++++++++++++++++++++++ src/tlockstack.cpp | 1 + src/tlockstack.h | 58 ++++++++++++++++++++++++++++ src/tthreadpoolawaiter.cpp | 1 + src/tthreadpoolawaiter.h | 68 +++++++++++++++++++++++++++++++++ src/turingcoroutine.h | 15 +------- src/turingcoroutine_linux.cpp | 60 ++++++++++++++--------------- src/turingserver.h | 23 +++--------- src/turingserver_linux.cpp | 71 +++++------------------------------ 12 files changed, 293 insertions(+), 123 deletions(-) create mode 100644 src/tactioncontextroutine.cpp create mode 100644 src/tactioncontextroutine.h create mode 100644 src/tlockqueue.cpp create mode 100644 src/tlockqueue.h create mode 100644 src/tlockstack.cpp create mode 100644 src/tlockstack.h create mode 100644 src/tthreadpoolawaiter.cpp create mode 100644 src/tthreadpoolawaiter.h 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/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..8b527b165 --- /dev/null +++ b/src/tlockqueue.h @@ -0,0 +1,49 @@ +#pragma once +#include +#include +#include + + +template +class TLockQueue { +public: + 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::move(_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..86e4330e8 --- /dev/null +++ b/src/tlockstack.h @@ -0,0 +1,58 @@ +#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(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::move(_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/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..b24bf81ee --- /dev/null +++ b/src/tthreadpoolawaiter.h @@ -0,0 +1,68 @@ +#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() { if (_fd > 0) tf_close(_fd); } + + bool await_ready() const noexcept { return false; } + + bool await_suspend(std::coroutine_handle handle) + { + QThreadPool::globalInstance()->start([this, handle] { + try { + if constexpr (std::is_void_v) { + _func(); + } else { + _result = _func(); + } + } catch (...) { + _eptr = std::current_exception(); + } + + TUringServer::instance()->addResumeHandle(handle); + }); + return true; + } + + Result await_resume() + { + if (_eptr) { + std::rethrow_exception(_eptr); + } + + if constexpr (std::is_void_v) { + return std::monostate{}; + } + return std::move(_result); + } + +private: + FuncType _func; + Result _result; + std::exception_ptr _eptr; + int _fd {0}; +}; diff --git a/src/turingcoroutine.h b/src/turingcoroutine.h index ade751ba2..f1cae253a 100644 --- a/src/turingcoroutine.h +++ b/src/turingcoroutine.h @@ -1,28 +1,15 @@ #pragma once -//#include -//#include "tthreadpool.h" -#include -#include -class TUringCoroutine; -class THttpRequest; struct TUringTask; -//class TUringCoroutine : public TActionContext { class TUringCoroutine { public: - // TUringCoroutine(int socketDescriptor) : - // TActionContext(), _sd(socketDescriptor) {} - TUringCoroutine(int socketDescriptor) : + explicit TUringCoroutine(int socketDescriptor) : _sd(socketDescriptor) {} virtual ~TUringCoroutine(); TUringTask start(); - //TAsyncTask executeRequest(THttpRequest &request); - -// protected: -// virtual int64_t writeResponse(THttpResponseHeader &, QIODevice *) override; private: int _sd {0}; diff --git a/src/turingcoroutine_linux.cpp b/src/turingcoroutine_linux.cpp index 239080236..0412505f1 100644 --- a/src/turingcoroutine_linux.cpp +++ b/src/turingcoroutine_linux.cpp @@ -69,40 +69,40 @@ class AsyncSend : public TAwaitBase { }; -template -class AsyncFunction : public TAwaitBase { -public: - explicit AsyncFunction(std::function f) : - _func(std::move(f)) {} - ~AsyncFunction() { if (_fd > 0) ::close(_fd); } +// template +// class AsyncFunction : public TAwaitBase { +// public: +// explicit AsyncFunction(std::function f) : +// _func(std::move(f)) {} +// ~AsyncFunction() { if (_fd > 0) ::close(_fd); } - void await_suspend(std::coroutine_handle handle) - { - _handle = handle; - _fd = eventfd(0, (EFD_NONBLOCK | EFD_CLOEXEC)); - TUringServer::instance()->addEvent(_fd, this); - std::thread([this]() { - _result = _func(); - notifyResume(); - }).detach(); - } +// void await_suspend(std::coroutine_handle handle) +// { +// _handle = handle; +// _fd = eventfd(0, (EFD_NONBLOCK | EFD_CLOEXEC)); +// TUringServer::instance()->addEvent(_fd, this); +// std::thread([this]() { +// _result = _func(); +// notifyResume(); +// }).detach(); +// } - R await_resume() { return _result; } +// R await_resume() { return _result; } -protected: - void notifyResume() - { - if (_fd > 0) { - uint64_t one = 1; - write(_fd, &one, sizeof(one)); // 通知 - } - } +// protected: +// void notifyResume() +// { +// if (_fd > 0) { +// uint64_t one = 1; +// write(_fd, &one, sizeof(one)); // 通知 +// } +// } -private: - int _fd {0}; - std::function _func; - R _result {}; -}; +// private: +// int _fd {0}; +// std::function _func; +// R _result {}; +// }; template diff --git a/src/turingserver.h b/src/turingserver.h index 21ef413c2..1e57249ca 100644 --- a/src/turingserver.h +++ b/src/turingserver.h @@ -1,15 +1,9 @@ #pragma once -#include "tatomic.h" -#include -#include -#include -#include -#include -#include #include #include #include #include +#include #include #include #include @@ -25,15 +19,12 @@ class TActionController; class TUringCoroutine; -//template class TUringTask { public: class promise_type { public: - //std::conditional_t, std::monostate, T> value; - std::exception_ptr eptr; + std::exception_ptr exptr; - //TUringCoroutine *self {nullptr}; TUringTask get_return_object() { return TUringTask{std::coroutine_handle::from_promise(*this)}; @@ -41,9 +32,7 @@ class TUringTask { std::suspend_never initial_suspend() noexcept { return {}; } std::suspend_never final_suspend() noexcept { return {}; } void return_void() noexcept {} - // void unhandled_exception() { std::terminate(); } - //void return_value(const T &v) noexcept { value = v; } - void unhandled_exception() { eptr = std::current_exception(); } + void unhandled_exception() { exptr = std::current_exception(); } }; explicit TUringTask(std::coroutine_handle h) : handle(h) {} @@ -62,7 +51,7 @@ class TAwaitBase { _sqecounter = 1; } - std::coroutine_handle _handle{}; + std::coroutine_handle _handle {}; int _cqeres {0}; int _cqeflags {0}; int _sqecounter {1}; @@ -80,8 +69,6 @@ class T_CORE_EXPORT TUringServer : public TDatabaseContextThread, public TApplic void stop() override; void setAutoReloadingEnabled(bool enable) override; bool isAutoReloadingEnabled() override; - //TActionContext *currentContext() const; - //TActionController *currentController() const; void registerForGC(TUringCoroutine *); static TUringServer *instance(int listeningSocket = 0); @@ -103,7 +90,7 @@ class T_CORE_EXPORT TUringServer : public TDatabaseContextThread, public TApplic int _notifyFd {0}; bool _autoReload {false}; TLockQueue> _resumeHandlers; - std::list _garbage; + TLockStack _garbage; friend class TEpollSocket; T_DISABLE_COPY(TUringServer) diff --git a/src/turingserver_linux.cpp b/src/turingserver_linux.cpp index 25d66405c..6e0d51f95 100644 --- a/src/turingserver_linux.cpp +++ b/src/turingserver_linux.cpp @@ -24,7 +24,6 @@ constexpr int SEND_BUF_SIZE = 128 * 1024; constexpr int RECV_BUF_SIZE = 128 * 1024; -constexpr int SPLICE_LEN = 1024 * 1024; constexpr uint64_t UD_NOTIFY = 0xFF00000000000000ULL; @@ -142,24 +141,23 @@ void TUringServer::run() .tv_nsec = 0 }; - // --- イベントループ --- + std::optional gcco; + std::optional> cohandle; + while (!_stopped) { - if (_garbage.size() > 0) { - for (auto &ptr : _garbage) { - delete ptr; - } - _garbage.clear(); + while ((gcco = _garbage.pop())) { + delete *gcco; } // Resume handlers - auto h = _resumeHandlers.pop(); - if (h) { + cohandle = _resumeHandlers.pop(); + if (cohandle) { uint64_t tmp; tf_read(_notifyFd, &tmp, sizeof(tmp)); do { - h->resume(); - } while ((h = _resumeHandlers.pop())); + cohandle->resume(); + } while ((cohandle = _resumeHandlers.pop())); } io_uring_cqe *cqe = nullptr; @@ -200,12 +198,7 @@ void TUringServer::run() int fd = cqe->res; setBufferOption(fd); auto *coro = new TUringCoroutine(fd); -#if 0 - TUringTask task = coro->start(); - task.handle.promise().self = coro; -#else coro->start(); -#endif await->clear(); // clear } else { int err = -cqe->res; @@ -242,9 +235,7 @@ void TUringServer::run() } if (--await->_sqecounter == 0 && await->_handle && !await->_handle.done()) { - //_currentCoroutine = await->_handle.promise().self; await->_handle.resume(); - //_currentCoroutine = nullptr; } } } @@ -292,33 +283,6 @@ bool TUringServer::isAutoReloadingEnabled() return _autoReload; } - -// TActionContext *TUringServer::currentContext() const -// { -// return _currentCoroutine; -// } - - -// TActionController *TUringServer::currentController() const -// { -// auto *context = currentContext(); -// return (context) ? context->currentController() : nullptr; -// } - -/* -void TUringServer::timerEvent(QTimerEvent *event) -{ - if (event->timerId() != reloadTimer.timerId()) { - QThread::timerEvent(event); - } else { - if (newerLibraryExists()) { - tSystemInfo("Detect new library of application. Reloading the libraries."); - Tf::app()->exit(127); - } - } -} -*/ - // // Prepare a accept request // @@ -459,22 +423,7 @@ void TUringServer::addResumeHandle(std::coroutine_handle handle) 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_nop(sqe); - // io_uring_sqe_set_data(sqe, reinterpret_cast(UD_NOP_FLAG | (uint64_t)handle.address())); - // return io_uring_submit(&_ring); -} -*/ - void TUringServer::registerForGC(TUringCoroutine *coroutine) { - _garbage.push_back(coroutine); + _garbage.push(coroutine); } From fab3306b02409f5578660df9a25145c12062da9d Mon Sep 17 00:00:00 2001 From: aoyama Date: Mon, 8 Dec 2025 19:44:14 +0900 Subject: [PATCH 14/16] update --- src/tactioncontext.cpp | 2 -- src/tthreadpoolawaiter.h | 11 +++++------ src/turingcoroutine_linux.cpp | 2 +- src/turingserver.h | 2 +- src/turingserver_linux.cpp | 4 +++- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/tactioncontext.cpp b/src/tactioncontext.cpp index 8fb39114f..3a361e181 100644 --- a/src/tactioncontext.cpp +++ b/src/tactioncontext.cpp @@ -129,8 +129,6 @@ void TActionContext::execute(THttpRequest &request) // Call controller method TDispatcher ctlrDispatcher(route.controller); _currController = ctlrDispatcher.object(); -tSystemDebug("##################### _currController:{} this:{}", (uint64_t)_currController, (uint64_t)this); -tSystemDebug("####################1 currentDatabaseContext:{} actioncontext:{}", (uint64_t)currentDatabaseContext(), (uint64_t)dynamic_cast(currentDatabaseContext())); if (_currController) { _currController->setActionName(route.action); _currController->setArguments(route.params); diff --git a/src/tthreadpoolawaiter.h b/src/tthreadpoolawaiter.h index b24bf81ee..8f63249f1 100644 --- a/src/tthreadpoolawaiter.h +++ b/src/tthreadpoolawaiter.h @@ -26,12 +26,13 @@ class TThreadPoolAwaiter : public TAwaitBase { }); } - ~TThreadPoolAwaiter() { if (_fd > 0) tf_close(_fd); } + ~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) { @@ -40,7 +41,7 @@ class TThreadPoolAwaiter : public TAwaitBase { _result = _func(); } } catch (...) { - _eptr = std::current_exception(); + handle.promise().exptr = std::current_exception(); } TUringServer::instance()->addResumeHandle(handle); @@ -50,8 +51,8 @@ class TThreadPoolAwaiter : public TAwaitBase { Result await_resume() { - if (_eptr) { - std::rethrow_exception(_eptr); + if (_handle.promise().exptr) { + std::rethrow_exception(_handle.promise().exptr); } if constexpr (std::is_void_v) { @@ -63,6 +64,4 @@ class TThreadPoolAwaiter : public TAwaitBase { private: FuncType _func; Result _result; - std::exception_ptr _eptr; - int _fd {0}; }; diff --git a/src/turingcoroutine_linux.cpp b/src/turingcoroutine_linux.cpp index 0412505f1..7903fbdb8 100644 --- a/src/turingcoroutine_linux.cpp +++ b/src/turingcoroutine_linux.cpp @@ -133,7 +133,7 @@ TUringCoroutine::~TUringCoroutine() { //tSystemDebug("~TUringCoroutine: sd:{}", _sd); if (_sd > 0) { - ::close(_sd); + tf_close(_sd); } } diff --git a/src/turingserver.h b/src/turingserver.h index 1e57249ca..214ff4a03 100644 --- a/src/turingserver.h +++ b/src/turingserver.h @@ -36,7 +36,7 @@ class TUringTask { }; explicit TUringTask(std::coroutine_handle h) : handle(h) {} - std::coroutine_handle handle; + std::coroutine_handle handle {}; }; diff --git a/src/turingserver_linux.cpp b/src/turingserver_linux.cpp index 6e0d51f95..ef9da7c4c 100644 --- a/src/turingserver_linux.cpp +++ b/src/turingserver_linux.cpp @@ -156,7 +156,9 @@ void TUringServer::run() tf_read(_notifyFd, &tmp, sizeof(tmp)); do { - cohandle->resume(); + if (!cohandle->done()) { + cohandle->resume(); + } } while ((cohandle = _resumeHandlers.pop())); } From 48dc75acfacc4ce7b26d73d9689627b336c63115 Mon Sep 17 00:00:00 2001 From: aoyama Date: Mon, 22 Dec 2025 21:54:09 +0900 Subject: [PATCH 15/16] updated --- src/tabstractwebsocket.cpp | 20 +-- src/tepollwebsocket.cpp | 10 +- src/test/container/main.cpp | 189 +++++++++++++++---------- src/tfcore.h | 8 +- src/tfcore_unix.h | 2 +- src/tkvsdatabase.cpp | 2 +- src/tkvsdatabase.h | 4 +- src/tlockqueue.h | 10 +- src/tlockstack.h | 10 +- src/tsqldatabase.cpp | 4 +- src/tsqldatabasepool.h | 2 - src/turingcoroutine_linux.cpp | 255 ++++++++++++++++++---------------- src/turingserver.h | 7 +- src/turingserver_linux.cpp | 70 ++++++++-- src/twebsocket.cpp | 10 +- src/twebsocketframe.cpp | 4 +- src/twebsocketframe.h | 4 +- src/twebsocketworker.cpp | 14 +- src/twebsocketworker.h | 5 +- 19 files changed, 373 insertions(+), 257 deletions(-) diff --git a/src/tabstractwebsocket.cpp b/src/tabstractwebsocket.cpp index 6d3163de3..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; 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/main.cpp b/src/test/container/main.cpp index 118e6aaee..b635c25ce 100644 --- a/src/test/container/main.cpp +++ b/src/test/container/main.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -13,8 +14,8 @@ template class FCQueue { public: - enum class State { Empty, Pending, Applied }; - enum class Op { Push, Pop }; + enum class State : uint8_t { Empty, Pending, Applied }; + enum class Op : uint8_t { Push, Pop }; struct Request { std::atomic state {FCQueue::State::Empty}; @@ -22,98 +23,128 @@ class FCQueue { std::optional value; }; - explicit FCQueue() + explicit FCQueue() // : reqs_{128} { - while (reqs_.size() < 128) { - reqs_.push_back(new Request{}); - } + // while (reqs_.size() < 128) { + // reqs_.push_back(new Request{}); + // } } ~FCQueue() { - for (auto p : reqs_) { - delete p; - } + // for (auto p : reqs_) { + // delete p; + // } } void push(T v) { - 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"); + if (try_push(v)) { + return; } - r->value = std::move(v); - r->op = Op::Push; - r->state.store(State::Pending, std::memory_order_release); + size_t id = acquireSlot(); + auto& r = reqs_[id]; - while (true) { - combine(); + // State s = r.state.load(std::memory_order_acquire); + // if (s != State::Empty) { + // throw std::runtime_error("FCQueue: push logic error"); + // } - if (r->state.load(std::memory_order_acquire) == State::Applied) { - r->state.store(State::Empty, std::memory_order_release); - break; - } + 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() { - size_t id = acquireSlot(); - auto& r = reqs_[id]; + std::optional val; - State s = r->state.load(std::memory_order_acquire); - if (s != State::Empty) { - throw std::runtime_error("FCQueue: pop logic error"); + if (try_pop(val)) { + return val; } - std::optional val; - r->op = Op::Pop; - r->state.store(State::Pending, std::memory_order_release); + size_t id = acquireSlot(); + auto& r = reqs_[id]; - while (true) { - combine(); - - if (r->state.load(std::memory_order_acquire) == State::Applied) { - val = std::move(r->value); - r->state.store(State::Empty, std::memory_order_release); - break; - } + 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) return mySlot; + + 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) { - std::lock_guard lock(combineLock_); + 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{}); + // } - size_t extend = reqs_.size(); - while (extend <= idx) extend *= 2; - while (reqs_.size() < extend) { - reqs_.push_back(new Request{}); - } } void combine() @@ -122,25 +153,34 @@ class FCQueue { return; } - int max = slotCounter_.load(std::memory_order_acquire); + 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); + } - for (int i = 0; i < max; i++) { - auto &r = reqs_[i]; - State s = r->state.load(std::memory_order_acquire); + auto &r = reqs_[idx]; + State s = r.state.load(std::memory_order_acquire); - if (s != State::Pending) + if (s != State::Pending) [[unlikely]] { continue; // Empty or Applied は combiner が触らない + } - switch (r->op) { + switch (r.op) { case Op::Push: - q_.push(std::move(r->value.value())); + q_.push(std::move(r.value.value())); break; case Op::Pop: if (q_.empty()) { - r->value = std::nullopt; + r.value = std::nullopt; } else { - r->value = std::move(q_.front()); + r.value = std::move(q_.front()); q_.pop(); } break; @@ -148,7 +188,8 @@ class FCQueue { default: break; } - r->state.store(State::Applied, std::memory_order_release); + r.state.store(State::Applied, std::memory_order_release); + cnt++; } combineLock_.unlock(); @@ -156,7 +197,10 @@ class FCQueue { private: std::mutex combineLock_; - std::deque reqs_; + std::atomic combineCounter {0}; + //std::deque reqs_; + std::array reqs_; + //std::vector reqs_; std::queue q_; std::atomic slotCounter_ {0}; }; @@ -311,36 +355,37 @@ class TestQueue : public QObject { private slots: void correctnessTest_FCQueue() { - correctnessTest, &FCQueue::push, &FCQueue::pop>(); + correctnessTest, &FCQueue::push, &FCQueue::pop>(); } - void correctnessTest_TLockQueue() - { - correctnessTest, &TLockQueue::push, &TLockQueue::pop>(); - } + // void correctnessTest_TLockQueue() + // { + // correctnessTest, &TLockQueue::push, &TLockQueue::pop>(); + // } - void correctnessTest_TLockStack() - { - correctnessTest, &TLockStack::push, &TLockStack::pop>(); - } + // void correctnessTest_TLockStack() + // { + // correctnessTest, &TLockStack::push, &TLockStack::pop>(); + // } void stressTest_FCQueue() { - stressTest, &FCQueue::push, &FCQueue::pop>(); + stressTest, &FCQueue::push, &FCQueue::pop>(); } void stressTest_TLockQueue() { - stressTest, &TLockQueue::push, &TLockQueue::pop>(); + using PushFunc = void (TLockQueue::*)(const int64_t&); + stressTest, static_cast(&TLockQueue::push), &TLockQueue::pop>(); } void stressTest_TLockStack() { - stressTest, &TLockStack::push, &TLockStack::pop>(); + using PushFunc = void (TLockStack::*)(const int64_t&); + stressTest, static_cast(&TLockStack::push), &TLockStack::pop>(); } }; -//QTEST_MAIN(TestQueue) -//TF_TEST_MAIN(TestQueue) + TF_TEST_SQLLESS_MAIN(TestQueue) #include "main.moc" 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/tkvsdatabase.cpp b/src/tkvsdatabase.cpp index 72c9878fd..86ff9d0be 100644 --- a/src/tkvsdatabase.cpp +++ b/src/tkvsdatabase.cpp @@ -315,7 +315,7 @@ void TKvsDatabase::moveToThread(QThread *targetThread) TKvsDatabase &TKvsDatabase::operator=(TKvsDatabase &&other) { if (this != &other) { - _connectName = other._connectName; + _connectName = std::forward(other._connectName); _driver = other._driver; other._driver = nullptr; } diff --git a/src/tkvsdatabase.h b/src/tkvsdatabase.h index dda619cdf..796380435 100644 --- a/src/tkvsdatabase.h +++ b/src/tkvsdatabase.h @@ -14,10 +14,10 @@ class T_CORE_EXPORT TKvsDatabase { class Handle { public: Handle() = default; - Handle(KvsDbPtr ptr) : _dbptr(std::move(ptr)) {} + explicit Handle(KvsDbPtr ptr) : _dbptr(std::move(ptr)) {} Handle(const Handle &) = delete; Handle &operator=(const Handle &) = delete; - Handle(Handle &&other) noexcept = default; + Handle(Handle &&other) { *this = std::move(other); } Handle &operator=(Handle &&other); ~Handle(); diff --git a/src/tlockqueue.h b/src/tlockqueue.h index 8b527b165..c3bb255c7 100644 --- a/src/tlockqueue.h +++ b/src/tlockqueue.h @@ -7,7 +7,13 @@ template class TLockQueue { public: - void push(T val) + 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)); @@ -19,7 +25,7 @@ class TLockQueue { std::optional val; if (!_queue.empty()) { - val = std::move(_queue.front()); + val = std::forward(_queue.front()); _queue.pop(); } return val; diff --git a/src/tlockstack.h b/src/tlockstack.h index 86e4330e8..2346cf418 100644 --- a/src/tlockstack.h +++ b/src/tlockstack.h @@ -16,7 +16,13 @@ class TLockStack { } TLockStack &operator=(TLockStack &&) = delete; - void push(T val) + 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)); @@ -28,7 +34,7 @@ class TLockStack { std::optional val; if (!_stack.empty()) { - val = std::move(_stack.top()); + val = std::forward(_stack.top()); _stack.pop(); } return val; diff --git a/src/tsqldatabase.cpp b/src/tsqldatabase.cpp index c904e2e5e..befce5317 100644 --- a/src/tsqldatabase.cpp +++ b/src/tsqldatabase.cpp @@ -39,8 +39,8 @@ TSqlDatabase::TSqlDatabase(TSqlDatabase &&other) TSqlDatabase &TSqlDatabase::operator=(TSqlDatabase &&other) { if (this != &other) { - _sqlDatabase = other._sqlDatabase; - _postOpenStatements = other._postOpenStatements; + _sqlDatabase = std::forward(other._sqlDatabase); + _postOpenStatements = std::forward(other._postOpenStatements); _enableUpsert = other._enableUpsert; _driverExtension = std::move(other._driverExtension); } diff --git a/src/tsqldatabasepool.h b/src/tsqldatabasepool.h index 7eeab597f..71e3f7f03 100644 --- a/src/tsqldatabasepool.h +++ b/src/tsqldatabasepool.h @@ -40,8 +40,6 @@ class T_CORE_EXPORT TSqlDatabasePool : public QObject { TAtomic *lastCachedTime {nullptr}; int maxConnects {0}; QBasicTimer timer; - // std::vector> availableDatabases; - // std::vector> cachedDatabases; std::vector> availableDatabases; std::vector> cachedDatabases; diff --git a/src/turingcoroutine_linux.cpp b/src/turingcoroutine_linux.cpp index 7903fbdb8..dbcfdc3d1 100644 --- a/src/turingcoroutine_linux.cpp +++ b/src/turingcoroutine_linux.cpp @@ -17,13 +17,13 @@ constexpr uint READ_THRESHOLD_LENGTH = 4 * 1024 * 1024; // bytes class AsyncRecv : public TAwaitBase { public: - AsyncRecv(int fd, void* buffer, size_t length, int msecs) : - _fd(fd), _buf(buffer), _len(length), _msecs(msecs) { } + 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(_fd, _buf, _len, _msecs, this) < 0) { + if (TUringServer::instance()->addRecv(_sd, _buf, _len, _msecs, this) < 0) { tSystemError("addRecv error: {}", strerror(errno)); } } @@ -34,7 +34,7 @@ class AsyncRecv : public TAwaitBase { } private: - int _fd {0}; + int _sd {0}; void* _buf {nullptr}; size_t _len {0}; int _msecs {0}; @@ -43,13 +43,13 @@ class AsyncRecv : public TAwaitBase { class AsyncSend : public TAwaitBase { public: - AsyncSend(int fd, const void *buf, size_t len) : - _fd(fd), _buf(buf), _len(len) { } + 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(_fd, _buf, _len, this); + int res = TUringServer::instance()->addSendZc(_sd, _buf, _len, this); if (res < 0) { tSystemError("addSend error: {}", strerror(errno)); @@ -63,46 +63,115 @@ class AsyncSend : public TAwaitBase { } private: - int _fd {0}; + int _sd {0}; const void* _buf {nullptr}; size_t _len {0}; }; -// template -// class AsyncFunction : public TAwaitBase { -// public: -// explicit AsyncFunction(std::function f) : -// _func(std::move(f)) {} -// ~AsyncFunction() { if (_fd > 0) ::close(_fd); } - -// void await_suspend(std::coroutine_handle handle) -// { -// _handle = handle; -// _fd = eventfd(0, (EFD_NONBLOCK | EFD_CLOEXEC)); -// TUringServer::instance()->addEvent(_fd, this); -// std::thread([this]() { -// _result = _func(); -// notifyResume(); -// }).detach(); -// } - -// R await_resume() { return _result; } - -// protected: -// void notifyResume() -// { -// if (_fd > 0) { -// uint64_t one = 1; -// write(_fd, &one, sizeof(one)); // 通知 -// } -// } - -// private: -// int _fd {0}; -// std::function _func; -// R _result {}; -// }; +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); + return (_cqeres < 0) || (_offset + _cqeres >= _fileSize); + } + + 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) { + // error + 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, 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 @@ -115,20 +184,6 @@ class ScopeExitFunction { }; -// class CurrentRoutineScope { -// public: -// CurrentRoutineScope(TUringCoroutine *&slot, TUringCoroutine *now) : -// _slot(slot), _prev(slot) -// { -// _slot = now; -// } -// ~CurrentRoutineScope() { _slot = _prev; } -// private: -// TUringCoroutine *&_slot; -// TUringCoroutine *_prev; -// }; - - TUringCoroutine::~TUringCoroutine() { //tSystemDebug("~TUringCoroutine: sd:{}", _sd); @@ -219,32 +274,33 @@ TUringTask TUringCoroutine::start() _response = std::move(result.response); _fileName = std::move(result.fileName); - int res = co_await AsyncSend(_sd, _response.data(), _response.length()); - if (res <= 0) { - tSystemError("Send error fd={} res={}", _sd, res); - co_return; + 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()) { - //int64_t fileSize = QFileInfo(_fileName).size(); int fd = ::open(qUtf8Printable(_fileName), O_RDONLY | O_CLOEXEC); if (fd < 0) { - tSystemDebug("File open error: {}", _fileName); + tSystemError("File open error: {}", _fileName); Tf::warn("File open error: {}", _fileName); co_return; } ScopeExitFunction fd_closing([fd]{ ::close(fd); }); - const int64_t file_size = lseek(fd, 0, SEEK_END); - if (file_size < 0) { - tSystemDebug("lseek error: {}", strerror(errno)); + struct stat st{}; + if (fstat(fd, &st) != 0) { + tSystemError("fstat error: {}", strerror(errno)); co_return; } - lseek(fd, 0, SEEK_SET); - - void *mapped = mmap(nullptr, file_size, PROT_READ, MAP_PRIVATE, fd, 0); + 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; @@ -252,20 +308,25 @@ TUringTask TUringCoroutine::start() const int64_t slice_len = 16 * 1024; int64_t sent_len = 0; - while (sent_len < file_size) { - int64_t send_len = std::min(file_size - sent_len, slice_len); + 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 fd={} res={}", _sd, res); + tSystemError("Send error sd={} res={}", _sd, res); break; } sent_len += res; } - munmap(mapped, file_size); + munmap(mapped, fileSize); +#else + int res = co_await AsyncSendFile(_sd, fd, fileSize); + tSystemDebug("AsyncSendFile: res:{}", res); + +#endif _fileName.resize(0); } @@ -276,53 +337,3 @@ TUringTask TUringCoroutine::start() } } } - -/* -int64_t TUringCoroutine::writeResponse(THttpResponseHeader &header, QIODevice *body) -{ - if (keepAliveTimeout() > 0) { - header.setRawHeader(QByteArrayLiteral("Connection"), QByteArrayLiteral("Keep-Alive")); - } - // Writes HTTP header - _response = header.toByteArray(); - - if (body) { - if (auto *buf = dynamic_cast(body); buf) { // dynamic_cast is faster for QBuffer - _response += buf->buffer(); - } else if (auto *file = qobject_cast(body); file) { - _fileName = file->fileName(); - } else { - tSystemError("Invalid body [{}:{}]", __FILE__, __LINE__); - } - } - return 0; -} -*/ - -// TAsyncTask TUringCoroutine::executeRequest(THttpRequest &request) -// { - // int res = co_await TThreadPool::instance().run([this](THttpRequest &req) { - // execute(req); - // return 0; - // }, request); - // co_return res; - - - // int x = 1; - // int res = co_await TThreadPool::instance().run([this](int &d) { - // d++; - // return 0; - // }, x); - // co_return res; - - - // tSystemInfo("#### foo0"); - // int x = co_await TThreadPool::instance().run([](int a) { - // tSystemInfo("#### foo1"); - // std::this_thread::sleep_for(std::chrono::milliseconds(100)); - // tSystemInfo("#### foo2 {}", a); - // return a * 2; - // }, 3); - // tSystemInfo("#### foo3 {}", x); - // co_return x; -//} diff --git a/src/turingserver.h b/src/turingserver.h index 214ff4a03..f863d2513 100644 --- a/src/turingserver.h +++ b/src/turingserver.h @@ -44,12 +44,14 @@ class TAwaitBase { public: virtual ~TAwaitBase() { } virtual bool await_ready() const noexcept { return false; } - inline void clear() + void clear() { _cqeres = 0; _cqeflags = 0; _sqecounter = 1; } + virtual void iterate() { } + virtual bool completed() const { return true; } std::coroutine_handle _handle {}; int _cqeres {0}; @@ -76,7 +78,8 @@ class T_CORE_EXPORT TUringServer : public TDatabaseContextThread, public TApplic 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 addEvent(int sd, 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: diff --git a/src/turingserver_linux.cpp b/src/turingserver_linux.cpp index ef9da7c4c..975ee852d 100644 --- a/src/turingserver_linux.cpp +++ b/src/turingserver_linux.cpp @@ -166,7 +166,7 @@ void TUringServer::run() 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) { + if (res < 0 || !cqe) [[unlikely]] { if (res == -EINTR || res == -EAGAIN) { continue; } @@ -184,8 +184,8 @@ void TUringServer::run() } void *user_data = io_uring_cqe_get_data(cqe); - if (user_data) { - if (user_data == (void*)UD_NOTIFY) { + if (user_data) [[likely]] { + if (user_data == (void*)UD_NOTIFY) [[unlikely]] { if (!(cqe->flags & IORING_CQE_F_MORE)) { addNotifyEvent(); } @@ -230,14 +230,18 @@ void TUringServer::run() } } else { tSystemDebug("cqe->res:{} cqe->flags: {}", await->_cqeres, cqe->flags); - if (cqe->flags != IORING_CQE_F_MORE) { + 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()) { - await->_handle.resume(); + if (await->completed()) { + await->_handle.resume(); + } else { + await->iterate(); + } } } } @@ -393,10 +397,36 @@ int TUringServer::addSendZc(int fd, const void* buf, size_t len, TAwaitBase* awa // } -// -// Prepare a event request -// -int TUringServer::addEvent(int fd, TAwaitBase* await) const +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) { @@ -404,7 +434,7 @@ int TUringServer::addEvent(int fd, TAwaitBase* await) const return -1; } - io_uring_prep_poll_add(sqe, fd, POLL_IN); + io_uring_prep_poll_add(sqe, sd, poll_mask); if (await) { await->clear(); io_uring_sqe_set_data(sqe, await); @@ -413,6 +443,26 @@ int TUringServer::addEvent(int fd, TAwaitBase* await) const } +// +// 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); 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; }; - From d4bded26a740860aaf819dfde98004ca15f82479 Mon Sep 17 00:00:00 2001 From: aoyama Date: Mon, 22 Dec 2025 23:06:36 +0900 Subject: [PATCH 16/16] update --- src/turingcoroutine_linux.cpp | 22 +++++++++++++++++++--- src/turingserver_linux.cpp | 4 ++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/turingcoroutine_linux.cpp b/src/turingcoroutine_linux.cpp index dbcfdc3d1..1cf7eb719 100644 --- a/src/turingcoroutine_linux.cpp +++ b/src/turingcoroutine_linux.cpp @@ -105,7 +105,24 @@ class AsyncSendFile : public TAwaitBase { bool completed() const override { //tSystemInfo("AsyncSendFile::completed : _offset:{} _cqeflags:{} _cqeres:{}", _offset, _cqeflags, _cqeres); - return (_cqeres < 0) || (_offset + _cqeres >= _fileSize); + 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 @@ -116,7 +133,6 @@ class AsyncSendFile : public TAwaitBase { switch (_state) { case State::WaitForPollOut: if (_cqeres < 0) { - // error return; } _state = State::Idle; @@ -144,7 +160,7 @@ class AsyncSendFile : public TAwaitBase { } } - int res = TUringServer::instance()->addPoll(_sd, POLLOUT, this); + int res = TUringServer::instance()->addPoll(_sd, (POLLOUT | POLLERR | POLLHUP), this); if (res < 0) { tSystemError("addPoll error: {}", strerror(errno)); _cqeres = -1; diff --git a/src/turingserver_linux.cpp b/src/turingserver_linux.cpp index 975ee852d..f8f59fe5d 100644 --- a/src/turingserver_linux.cpp +++ b/src/turingserver_linux.cpp @@ -80,7 +80,7 @@ TUringServer::TUringServer(int listeningSocket, QObject *parent) : TApplicationServerBase(), _listenSocket(listeningSocket) { - io_uring_queue_init(8192 * 2, &_ring, 0); + io_uring_queue_init(8192, &_ring, 0); // Event fd _notifyFd = eventfd(0, (EFD_NONBLOCK | EFD_CLOEXEC)); @@ -229,7 +229,7 @@ void TUringServer::run() } } } else { - tSystemDebug("cqe->res:{} cqe->flags: {}", await->_cqeres, cqe->flags); + 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;