2020
2121// hook_phase_dispatch.cpp -- per-phase hook firing (the fire_* family).
2222//
23- // Carved out of src/hook_handle.cpp in TASK-086 to keep both translation
24- // units under the project per-file LOC ceiling (FILE_LOC_MAX in
23+ // Split out of src/hook_handle.cpp to keep both translation units under
24+ // the project per-file LOC ceiling (FILE_LOC_MAX in
2525// scripts/check-file-size.sh). hook_handle.cpp retains the hook_handle
2626// lifetime ops (move/dtor/detach/remove and the per-phase erase
2727// templates); this file holds the dispatch helpers and every
28- // detail::webserver_impl::fire_* member. No behaviour change: the bodies
29- // are moved verbatim.
28+ // detail::webserver_impl::fire_* member.
3029
3130#include < exception>
3231#include < optional>
4443
4544namespace httpserver {
4645
47- // ---- fire_* (TASK-046) ------- --------------------------------------------
46+ // ---- fire_*: lifecycle phases --------------------------------------------
4847//
49- // Per the plan: snapshot the phase vector under a shared_lock, release
50- // the lock, iterate the snapshot inside a try/catch routed through
51- // log_dispatch_error. Mirrors the TASK-027 route-cache promotion pattern
52- // (shared_lock + atomic gate). Reentrancy: a hook may call
53- // ws.add_hook() / handle.remove() because we no longer hold the table
54- // lock by the time the user code runs.
55-
56- // (Previously an unused kHookSnapshotReserve constant lived here.
57- // The thread_local snapshot buffers below are now sized lazily on
58- // first use; the constant was dead code as of TASK-048's perf rework.
59- // Removed in TASK-049 to silence -Wunused-const-variable under
60- // --enable-debug.)
48+ // Snapshot the phase vector under a shared_lock, release the lock,
49+ // iterate the snapshot inside a try/catch routed through
50+ // log_dispatch_error. Reentrancy: a hook may call ws.add_hook() /
51+ // handle.remove() because we no longer hold the table lock by the time
52+ // the user code runs.
6153
6254// fire_hooks_for_phase: shared dispatch template for all void-returning
6355// lifecycle hook phases. Snapshots the caller-supplied vector under a
@@ -68,12 +60,12 @@ namespace httpserver {
6860// callers are noexcept and will std::terminate on uncaught throws; the
6961// inner catches ensure no exception can propagate.
7062//
71- // TASK-048 perf : the snapshot vector is thread_local so the per-template-
63+ // Perf : the snapshot vector is thread_local so the per-template-
7264// instantiation per-thread buffer is reused across calls — no heap
7365// allocation after the first request on each thread (warm path).
7466
7567// One-allocation log helpers shared by fire_hooks_for_phase and
76- // fire_short_circuit_hooks_for_phase (TASK-049 review) . Free helpers
68+ // fire_short_circuit_hooks_for_phase. Free helpers
7769// (not lambdas captured per-instance) so the two templates' inner
7870// catch blocks can stay one-liners without divergent strings.
7971static void log_hook_threw (detail::webserver_impl* impl,
@@ -144,7 +136,7 @@ void detail::webserver_impl::fire_connection_closed(
144136 fire_hooks_for_phase (this , hooks_connection_closed_, ctx, " connection_closed" );
145137}
146138
147- // ---- fire_* (TASK-047) ----------------------- ----------------------------
139+ // ---- fire_*: pre-handler short-circuit phases ----------------------------
148140//
149141// Short-circuit-capable firing helpers. Mirrors fire_hooks_for_phase but:
150142// - the entry's std::function returns hook_action,
@@ -153,9 +145,9 @@ void detail::webserver_impl::fire_connection_closed(
153145// extracted http_response.
154146//
155147// A throwing hook is caught + logged and treated as if it had returned
156- // hook_action::pass() -- same DR-009 §5.2 routing as the void variant.
148+ // hook_action::pass() -- same exception routing as the void variant.
157149//
158- // TASK-048 perf : thread_local snapshot buffer (same rationale as
150+ // Perf : thread_local snapshot buffer (same rationale as
159151// fire_hooks_for_phase above).
160152template <typename Ctx>
161153static std::optional<::httpserver::http_response>
@@ -208,7 +200,7 @@ detail::webserver_impl::fire_body_chunk(
208200 this , hooks_body_chunk_, ctx, " body_chunk" );
209201}
210202
211- // ---- fire_* (TASK-048) ---------------------- -----------------------------
203+ // ---- fire_*: route_resolved + before_handler -----------------------------
212204
213205void detail::webserver_impl::fire_route_resolved (
214206 const ::httpserver::route_resolved_ctx& ctx) noexcept {
@@ -222,7 +214,7 @@ detail::webserver_impl::fire_before_handler(
222214 this , hooks_before_handler_, ctx, " before_handler" );
223215}
224216
225- // ---- fire_* (TASK-049) -------- -------------------------------------------
217+ // ---- fire_*: handler_exception -------------------------------------------
226218//
227219// handler_exception is the only short-circuit-capable phase whose ctx is
228220// passed as `const&` (the user cannot mutate the in-flight exception or
@@ -235,10 +227,16 @@ detail::webserver_impl::fire_before_handler(
235227// try/catch -- with an extra tail that invokes the alias slot after the
236228// user vector is exhausted.
237229//
238- // Structural differences preventing template reuse (findings #12, #13) :
230+ // Structural differences vs fire_short_circuit_hooks_for_phase :
239231// 1. ctx is `const Ctx&` (not `Ctx&`) -- the template takes mutable.
240232// 2. The alias tail (handler_exception_alias_) sits AFTER the user
241233// vector and has its own throw-containment block.
234+ // 3. No empty-vector early-return under the lock: the template bails
235+ // out before copying when the phase vector is empty; here the
236+ // snapshot copy runs unconditionally. Behaviourally irrelevant --
237+ // copying an empty vector into the already-cleared thread_local
238+ // buffer allocates nothing, and control must fall through to the
239+ // alias tail regardless (it fires even with zero user hooks).
242240// If the template is extended to support const-ctx or alias-tail in a
243241// future task, fire_handler_exception should be collapsed into it and
244242// the rationale comment updated accordingly.
@@ -248,7 +246,7 @@ detail::webserver_impl::fire_handler_exception(
248246 using EntryVec = std::vector<phase_entry<
249247 ::httpserver::hook_action (
250248 const ::httpserver::handler_exception_ctx&)>>;
251- // Per-thread cost note (finding #30) : this thread_local EntryVec is a
249+ // Per-thread cost note: this thread_local EntryVec is a
252250 // SECOND per-thread snapshot buffer in addition to the one inside
253251 // fire_short_circuit_hooks_for_phase. Both buffers are warm after the
254252 // first invocation on a given thread (no heap allocation on subsequent
@@ -280,13 +278,13 @@ detail::webserver_impl::fire_handler_exception(
280278 }
281279 // Tail: invoke the alias slot, if any. Read without synchronisation:
282280 // the slot is single-writer-at-construction, immutable after
283- // webserver::start() (DR-012 / §4.10) .
281+ // webserver::start().
284282 //
285283 // Throw containment: a throwing alias is logged with the legacy
286- // "internal_error_handler threw" prefix so the DR-009 §5.2 point 4
287- // log contract (and its tests in basic.cpp) is preserved verbatim
288- // even though the call site has moved from
289- // run_internal_error_handler_safely into the hook chain.
284+ // "internal_error_handler threw" prefix so the error-log contract
285+ // (and its tests in basic.cpp) is preserved verbatim even though
286+ // the call site has moved from run_internal_error_handler_safely
287+ // into the hook chain.
290288 if (handler_exception_alias_) {
291289 try {
292290 auto action = handler_exception_alias_ (ctx);
@@ -305,7 +303,7 @@ detail::webserver_impl::fire_handler_exception(
305303 return std::nullopt ;
306304}
307305
308- // ---- fire_* (TASK-050) ---------- -----------------------------------------
306+ // ---- fire_*: post-handler phases -----------------------------------------
309307//
310308// after_handler is the post-handler short-circuit. Returns engaged
311309// optional iff a hook short-circuited with respond_with(); the caller
@@ -320,13 +318,13 @@ detail::webserver_impl::fire_after_handler(
320318}
321319
322320// response_sent: void-returning user hooks, then the log_access_alias_
323- // slot. Same alias-tail pattern as fire_handler_exception (TASK-049) .
321+ // slot. Same alias-tail pattern as fire_handler_exception.
324322void detail::webserver_impl::fire_response_sent (
325323 const ::httpserver::response_sent_ctx& ctx) noexcept {
326324 fire_hooks_for_phase (this , hooks_response_sent_, ctx, " response_sent" );
327325 // Tail: invoke the alias slot, if any. Read without synchronisation:
328326 // the slot is single-writer-at-construction, immutable after
329- // webserver::start() (DR-012 / §4.10) .
327+ // webserver::start().
330328 if (log_access_alias_) {
331329 try {
332330 log_access_alias_ (ctx);
0 commit comments