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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions