Skip to content

[Proposal] New Accpetor/Listener #6

@sandman7920

Description

@sandman7920

I think it's a good idea to have two io_context.
One for accepting new connections (maybe also signal handler),
and one for sessions.

Currently I am using something like this:

int main(int argc, char *argv[]) {
    namespace ba = boost::asio;
    using tcp = ba::ip::tcp;
    // Only for acceptor (maybe also signal handler)
    ba::io_context ctx_accpetor{1};
    tcp::acceptor acceptor(ctx_accpetor);

    std::const_view address{"::"};
    constexpr uint16_t port{8081};

    // Start listen before allocate any resources
    // If we can't bind exit/throw
    tcp::endpoint ep;
    try {
        ep = {ba::ip::make_address(address), port};
        acceptor.open(ep.protocol());
        acceptor.set_option(ba::socket_base::reuse_address{true});
        acceptor.bind(ep);
        acceptor.listen();
    } catch (const std::exception &e) {
        std::cerr << "[" << address <<"]:" << port <<" " << e.what() << '\n';
        return 1;
    }

    auto app = expresscpp::ExpressCpp();
	
    app.Use([](const auto & /*req*/, const auto &res, const Next & /*next*/) {
        res->Json("{}");
    });
	
    auto thn = std::thread::hardware_concurrency;
    // Only for sessions
    ba::io_context ctx_sessions{htn};
    // Keep ctx_sessions running (wait for new sessions to spawn)
    auto work_guard = boost::asio::make_work_guard(ctx_sessions);
    // Start all workers
    std::vector<std::thread> workers;
    workers.reserve(thn);
    for (uint32_t i = 0; i < htn; ++i) {
        workers.emplace_back(std::thread([&ctx_sessions] { ctx_sessions.run(); }));
    }

    // Accpet only on one thread (no need for strand, std::mutex)
    using AcceptHandler = std::function<void(const boost::system::error_code &, ba::ip::tcp::socket)>;
    AcceptHandler handler = [&](const auto &ec, auto peer) {
        if (!ec) {
            std::make_shared<expresscpp::Session>(std::move(peer), &app)->run();
            acceptor.async_accept(ctx_sessions, handler);
        }
    };

    acceptor.async_accept(ctx_sessions, handler);

    // attach signals to ctx_accpetor and thread
    ba::signal_set signal_set(ctx_accpetor, SIGINT, SIGTERM, SIGQUIT);
    signal_set.async_wait([&acceptor](boost::system::error_code const &, int /*sig*/) {
        boost::system::error_code ec;
        acceptor.cancel(ec);
        acceptor.close(ec);
    });

    // Start Accept
    ctx_accpetor.run();
    // Acceptor is cancelled
	
    // Retire guard
    work_guard.reset();
	
    // Wait for all sessions to finish gracefully
    for (auto i = workers.size(); i--;) {
        workers.at(i).join();
    }

    return 0;
}

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