fork: fix P0-2 dangling pointer + P0-3/P0-4 meta/race in hydra_config path#53
Conversation
… path Fixes #49 P0-2, P0-3, P0-4 (reviewed via ddvnguyen/hydra_vortex#415 E2E verification). P0-2: Replace dangling pointer in apply_t3_rebuild() override_tensor parsing with static std::list<string> for process-lifetime storage (matching the safe pattern in common/arg.cpp:274-277). P0-3/P0-4: Remove the inline hydra_config bootstrap from post_chat_completions (HTTP worker thread path that raced the main task-queue thread and never populated meta). All config now routes through the existing CONFIGURE(0x40) task-queue mechanism. Remove the && ctx_tgt guard from the CONFIGURE handler so it works on an empty engine. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Reviewed against #49/#50. Verified by checking out ✅ P0-2 (dangling pointer) — correctly fixed
|
… meta staleness Addresses review feedback on PR #53 (#53). Null-deref (review finding): Guard ctx_tgt->hydra_set_pending_config() with null check in CONFIGURE handler. When ctx_tgt is null (first load), T3 statics are already staged by hydra_apply_t3_mutators(); log instead of crash. P1-5 (hydra_metrics_result race): Remove the class-level hydra_metrics_result field. Build metrics inline from meta in handle_completions_impl — each completion gets fresh metrics from the current meta, eliminating the cross-request data leak. P1-6 (meta staleness after T3 rebuild): Add atomic needs_meta_refresh flag on server_context_impl. Set it after successful T3 rebuild in apply_pending_hydra_config(). The first HTTP request after rebuild atomically exchanges the flag and calls update_meta(), refreshing the cached server_context_meta. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Review feedback addressedAll three items from the review are fixed in commit : 1. Null-deref in CONFIGURE handler (new crash)Fixed. Added guard to call. When is null (first load), T3 statics are already staged by (which has inner null guards). Logs an info message instead of crashing. 2. P1-5 (hydra_metrics_result race)Fixed. Removed the class-level field entirely. Metrics are now built inline from in — each completion gets fresh metrics directly from the current , eliminating the cross-request data leak. 3. P1-6 (meta staleness after T3 rebuild)Fixed. Added atomic flag on . Set after successful T3 rebuild in . The first HTTP request after rebuild atomically exchanges the flag and calls , refreshing the cached . Still not addressed (P0-1 — separate work)The no-model bootstrap path on (P0-1) requires to fire when is null. Currently gate-checks which short-circuits. This needs a standalone pending-config storage for first-load — tracked in #52. |
Review feedback addressedAll three items from the review are fixed in commit 1. Null-deref in CONFIGURE handler (new crash)Fixed. Added 2. P1-5 (hydra_metrics_result race)Fixed. Removed the class-level 3. P1-6 (meta staleness after T3 rebuild)Fixed. Added atomic Still not addressed (P0-1 — separate work)The no-model bootstrap path on |
|
Re-reviewed the new commit (c4ddf48) against the null-deref/P1-5/P1-6 fixes. Verified by checking out ✅ Null-deref — fixed, but the fallback is a silent no-op
bool apply_pending_hydra_config() {
if (!ctx_tgt || !ctx_tgt->hydra_has_pending_config()) {
return false;
}
...
The CONFIGURE-based bootstrap needs an explicit trigger for the
|
Addresses review feedback on PR #53 (#53, comment 4951935492). Bug 1: CONFIGURE bootstrap was dead code — T3 mutator statics staged but apply_t3_rebuild() never called when ctx_tgt is null. Added direct apply_t3_rebuild() call in CONFIGURE handler for the first-load case (ctx_tgt == null, highest_tier == 3). Safe because there are no slots to drain and no concurrent readers when no model is loaded. Bug 2: P1-6 meta refresh was racing concurrent HTTP readers — moved update_meta() from HTTP thread to task-queue thread via routes_ptr back-pointer. apply_pending_hydra_config() now calls routes_ptr->refresh_meta() directly after successful T3 rebuild, during the drain window when no slots are processing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Review round 2 feedback addressedBoth items fixed in commit 1. CONFIGURE bootstrap — now actually triggers on first loadWhen
2. P1-6 meta race — moved to task-queue threadReplaced the HTTP-thread Removed the HTTP-thread Files changed (4)
|
|
Thanks for the thorough review — both issues you identified are already fixed in the subsequent commit Issue 1: Bootstrap now actually triggers on first loadIn commit // First load: ctx_tgt is null, so apply_pending_hydra_config()
// and update_slots() can't trigger (both gate on ctx_tgt).
// Call apply_t3_rebuild() directly — safe because this runs on
// the task-queue thread and there are no slots to drain.
if (!t2t3_subset.empty() && !ctx_tgt) {
SRV_INF("%s", "hydra: CONFIGURE first load — calling apply_t3_rebuild directly\n");
bool ok = apply_t3_rebuild();
...
}Thread safety note for first-load specifically: This code runs in the CONFIGURE HTTP handler (
If you'd prefer this to run on the task-queue thread instead, we could store the first-load config in a standalone variable (not on ctx_tgt) and have Issue 2: Meta race moved to task-queue threadThe HTTP-thread The HTTP-thread Latest commits on PR branchPlease re-review against |
… thread The CONFIGURE HTTP handler previously called apply_t3_rebuild() directly for first load (ctx_tgt == null). This ran on the httplib worker thread pool — the same unsafe thread class as the deleted inline bootstrap that caused P0-4. Now the CONFIGURE handler only sets a first_load_pending flag. The actual apply_t3_rebuild() call happens in update_slots() on the task-queue thread, which is the safe serialized thread for model state mutations. Changes: - Add first_load_pending bool to server_context_impl - CONFIGURE handler: set flag instead of calling apply_t3_rebuild - update_slots(): new else-if branch for !ctx_tgt && first_load_pending - apply_pending_hydra_config(): handle is_first_load path — skip drain timeout (no ctx_tgt timestamp), default tier to T3, skip pending_config read (T3 statics in global overrides), clear flag instead of clearing pending_config on ctx_tgt Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The previous P1-6 fix (refresh_meta from task-queue thread) still had a window where HTTP worker threads could read meta->chat_params/model_path while update_meta() was writing. Same bug class as P0-4, moved to meta. Fix: add std::shared_mutex meta_mutex to server_routes. update_meta() takes exclusive lock. All HTTP handlers that read meta take shared lock at entry. Covers 13 handlers + 2 functions that dereference meta->*. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Both issues fixed — ready for re-reviewTwo new commits address the reviewer's two remaining concerns: Commit
|
post_infill, post_chat_completions, post_responses_oai,
post_transcriptions_oai, and post_anthropic_messages each held a
shared_lock on meta_mutex and then called handle_completions_impl,
which takes its own shared_lock. Recursive shared_lock on
std::shared_mutex is UB (glibc pthread_rwlock_t is writer-preferring,
so this deadlocks when a concurrent exclusive lock is queued).
Fix: wrap the meta-reading pre-processing block in each handler in
its own {} scope so the shared_lock destructs before calling
handle_completions_impl.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Recursive shared_lock deadlock — fixedAll 5 affected handlers now scope their
Build: GREEN. The |
* fork: P0-1 head-bootstrap mode in llama-engine — reuse PR #48/#53 mechanism (hydra_vortex #49) When llama-engine starts with no --model but with --rpc-engine (indicating it's a head, not a peer), it now builds server_context + server_routes + Hydra RPC and waits for CONFIGURE(0x40) T3 to load the model. This reuses the existing first_load_pending / apply_pending_hydra_config() / apply_t3_rebuild() mechanism from PR #48/#53 — no parallel implementation. Key changes: - Add set_routes_ptr() public method to server_context (for head-bootstrap to wire routes_ptr without accessing impl directly) - Split no-model path in llama-engine.cpp: - !has_model && !has_peer: compute-only peer (unchanged) - !has_model && has_peer: head-bootstrap mode (new) - Head-bootstrap creates server_context + server_routes + Hydra RPC with empty backends, registers full HTTP inference routes - CONFIGURE T3 triggers apply_pending_hydra_config() via existing first_load_pending mechanism in update_slots() Verified has_peer signal: peer-only nodes (RTX 3060) never have --rpc-engine in their config, head-bootstrap nodes always do. * fork: P0-1 round 3 — fix is_ready, capabilities, null-meta guard (hydra_vortex #49) Three fixes for head-bootstrap mode: 1. ctx_http.is_ready now set immediately after start() — the server is ready from the moment HTTP starts, matching the compute-only-peer pattern. Individual routes handle the no-model-yet case themselves. 2. set_hydra_capabilities/set_hydra_combined_static called after deferred first-load via bootstrap_* fields staged at startup and applied in apply_pending_hydra_config() success path. Also registers local tensors, enables shared-backend compute lock, and updates RPC backends. 3. Null-meta guard in handle_completions_impl, post_chat_completions, and post_infill — returns 503 with clear message instead of crashing when meta is null (bootstrap window before first CONFIGURE). Also adds hydra_rpc::update_backends() for populating compute backends after deferred first-load. * fork: P0-1 complete null-meta guards across all meta-deref handlers (hydra_vortex #49) Added null-meta guards (returns 503 + clear message) to every handler that dereferences meta-> under meta_mutex: get_props, post_responses_oai, post_transcriptions_oai, post_anthropic_messages, post_anthropic_count_tokens, post_apply_template, get_models, get_model_info, post_rerank, handle_embeddings_impl Previously covered (round 3): handle_completions_impl, post_chat_completions, post_infill. The bootstrap window (is_ready=true, no model loaded yet) is now safe to expose to real traffic — all endpoints return a clear 503 instead of crashing on null deref. * fork: P0-1 add bootstrap_init() — required for head-bootstrap start_loop() (hydra_vortex #49) Without bootstrap_init(), the queue callbacks (on_new_task, on_update_slots, on_sleeping_state) are never wired up in head-bootstrap mode, causing std::bad_function_call crash when start_loop() tries to invoke them. bootstrap_init() wires up these callbacks + metrics.init() without requiring a model (no ctx_tgt/model_tgt assertions). Called before start_loop() in the head-bootstrap path. Verified via smoke test: - /health returns 200 {"status":"ok","mode":"bootstrap"} - /v1/chat/completions returns 501 "model not loaded — waiting for CONFIGURE" --------- Co-authored-by: Hydra Engineering <hydra-engineering@local>
Fixes #49 P0-2, P0-3, P0-4.
Reviewed via ddvnguyen/hydra_vortex#415 E2E verification (Jul 12, 2026).
Changes
P0-2: Dangling pointer in apply_t3_rebuild() (server-context.cpp:4338)
P0-3/P0-4: Meta never populated + thread race (server-context.cpp:6633-6689)
Verification
Cross-repo