STTNet 0.7.0

Chapter 6: WorkerPool

This chapter describes blocking-work dispatch, request snapshots, response queues, overload rejection, and shutdown boundaries.

1. What belongs in WorkerPool

  • Database and cache client calls
  • Filesystem, compression, and image work
  • External HTTP/RPC calls
  • Sleeping or unpredictable lock waits

2. Complete demo

#include <sttnet.h>

#include <chrono>
#include <iostream>
#include <thread>

int main()
{
    using namespace stt::network;
    using stt::system::ServerSetting;

    if(!ServerSetting::blockTerminationSignals())
    {
        std::cerr << "failed to block termination signals\n";
        return 1;
    }

    HttpServer server;

    // Bound the number of waiting Worker tasks. When this limit is reached,
    // STTNet rejects new tasks instead of allowing unbounded memory growth.
    server.setMaxPendingWorkerTasks(4096);

    server.setFunction("/slow",
        [&server](HttpServerFDHandler &client,
                  HttpRequestInformation &request) {
            // Reactor callbacks are non-blocking execution points. Database calls,
            // filesystem access, RPC, sleeping, and unpredictable lock waits belong
            // in the WorkerPool rather than the network event loop.
            server.putTask(
                [](HttpServerFDHandler &workerClient,
                   HttpRequestInformation &workerRequest) {
                    // client/request are safe snapshots supplied by STTNet.
                    // Outer callback references may be invalid when the Worker executes.
                    std::this_thread::sleep_for(std::chrono::milliseconds(200));

                    const std::string result =
                        "worker finished for " + workerRequest.loc;

                    // In a server Worker, sendText() enqueues the response for
                    // the Reactor; the Worker never writes socket/TLS directly.
                    return workerClient.sendText(result) ? 1 : -2;
                },
                client,
                request);

            // 0 means: this processing stage continues in the WorkerPool.
            // When the task returns 1, STTNet resumes the next registered stage.
            return 0;
        });

    // The second argument is the WorkerPool thread count, not Reactor count.
    // One server instance uses one Reactor thread to drive network events.
    if(!server.startListen(8082, 4))
    {
        std::cerr << "failed to listen on port 8082\n";
        return 2;
    }

    std::cout << "WorkerPool demo listening on http://127.0.0.1:8082\n";
    ServerSetting::waitForTerminationSignal();
    return server.close() ? 0 : 3;
}

3. Callback capture and lifetime

// Unsafe pattern: outer references may be invalid when the Worker runs.
server.putTask([&client, &request] { /* ... */ }, client, request);

// Safe pattern: use the snapshots passed to the Worker function.
server.putTask([](HttpServerFDHandler &workerClient,
                  HttpRequestInformation &workerRequest) {
    return workerClient.sendText("done") ? 1 : -2;
}, client, request);

4. Bounds and timeouts

setMaxPendingWorkerTasks() rejects overload instead of allowing unbounded memory growth. Monitor worker_task_rejections.

Graceful shutdown cannot safely kill a user task that is already running. Database, RPC, filesystem, and lock waits therefore use finite timeouts and eventually return.

5. Validation

./build/examples/sttnet_worker_pool
curl -i http://127.0.0.1:8082/slow