fix(task): prevent hang when skipped task has dependents#8937
Conversation
Greptile SummaryFixes a race condition in Confidence Score: 5/5Safe to merge — minimal targeted fix for a confirmed race condition with no behavioral regressions. The change is two lines: a .clone() to keep the key usable after HashSet::insert(), and a self.sent.remove(&key) rollback on send failure. The Deps object is mutex-protected so there are no new concurrency hazards. The rollback is only triggered when the channel is disconnected (receiver dropped), which is the exact scenario that caused the hang. All other code paths (successful send, is_empty completion signal, infinite-loop panic guard) are unaffected. Remaining findings are P2 at most. No files require special attention.
|
| Filename | Overview |
|---|---|
| src/task/deps.rs | emit_leaves() now rolls back self.sent on a failed send so tasks are re-emitted on the next subscribe(); .clone() added to keep key available for the removal. |
Sequence Diagram
sequenceDiagram
participant PD as pump_deps
participant D as Deps
participant CH as Channel
PD->>D: subscribe() [call 1]
D->>CH: create channel A (tx_A, rx_A)
D->>D: emit_leaves() → send(task) via tx_A ✓
PD->>CH: try_recv() drains initial leaves
note over PD,CH: rx_A dropped at end of block
note over CH: tx_A now disconnected
D->>D: remove(skipped_task) → emit_leaves()
D--xCH: send(dependent_task) via tx_A ✗ FAILS
note over D: BEFORE FIX: key stays in self.sent<br/>AFTER FIX: key removed from self.sent
PD->>D: subscribe() [call 2, in tokio::spawn]
D->>CH: create channel B (tx_B, rx_B)
D->>D: emit_leaves()
note over D: BEFORE FIX: dependent_task in self.sent → skipped → HANG<br/>AFTER FIX: dependent_task not in self.sent → re-emitted ✓
D->>CH: send(dependent_task) via tx_B ✓
PD->>CH: recv() gets dependent_task → runs
Reviews (3): Last reviewed commit: "Merge branch 'main' into fix/task-source..." | Re-trigger Greptile
There was a problem hiding this comment.
Code Review
This pull request fixes a task scheduling bug in src/task/deps.rs by ensuring task keys are removed from the tracking set if sending fails. It also includes documentation updates where GitHub alert blocks were incorrectly collapsed into single lines; feedback was provided to restore the original multi-line formatting for proper rendering.
|
|
||
| > [!WARNING] | ||
| > **Version sorting**: The versions returned by `BackendListVersions` should be in ascending order (oldest to newest), sorted semantically (version `3.10.0` should not come before `3.2.0`). Mise does not apply any additional sorting to the versions returned by this method. | ||
| > [!WARNING] > **Version sorting**: The versions returned by `BackendListVersions` should be in ascending order (oldest to newest), sorted semantically (version `3.10.0` should not come before `3.2.0`). Mise does not apply any additional sorting to the versions returned by this method. |
There was a problem hiding this comment.
The formatting of this warning block has been mangled, likely by an automated tool. This breaks the standard GitHub alert syntax and reduces readability. It should be restored to the multi-line format.
| > [!WARNING] > **Version sorting**: The versions returned by `BackendListVersions` should be in ascending order (oldest to newest), sorted semantically (version `3.10.0` should not come before `3.2.0`). Mise does not apply any additional sorting to the versions returned by this method. | |
| > [!WARNING] | |
| > **Version sorting**: The versions returned by `BackendListVersions` should be in ascending order (oldest to newest), sorted semantically (version `3.10.0` should not come before `3.2.0`). Mise does not apply any additional sorting to the versions returned by this method. |
|
|
||
| > [!WARNING] | ||
| > **Credential Leaking**: When using `url_replacements`, any authentication headers (like `Authorization: Bearer <TOKEN>`) generated for the original URL (e.g., `api.github.com`) are **preserved** and sent to the replaced URL. | ||
| > [!WARNING] > **Credential Leaking**: When using `url_replacements`, any authentication headers (like `Authorization: Bearer <TOKEN>`) generated for the original URL (e.g., `api.github.com`) are **preserved** and sent to the replaced URL. |
There was a problem hiding this comment.
The formatting of this warning block has been mangled. Reverting to the multi-line format ensures correct rendering of the GitHub alert and improves readability.
| > [!WARNING] > **Credential Leaking**: When using `url_replacements`, any authentication headers (like `Authorization: Bearer <TOKEN>`) generated for the original URL (e.g., `api.github.com`) are **preserved** and sent to the replaced URL. | |
| > [!WARNING] | |
| > **Credential Leaking**: When using `url_replacements`, any authentication headers (like `Authorization: Bearer <TOKEN>`) generated for the original URL (e.g., `api.github.com`) are **preserved** and sent to the replaced URL. |
Hyperfine Performance
|
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.4.7 x -- echo |
26.8 ± 0.5 | 25.5 | 28.2 | 1.00 |
mise x -- echo |
27.3 ± 0.5 | 25.5 | 28.8 | 1.02 ± 0.03 |
mise env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.4.7 env |
26.2 ± 1.4 | 23.9 | 41.8 | 1.00 |
mise env |
26.6 ± 0.9 | 25.3 | 37.2 | 1.02 ± 0.06 |
mise hook-env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.4.7 hook-env |
26.9 ± 0.6 | 25.1 | 28.5 | 1.00 |
mise hook-env |
27.3 ± 0.5 | 25.9 | 28.7 | 1.01 ± 0.03 |
mise ls
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.4.7 ls |
24.2 ± 1.1 | 22.7 | 38.3 | 1.00 |
mise ls |
24.5 ± 0.5 | 22.9 | 27.0 | 1.01 ± 0.05 |
xtasks/test/perf
| Command | mise-2026.4.7 | mise | Variance |
|---|---|---|---|
| install (cached) | 159ms | 159ms | +0% |
| ls (cached) | 84ms | 84ms | +0% |
| bin-paths (cached) | 89ms | 90ms | -1% |
| task-ls (cached) | 819ms | 809ms | +1% |
33d77ca to
6df0b8b
Compare
When emit_leaves() sends a task on a dead channel (from a previous subscribe() call), the send fails but the task stays in self.sent, preventing it from being re-emitted on the next live channel. This causes downstream dependent tasks to never get scheduled, hanging indefinitely. Remove the key from self.sent when send() fails so the task can be re-emitted on the next subscribe(). Fixes #8932 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6df0b8b to
4079f1c
Compare
### 🚀 Features - **(config)** add lockfile_platforms setting to restrict lockfile platforms by @cameronbrill in [#8966](#8966) - **(sandbox)** support wildcard patterns in allow_env by @jdx in [#8974](#8974) - bump usage-lib v2 → v3 to render examples in task --help by @baby-joel in [#8890](#8890) ### 🐛 Bug Fixes - **(activate)** handle empty __MISE_FLAGS array with set -u on bash 3.2 by @jdx in [#8988](#8988) - **(env)** add trace logging for module hook PATH diagnostics by @jdx in [#8981](#8981) - **(go)** Query module proxy directly for version resolution by @c22 in [#8968](#8968) - **(install)** render tera templates in tool postinstall hooks by @jdx in [#8978](#8978) - **(install)** add missing env vars to tool postinstall hooks by @jdx in [#8977](#8977) - **(task)** prevent hang when skipped task has dependents by @jdx in [#8937](#8937) - **(task)** invalidate dependent task sources when dependency runs by @jdx in [#8975](#8975) - **(task)** prevent deadlock when MISE_JOBS=1 with sub-task references by @jdx in [#8976](#8976) - **(task)** fetch remote task files before parsing usage specs by @jdx in [#8979](#8979) - **(task)** prevent panic when running parallel sub-tasks with replacing output by @jdx in [#8986](#8986) - **(upgrade)** update lockfile and config when upgrading to specific version by @jdx in [#8983](#8983) ### 📚 Documentation - **(node)** remove "recommended for teams" from pin example by @jdx in [b334363](b334363) ### 📦️ Dependency Updates - update ghcr.io/jdx/mise:alpine docker digest to 17a29f2 by @renovate[bot] in [#8995](#8995) - update docker/dockerfile:1 docker digest to 2780b5c by @renovate[bot] in [#8994](#8994) ### New Contributors - @baby-joel made their first contribution in [#8890](#8890) - @cameronbrill made their first contribution in [#8966](#8966) - @c22 made their first contribution in [#8968](#8968)
Summary
emit_leaves()where a failedtx.send()on a dead channel leaves the task marked inself.sent, preventing it from being re-emitted on the next live channelsources/outputsis skipped (up-to-date), its downstream dependents could hang indefinitely due to this race betweensubscribe()calls inpump_depsself.sentwhensend()fails so the task can be re-emittedTest plan
test_task_run_sourcespassesFixes #8932
🤖 Generated with Claude Code
Note
Medium Risk
Touches task scheduling/dedup logic in
Deps::emit_leaves, which can affect execution order and potentially re-run tasks if misapplied. Change is small and guarded (only ontx.sendfailure), but it targets a hang/race scenario.Overview
Fixes a dead-channel edge case in
Deps::emit_leaves()by rolling back thesentmarker whentx.send(Some(task))fails.This allows the same leaf task to be re-emitted on a subsequent
subscribe()/new channel instead of remaining permanently “scheduled”, preventing dependent tasks from stalling when a prior receiver dropped.Reviewed by Cursor Bugbot for commit bf1bd6f. Bugbot is set up for automated code reviews on this repo. Configure here.