Skip to content

Using Coroutine #2394

@UInSomnia

Description

@UInSomnia

Hello, An Tao! I'm using your Drogon framework without controllers. The bottom line is that every 5 minutes a method from my class should be run to make a series of API requests to the server.

main.cpp

#include <drogon/drogon.h>

#include "octopus.h"

int main()
{
    drogon::HttpAppFramework &root = drogon::app();
    
    root.loadConfigFile("../../config_drogon.json");
    
    InSomnia::Octopus octopus;
    octopus.starter();
    
    drogon::app().run();
    
    return 0;
}

octopus.cpp

    void Octopus::starter()
    {
        const Config &config = Config::get_instance();
        
        const Octopus_Mode octopus_mode = config.get_octopus_mode();
        
        const double delay_start =
            config.get_octopus_delay_start(); // seconds
        
        const double period =
            config.get_octopus_period(); // seconds
        
        drogon::HttpAppFramework &root = drogon::app();
        
        trantor::EventLoop *loop = root.getLoop();
        
        if (octopus_mode == Octopus_Mode::IMITATION)
        {
            loop->runAfter(delay_start, [this]()
            {
                this->exec();
            });
        }
        else if (octopus_mode == Octopus_Mode::REALITY)
        {
            loop->runAfter(delay_start, [this]()
            {
                this->exec();
            });
            loop->runEvery(period, [this]()
            {
                this->exec();
            });
        }
        else if (octopus_mode == Octopus_Mode::DATABASE_MAINTENANCE)
        {
            loop->runAfter(delay_start, []()
            {
                // Create user in data base
                User::create_user_db();
                
                // Print list users from data base
                const std::string str_list_users =
                    User::print_list_users_from_db();
                
                std::cout << "Users:\n" << str_list_users << "\n";
                std::cout.flush();
            });
        }
        else if (octopus_mode == Octopus_Mode::SANBOX)
        {
            loop->runAfter(delay_start, [this]()
            {
                // Test hash librarys
                // Sandbox::test_hash_library();
                
                // Test Trade API ByBit
                this->sandbox = std::make_shared<Sandbox>();
                // this->sandbox->test_trade_api_bybit();
                this->sandbox->test_trade_api_bybit_coro();
            });
        }
        else
        {
            throw std::runtime_error("Octopus mode is incorrect");
        }
    }

I first started the event loop (app.run()) and then (delayed by timer) called my methods that access the database and the remote server via API

Basically, this approach worked fine until I started using coroutines in the code. When using sendRequestCoro, the coroutine started executing and never ended. When debugging, it was clear that the thread was dying after launching the coroutine. As I diagnosed, the problem was that the coroutine was running in the same thread as the main event loop

If I moved the event loop to a separate thread, then the root was executed correctly, but accessing the database broke and deteriorated

Then I did this

int main()
{
    drogon::HttpAppFramework &root = drogon::app();
    root.loadConfigFile("../../config_drogon.json");

    std::mutex mtx;
    std::condition_variable cv;
    bool is_initialized = false;

    std::thread app_thread([&root, &mtx, &cv, &is_initialized]()
    {
        root.getLoop()->queueInLoop([&mtx, &cv, &is_initialized]()
        {
            {
                std::lock_guard<std::mutex> lock(mtx);
                is_initialized = true;
            }
            cv.notify_one(); // Сообщаем основному потоку, что можно продолжать
            std::cout << "Drogon initialized signal sent." << std::endl;
        });

        std::cout << "Starting Drogon..." << std::endl;
        root.run();
        std::cout << "Drogon stopped." << std::endl;
    });

    std::cout << "Waiting for Drogon to initialize..." << std::endl;
    std::unique_lock<std::mutex> lock(mtx);
    cv.wait(lock, [&is_initialized] { return is_initialized; });
    lock.unlock();
    std::cout << "Drogon initialized. Proceeding with runTest." << std::endl;

    InSomnia::Octopus octopus;
    octopus.starter();
    
    app_thread.join();

    std::cout << "Application finished." << std::endl;
    return 0;
}

But I see this code as ugly. Secondly, I can no longer use the launch of my methods every 5 minutes, since the

trantor::EventLoop *loop = root.getLoop();
loop->runEvery(...);

spoils the coroutine challenge (she goes into oblivion again)

An Tao, please tell me how to write beautiful and elegant Drogon code that calls one of my methods every 5 minutes. To make all Drogon functions work and it was right

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions