DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
worker.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file worker.h
6 * @brief Layer 5 (Session) - server worker identity.
7 *
8 * The server pipeline runs in one or more dedicated worker tasks (see
9 * DETWS_WORKER_COUNT). Each worker owns a disjoint partition of connection slots
10 * (slot i -> worker i % count) and its own scratch arena, so per-worker state
11 * (the arena, work buffers) is selected by the caller's worker id. This header is
12 * the single source of that id.
13 *
14 * The id is per-task/per-thread: a worker binds itself once at task entry via
15 * detws_worker_set_self(); any context that has not bound an id (the user's
16 * loop(), a unit test, the lwIP thread) reads 0, which is also the only valid id
17 * in the default single-worker build, so DETWS_WORKER_COUNT == 1 is byte-for-byte
18 * the original single-pipeline behavior.
19 *
20 * @author Douglas Quigg (dstroy0)
21 * @date 2026
22 */
23
24#ifndef DETERMINISTICESPASYNCWEBSERVER_WORKER_H
25#define DETERMINISTICESPASYNCWEBSERVER_WORKER_H
26
27#include "ServerConfig.h"
28
29/** @brief Number of server worker tasks (DETWS_WORKER_COUNT). */
30int detws_worker_count(void);
31
32/** @brief Worker id [0, count) of the calling task; 0 by default / single-worker. */
33int detws_worker_self(void);
34
35/** @brief Bind the calling task/thread to worker id @p id (worker entry / tests). */
36void detws_worker_set_self(int id);
37
38// ---------------------------------------------------------------------------
39// Worker tasks (ESP32)
40// ---------------------------------------------------------------------------
41//
42// On ESP32 the server runs in dedicated FreeRTOS worker tasks instead of the
43// user's loop(): detws_workers_start() spawns DETWS_WORKER_COUNT tasks, each
44// pinned to a core, each binding its worker id and repeatedly invoking the
45// app-supplied pump (so this layer stays free of any app dependency). On host
46// builds there are no tasks - the pipeline is driven inline by handle() / tests -
47// so these are no-ops and detws_workers_running() is false.
48
49/** @brief Pump callback run by each worker task with its worker id. */
50typedef void (*detws_worker_pump_fn)(int worker_id);
51
52/** @brief Spawn the worker task(s) and start them running @p pump. No-op on host. */
54
55/**
56 * @brief Wake worker @p worker_id so it services a freshly-queued event now.
57 *
58 * Each worker blocks between service iterations (it no longer free-runs the poll),
59 * so a producer that posts to a worker's event/defer queue must nudge it. This is
60 * a FreeRTOS task notification (no allocation, task- and tcpip-thread safe); a
61 * nudge that lands between the worker's pump and its block is latched in the
62 * notification count, so the next block returns at once and no event is missed.
63 * No-op on host (no worker task; the pipeline runs inline). Safe to call with an
64 * out-of-range id or before the task exists.
65 */
66void detws_worker_wake(int worker_id);
67
68/** @brief Signal the worker task(s) to exit and wait briefly for them. No-op on host. */
69void detws_workers_stop(void);
70
71/** @brief True while worker task(s) are running (always false on host). */
72bool detws_workers_running(void);
73
74// ---------------------------------------------------------------------------
75// Deferred work (thread-safe app -> worker submission)
76// ---------------------------------------------------------------------------
77//
78// Route a callback to a worker so it runs in that worker's single-thread context.
79// This is how application code on loop() (or any other task) safely pushes to a
80// connection - e.g. an SSE broadcast on a timer, or ws_send from a sensor task:
81// instead of calling the send API directly (which would race the worker that owns
82// the slot), wrap it in a small function and hand it to the owning worker. The
83// worker drains and runs deferred callbacks each service iteration.
84// (DetWebServer::defer(slot, fn, arg) is the app-facing wrapper that resolves the
85// slot's owner; this layer stays free of the transport/conn_pool dependency.)
86//
87// @p arg must remain valid until the callback runs (point it at static/global
88// state, or data you keep alive). On host builds (no worker task) the callback
89// runs inline immediately, so tests and loop()-driven code behave identically.
90
91/** @brief Deferred callback signature. */
92typedef void (*detws_deferred_fn)(void *arg);
93
94/** @brief Run @p fn(@p arg) on worker @p worker_id. Returns false if the queue is full. */
95bool detws_defer(int worker_id, detws_deferred_fn fn, void *arg);
96
97/** @brief Drain and run worker @p worker_id's deferred callbacks (called by the worker). */
98void detws_worker_run_deferred(int worker_id);
99
100#endif // DETERMINISTICESPASYNCWEBSERVER_WORKER_H
User-facing configuration for DeterministicESPAsyncWebServer.
void detws_worker_set_self(int id)
Bind the calling task/thread to worker id id (worker entry / tests).
Definition worker.cpp:33
int detws_worker_count(void)
Number of server worker tasks (DETWS_WORKER_COUNT).
Definition worker.cpp:23
int detws_worker_self(void)
Worker id [0, count) of the calling task; 0 by default / single-worker.
Definition worker.cpp:28
void(* detws_worker_pump_fn)(int worker_id)
Pump callback run by each worker task with its worker id.
Definition worker.h:50
void detws_workers_stop(void)
Signal the worker task(s) to exit and wait briefly for them. No-op on host.
Definition worker.cpp:162
bool detws_workers_running(void)
True while worker task(s) are running (always false on host).
Definition worker.cpp:172
void detws_workers_start(detws_worker_pump_fn pump)
Spawn the worker task(s) and start them running pump. No-op on host.
Definition worker.cpp:112
void(* detws_deferred_fn)(void *arg)
Deferred callback signature.
Definition worker.h:92
bool detws_defer(int worker_id, detws_deferred_fn fn, void *arg)
Run fn(arg) on worker worker_id. Returns false if the queue is full.
Definition worker.cpp:130
void detws_worker_wake(int worker_id)
Wake worker worker_id so it services a freshly-queued event now.
Definition worker.cpp:143
void detws_worker_run_deferred(int worker_id)
Drain and run worker worker_id's deferred callbacks (called by the worker).
Definition worker.cpp:152