From 65c101bee030b18d409ab0e4de6afa91253c337b Mon Sep 17 00:00:00 2001 From: antao Date: Tue, 26 Mar 2019 10:45:39 +0800 Subject: [PATCH] Add the getLoop() method to the HttpAppFramework class --- lib/inc/drogon/HttpAppFramework.h | 58 ++++++++++++++++++------------- lib/src/HttpAppFrameworkImpl.cc | 25 ++++++++----- lib/src/HttpAppFrameworkImpl.h | 16 ++++++--- lib/src/HttpClientImpl.cc | 4 +-- lib/tests/CMakeLists.txt | 3 +- lib/tests/MainLoopTest.cc | 14 ++++++++ 6 files changed, 79 insertions(+), 41 deletions(-) create mode 100644 lib/tests/MainLoopTest.cc diff --git a/lib/inc/drogon/HttpAppFramework.h b/lib/inc/drogon/HttpAppFramework.h index ca6fd497..95ae6428 100755 --- a/lib/inc/drogon/HttpAppFramework.h +++ b/lib/inc/drogon/HttpAppFramework.h @@ -71,8 +71,8 @@ class HttpAppFramework : public trantor::NonCopyable ///Run the event loop /** * Calling this method starts the IO event loops and the main loop of the application; - * Usually, the thread that calls this method is the main thread of the application; - * This method blocks the calling thread until the main loop exits. + * This method MUST be called in the main thread. + * This method blocks the main thread until the main event loop exits. */ virtual void run() = 0; @@ -83,7 +83,7 @@ class HttpAppFramework : public trantor::NonCopyable /** * Calling this method results in stopping all network IO in the * framework and interrupting the blocking of the run() method. Usually, - * after calling this method, the application will exit. + * after calling this method, the application exits. * * NOTE: * This method can be called in any thread and anywhere. @@ -91,16 +91,15 @@ class HttpAppFramework : public trantor::NonCopyable */ virtual void quit() = 0; - ////////////////////////////////////////////////////// - // ///Get the event loop of the framework; - // /** - // * NOTE: - // * The event loop is not the network IO loop, but the main event loop - // * of the framework in which only some timer tasks are running; - // * You can run some timer tasks or other tasks in this loop; - // */ - // // virtual trantor::EventLoop *loop() = 0; - /////////////////////////////////////////////////////////// + ///Get the main event loop of the framework; + /** + * NOTE: + * The event loop is not the network IO loop, but the main event loop + * of the framework in which only some timer tasks are running; + * User can run some timer tasks or other tasks in this loop; + * This method can be call in any thread. + */ + virtual trantor::EventLoop *getLoop() = 0; ///Load the configuration file with json format. virtual void loadConfigFile(const std::string &fileName) = 0; @@ -130,18 +129,18 @@ class HttpAppFramework : public trantor::NonCopyable * @param filtersAndMethods is the same as the third parameter in the above method. * * FOR EXAMPLE: - * app.registerHandler("/hello?username={1}", - [](const HttpRequestPtr& req, - const std::function & callback, - const std::string &name) - { - Json::Value json; - json["result"]="ok"; - json["message"]=std::string("hello,")+name; - auto resp=HttpResponse::newHttpJsonResponse(json); - callback(resp); - }, - {Get,"LoginFilter"}); + * app.registerHandler("/hello?username={1}", + * [](const HttpRequestPtr& req, + * const std::function & callback, + * const std::string &name) + * { + * Json::Value json; + * json["result"]="ok"; + * json["message"]=std::string("hello,")+name; + * auto resp=HttpResponse::newHttpJsonResponse(json); + * callback(resp); + * }, + * {Get,"LoginFilter"}); * * NOTE: * As you can see in the above example, this method supports parameters mapping. @@ -182,6 +181,7 @@ class HttpAppFramework : public trantor::NonCopyable } registerHttpController(pathPattern, binder, validMethods, filters); } + /// Register a WebSocketController into the framework. /// The parameters of this method are the same as those in the registerHttpSimpleController() method. virtual void registerWebSocketController(const std::string &pathName, @@ -424,9 +424,17 @@ class HttpAppFramework : public trantor::NonCopyable #if USE_ORM ///Get a database client by @param name + /** + * NOTE: + * This method must be called after the framework has been run. + */ virtual orm::DbClientPtr getDbClient(const std::string &name = "default") = 0; ///Get a 'fast' database client by @param name + /** + * NOTE: + * This method must be called after the framework has been run. + */ virtual orm::DbClientPtr getFastDbClient(const std::string &name = "default") = 0; ///Create a database client diff --git a/lib/src/HttpAppFrameworkImpl.cc b/lib/src/HttpAppFrameworkImpl.cc index f13e7af0..6af8fa7f 100755 --- a/lib/src/HttpAppFrameworkImpl.cc +++ b/lib/src/HttpAppFrameworkImpl.cc @@ -44,6 +44,13 @@ using namespace drogon; using namespace std::placeholders; +///Make sure that the main event loop is initialized in the main thread. +drogon::InitBeforeMainFunction drogon::HttpAppFrameworkImpl::_initFirst([]() { + HttpAppFrameworkImpl::instance().getLoop()->runInLoop([]() { + LOG_TRACE << "Initialize the main event loop in the main thread"; + }); +}); + static void godaemon(void) { printf("Initializing daemon mode\n"); @@ -212,9 +219,9 @@ void HttpAppFrameworkImpl::run() //go daemon! godaemon(); #ifdef __linux__ - loop()->resetTimerQueue(); + getLoop()->resetTimerQueue(); #endif - loop()->resetAfterFork(); + getLoop()->resetAfterFork(); } //set relaunching if (_relaunchOnError) @@ -237,7 +244,7 @@ void HttpAppFrameworkImpl::run() sleep(1); LOG_INFO << "start new process"; } - loop()->resetAfterFork(); + getLoop()->resetAfterFork(); } //set logger @@ -271,7 +278,7 @@ void HttpAppFrameworkImpl::run() if (!_libFilePaths.empty()) { - _sharedLibManagerPtr = std::unique_ptr(new SharedLibManager(loop(), _libFilePaths)); + _sharedLibManagerPtr = std::unique_ptr(new SharedLibManager(getLoop(), _libFilePaths)); } std::vector> servers; std::vector> loopThreads; @@ -399,21 +406,21 @@ void HttpAppFrameworkImpl::run() tmpTimeout = tmpTimeout / 100; } } - _sessionMapPtr = std::unique_ptr>(new CacheMap(loop(), 1.0, wheelNum, bucketNum)); + _sessionMapPtr = std::unique_ptr>(new CacheMap(getLoop(), 1.0, wheelNum, bucketNum)); } else if (_sessionTimeout == 0) { - _sessionMapPtr = std::unique_ptr>(new CacheMap(loop(), 0, 0, 0)); + _sessionMapPtr = std::unique_ptr>(new CacheMap(getLoop(), 0, 0, 0)); } } - _responseCachingMap = std::unique_ptr>(new CacheMap(loop(), 1.0, 4, 50)); //Max timeout up to about 70 days; + _responseCachingMap = std::unique_ptr>(new CacheMap(getLoop(), 1.0, 4, 50)); //Max timeout up to about 70 days; // Let listener event loops run when everything is ready. for (auto &loopTh : loopThreads) { loopTh->run(); } - loop()->loop(); + getLoop()->loop(); } #if USE_ORM void HttpAppFrameworkImpl::createDbClients(const std::vector &ioloops) @@ -745,7 +752,7 @@ void HttpAppFrameworkImpl::onAsyncRequest(const HttpRequestImplPtr &req, std::fu _httpSimpleCtrlsRouter.route(req, std::move(callback), needSetJsessionid, std::move(sessionId)); } -trantor::EventLoop *HttpAppFrameworkImpl::loop() +trantor::EventLoop *HttpAppFrameworkImpl::getLoop() { static trantor::EventLoop loop; return &loop; diff --git a/lib/src/HttpAppFrameworkImpl.h b/lib/src/HttpAppFrameworkImpl.h index b3cca0df..d280cf6d 100644 --- a/lib/src/HttpAppFrameworkImpl.h +++ b/lib/src/HttpAppFrameworkImpl.h @@ -37,6 +37,13 @@ namespace drogon { +struct InitBeforeMainFunction +{ + InitBeforeMainFunction(const std::function &func) + { + func(); + } +}; class HttpAppFrameworkImpl : public HttpAppFramework { public: @@ -112,12 +119,12 @@ class HttpAppFrameworkImpl : public HttpAppFramework return _running; } - trantor::EventLoop *loop(); + virtual trantor::EventLoop *getLoop() override; virtual void quit() override { - if (loop()->isRunning()) - loop()->quit(); + if (getLoop()->isRunning()) + getLoop()->quit(); } virtual void setServerHeaderField(const std::string &server) override @@ -126,7 +133,7 @@ class HttpAppFrameworkImpl : public HttpAppFramework assert(server.find("\r\n") == std::string::npos); _serverHeader = "Server: " + server + "\r\n"; } - + const std::string &getServerHeaderString() const { return _serverHeader; @@ -237,6 +244,7 @@ class HttpAppFrameworkImpl : public HttpAppFramework std::map> _dbFastClientsMap; void createDbClients(const std::vector &ioloops); #endif + static InitBeforeMainFunction _initFirst; }; } // namespace drogon diff --git a/lib/src/HttpClientImpl.cc b/lib/src/HttpClientImpl.cc index eba14db0..1e50f462 100644 --- a/lib/src/HttpClientImpl.cc +++ b/lib/src/HttpClientImpl.cc @@ -304,10 +304,10 @@ void HttpClientImpl::onRecvMessage(const trantor::TcpConnectionPtr &connPtr, tra HttpClientPtr HttpClient::newHttpClient(const std::string &ip, uint16_t port, bool useSSL, trantor::EventLoop *loop) { bool isIpv6 = ip.find(":") == std::string::npos ? false : true; - return std::make_shared(loop == nullptr ? HttpAppFrameworkImpl::instance().loop() : loop, trantor::InetAddress(ip, port, isIpv6), useSSL); + return std::make_shared(loop == nullptr ? HttpAppFrameworkImpl::instance().getLoop() : loop, trantor::InetAddress(ip, port, isIpv6), useSSL); } HttpClientPtr HttpClient::newHttpClient(const std::string &hostString, trantor::EventLoop *loop) { - return std::make_shared(loop == nullptr ? HttpAppFrameworkImpl::instance().loop() : loop, hostString); + return std::make_shared(loop == nullptr ? HttpAppFrameworkImpl::instance().getLoop() : loop, hostString); } diff --git a/lib/tests/CMakeLists.txt b/lib/tests/CMakeLists.txt index 10990436..f05c97d2 100755 --- a/lib/tests/CMakeLists.txt +++ b/lib/tests/CMakeLists.txt @@ -9,4 +9,5 @@ add_executable(view_data_test HttpViewDataTest.cc) add_executable(md5_test Md5Test.cc ../src/ssl_funcs/Md5.cc) add_executable(http_full_date_test HttpFullDateTest.cc) add_executable(gzip_test GzipTest.cc) -add_executable(url_codec_test UrlCodecTest.cc) \ No newline at end of file +add_executable(url_codec_test UrlCodecTest.cc) +add_executable(main_loop_test MainLoopTest.cc) \ No newline at end of file diff --git a/lib/tests/MainLoopTest.cc b/lib/tests/MainLoopTest.cc new file mode 100644 index 00000000..c8034b25 --- /dev/null +++ b/lib/tests/MainLoopTest.cc @@ -0,0 +1,14 @@ +#include +#include +#include + +int main() +{ + std::thread([]() { + drogon::app().getLoop()->runEvery(1, []() { + std::cout << "!" << std::endl; + }); + }) + .detach(); + drogon::app().run(); +} \ No newline at end of file