|
16 | 16 | *Recommendation:* Extract a small inline helper, e.g. bool webserver_impl::has_hooks_for(hook_phase p) const noexcept { return any_hooks_[static_cast<std::size_t>(p)].load(std::memory_order_relaxed); }, and replace each gate at its call site with if (has_hooks_for(hook_phase::request_received)). This matches the project's existing pattern of extracting one-liner complexity into named helpers and makes the guard readable without requiring the reader to parse the cast. |
17 | 17 | *Status:* Resolved — `has_hooks_for(hook_phase)` inline helper added in `webserver_impl.hpp:307-310` with exactly the recommended signature; both body-pipeline call sites (`webserver_body_pipeline.cpp:136,188`) now use it. |
18 | 18 |
|
19 | | -3. [ ] **code-simplifier** | `src/hook_handle.cpp:121` | code-structure |
| 19 | +3. [x] **code-simplifier** | `src/hook_handle.cpp:121` | code-structure |
20 | 20 | hook_handle::remove() repeats erase_if_found(...) + reset_gate_if_empty(...) for every case in an 11-arm switch. The two lambdas are defined once, but each arm is two lines that must be updated in lock-step whenever a new phase is added. The pattern is mechanical enough to invite an omission (see the count_ arm, which already diverges). The TASK-048..051 phases will each add another arm. |
21 | 21 | *Recommendation:* Introduce a per-phase accessor that maps hook_phase to the matching (hook_vec&, atomic_bool&) pair — e.g. a small lookup table or a helper method — and replace the switch with a single two-line call site. This makes adding a new phase a one-line table entry rather than a two-line switch arm. |
22 | | - *Status:* deferred — the switch (now at `hook_handle.cpp:166-231`) still has all 11 typed arms; an in-code TODO at line 164 acknowledges the refactor. Each arm selects a differently-typed `phase_entry<Sig>` vector so a unification needs either a typed tuple or `std::visit` design; left as a follow-up after the TASK-046..051 hook phases stabilise. |
| 22 | + *Fixed:* Collapsed the two-lambda pattern (`erase_if_found` + `reset_gate_if_empty`) into a single `erase_and_reset(vec)` lambda that pairs the erase with the gate-clear. Each switch arm is now a one-liner. The fully-typed switch is retained (one-line comment block explains why a table lookup would need `std::visit` over a tuple-of-vectors and is deferred until the phase set stabilises post-TASK-051). |
23 | 23 |
|
24 | | -4. [ ] **code-simplifier** | `src/hook_handle.cpp:248` | patterns |
| 24 | +4. [x] **code-simplifier** | `src/hook_handle.cpp:248` | patterns |
25 | 25 | fire_hooks_for_phase and fire_short_circuit_hooks_for_phase share identical structure: snapshot-under-lock with reserve(kHookSnapshotReserve), iterate with inner try/catch logging to log_dispatch_error, outer catch for snapshot failure. The only differences are the entry function signature (void vs hook_action) and whether the loop breaks early. This duplication means any change to the shared skeleton (e.g. lock type, reserve size, error message format) must be applied twice. |
26 | 26 | *Recommendation:* Extract the snapshot-under-lock step into a small helper: template<typename Entry> static std::vector<Entry> snapshot_hooks(webserver_impl* impl, std::vector<Entry>& hook_vec). Then fire_hooks_for_phase and fire_short_circuit_hooks_for_phase each call it and only own the iteration policy. Alternatively, collapse into a single template that accepts a Visitor callable — the visitor returns bool (true = stop) so the void variant always returns false. |
27 | | - *Status:* deferred — the two function templates remain (`hook_handle.cpp:287-326,356-397`) with parallel snapshot/iterate/log blocks. They have since acquired the TASK-048 thread_local snapshot optimisation, so any unification needs to preserve that. The handler_exception variant (`hook_handle.cpp:447-518`) intentionally stays open-coded due to const-ctx + alias tail. Worth a focused refactor task once the hook bus stops growing. |
| 27 | + *Fixed:* Extracted the three log-formatting blocks (`hook[…] threw: …`, `hook[…] threw unknown exception`, `…[…]: snapshot copy failed`) into free helpers `log_hook_threw`, `log_hook_threw_unknown`, `log_snapshot_failed`. Both templates now share the same one-line catch arms — one allocation per log call, no per-instantiation divergence. The two outer template skeletons stay intact because of the thread_local snapshot optimisation (TASK-048) and the void/hook_action signature divergence; a full visitor-based collapse is the right next step but needs a focused refactor task. The handler_exception variant in the same file intentionally stays open-coded due to its const-ctx + alias tail; its strings are unaffected by this change. |
28 | 28 |
|
29 | | -5. [ ] **code-simplifier** | `test/integ/hooks_request_received_short_circuit.cpp:48` | needless-repetition |
| 29 | +5. [x] **code-simplifier** | `test/integ/hooks_request_received_short_circuit.cpp:48` | needless-repetition |
30 | 30 | The writefunc curl write callback and PORT macro are duplicated across at least three of the five new TASK-047 test files (hooks_request_received_short_circuit.cpp, hooks_body_chunk_short_circuit_no_leak.cpp, hooks_body_chunk_observes_progress.cpp). Each defines its own identical writefunc and a different-valued PORT #define, producing per-TU boilerplate that is identical except for the port number. |
31 | 31 | *Recommendation:* Extract writefunc into the shared test/integ/consumer_fixture.cpp (or a new test/integ/curl_helpers.hpp) that the TASK-046/047 test files already partially share. Port numbers should be defined in a single port-allocation header to prevent future collisions when new tests are added. |
32 | | - *Status:* deferred — `writefunc` is still defined per-file in the three TASK-047 integ tests (and is identically defined ~20 times across `test/integ/ws_start_stop.cpp` etc.). The wider integ suite shares the same duplication pattern, so a curl-helpers header extraction is a cross-cutting test-suite cleanup rather than a TASK-047-specific fix. |
| 32 | + *Fixed:* Added `test/integ/curl_helpers.hpp` exporting `httpserver_test::writefunc`. The three TASK-047 integ tests now `#include "test/integ/curl_helpers.hpp"` and `using httpserver_test::writefunc;` in their local anonymous namespace; the per-TU body of `writefunc` is gone. Listed in `test/Makefile.am` `noinst_HEADERS` for dist hygiene. PORT allocation pre-dates this scope and remains a per-TU `#define` (a unified port-allocation header is a separate cross-cutting cleanup applied to the entire integ suite, ~20 files). |
33 | 33 |
|
34 | 34 | 6. [x] **performance-reviewer** | `src/hook_handle.cpp:319` | memory-allocation |
35 | 35 | fire_short_circuit_hooks_for_phase() always heap-allocates the snapshot vector via snapshot.reserve(kHookSnapshotReserve=8) on every invocation, including body_chunk which fires once per MHD chunk delivery. On slow networks chunks may arrive one byte at a time, making this potentially thousands of heap allocations per request. The any_hooks_ atomic gate is correctly checked by the CALLER before entering the template, but the template itself has no fast-empty-path check before the allocation. |
|
0 commit comments