Commit 89bd12b0 authored by jan.koester's avatar jan.koester
Browse files

test

parent d566cfe2
Loading
Loading
Loading
Loading
+50 −22
Changes for test/httpd_h2_concurrency_test.cpp: 50 added lines, 22 removed lines.
Original line number Diff line number Diff line
@@ -189,27 +189,53 @@ std::vector<std::chrono::steady_clock::time_point> runConcurrentRequest(
    return times;
}

// Binds an ephemeral-port TCP listener, starts a SlowServer on it in a
// background thread, and returns the bound port. The server thread is
// intentionally never joined — the test process exits via std::exit once
// all assertions are done, which is fine for a short-lived test binary and
// avoids netplus::event::Running (a process-wide static) making a second,
// sequential runEventloop() call impossible after the first is stopped.
int startServer(std::unique_ptr<netplus::tcp> &sockOwner, size_t h2OffloadThreads) {
    sockOwner = std::make_unique<netplus::tcp>("127.0.0.1", 0, 16, -1);
    sockOwner->bind();
// A running server plus everything needed to tear it down deterministically.
// netplus::event's connection registry, wakeup eventfd and Running/Restart
// flags are process-wide statics (needed elsewhere so the H2-offload dispatch
// can look up/rearm a connection from any thread by fd). That means a
// SlowServer's background threads keep touching that shared static state for
// as long as they're alive — they must be fully stopped and joined before the
// next server starts (or before the process exits), or a worker thread can be
// mid-access when those statics get destroyed, which segfaults.
struct RunningServer {
    std::unique_ptr<netplus::tcp> sock;
    std::unique_ptr<SlowServer> server;
    std::thread thread;
};

// Binds an ephemeral-port TCP listener and starts a SlowServer on it in a
// background thread.
RunningServer startServer(int &port, size_t h2OffloadThreads) {
    RunningServer rs;
    rs.sock = std::make_unique<netplus::tcp>("127.0.0.1", 0, 16, -1);
    rs.sock->bind();

    sockaddr_in bound{};
    socklen_t blen = sizeof(bound);
    ::getsockname(sockOwner->fd(), reinterpret_cast<sockaddr*>(&bound), &blen);
    int port = ntohs(bound.sin_port);

    sockOwner->listen();

    auto *server = new SlowServer({sockOwner.get()}, h2OffloadThreads);
    std::thread([server] { server->runEventloop(); }).detach();
    ::getsockname(rs.sock->fd(), reinterpret_cast<sockaddr*>(&bound), &blen);
    port = ntohs(bound.sin_port);

    rs.sock->listen();

    rs.server = std::make_unique<SlowServer>(
        std::vector<netplus::socket*>{rs.sock.get()}, h2OffloadThreads);
    // requestStop() (see stopServer()) leaves this false; a fresh
    // runEventloop() call needs it true again to not exit its worker loop
    // immediately.
    netplus::event::Running = true;
    SlowServer *serverPtr = rs.server.get();
    rs.thread = std::thread([serverPtr] { serverPtr->runEventloop(); });
    std::this_thread::sleep_for(std::chrono::milliseconds(50));
    return port;
    return rs;
}

// Signals the server's event loop to stop and blocks until its worker
// threads have actually joined and torn down (see RunningServer above for
// why this must happen before the next server starts or the process exits).
void stopServer(RunningServer &rs) {
    netplus::event::requestStop();
    if (rs.thread.joinable())
        rs.thread.join();
}

} // namespace
@@ -223,9 +249,10 @@ int main() {
    // Run 1: h2OffloadThreads = 0 — must reproduce the staircase, proving
    // the untouched synchronous fallback path still behaves as before.
    {
        std::unique_ptr<netplus::tcp> sock;
        int port = startServer(sock, /*h2OffloadThreads=*/0);
        int port = 0;
        RunningServer rs = startServer(port, /*h2OffloadThreads=*/0);
        auto times = runConcurrentRequest(port, streamIds, delayMs);
        stopServer(rs);
        auto totalMs = std::chrono::duration_cast<std::chrono::milliseconds>(
            times.back() - times.front()).count();
        std::cout << "[sync]    spread=" << totalMs << "ms (expect >= ~"
@@ -246,9 +273,10 @@ int main() {
    // Run 2: h2OffloadThreads > 0 with shouldOffloadH2Dispatch() true —
    // must show the streams completing with overlapping wall clocks.
    {
        std::unique_ptr<netplus::tcp> sock;
        int port = startServer(sock, /*h2OffloadThreads=*/4);
        int port = 0;
        RunningServer rs = startServer(port, /*h2OffloadThreads=*/4);
        auto times = runConcurrentRequest(port, streamIds, delayMs);
        stopServer(rs);
        auto totalMs = std::chrono::duration_cast<std::chrono::milliseconds>(
            times.back() - times.front()).count();
        std::cout << "[offload] spread=" << totalMs << "ms (expect < ~"
@@ -262,5 +290,5 @@ int main() {
    }

    std::cout << (failures == 0 ? "PASS" : "FAIL") << std::endl;
    std::exit(failures == 0 ? 0 : 1);
    return failures == 0 ? 0 : 1;
}