fix race condition in histogram display#60
Conversation
WalkthroughAdds robust inflight tracking, tokened completions, and bounded retry for histogram computation; forwards histogram-window key events into the main key handler; and changes drag-start logic to prefer batch-selected images when dragging. Changes
Sequence DiagramsequenceDiagram
actor UI
participant App
participant Worker
UI->>App: update_histogram() / visibility change / key event
activate App
App->>App: Check inflight state (age, token)
alt inflight stuck (>10s) or stale
App->>App: Force-reset inflight tracking, preserve last args
end
App->>Worker: _kick_histogram_worker(submitted_token, args)
deactivate App
activate Worker
Worker->>Worker: Start compute (record start time)
alt compute returns result
Worker->>App: _on_histogram_done(result, submitted_token)
else compute returns None or raises
Worker->>App: _on_histogram_done(None/exception, submitted_token)
end
deactivate Worker
activate App
App->>App: _apply_histogram_result(result, submitted_token)
alt submitted_token == _hist_inflight_token
alt result is not None
App->>App: reset _hist_null_retries, clear inflight, update UI
else result is None and no pending work and retries < 3
App->>App: increment _hist_null_retries, re-queue _hist_last_args
else
App->>App: clear inflight or ignore per token check
end
else
App->>App: ignore stale completion
end
deactivate App
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e0bfeedf28
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| self._hist_inflight = True | ||
| self._hist_inflight_token = token | ||
| self._hist_inflight_since = time.monotonic() |
There was a problem hiding this comment.
Start watchdog timer when histogram job actually begins
The stuck-job watchdog uses _hist_inflight_since set at submission time, but this executor is single-threaded (max_workers=1), so jobs can sit queued behind an earlier slow task. In that case, _kick_histogram_worker can see elapsed > 10s for a queued (not hung) job, force-reset inflight, and issue newer tokens; when the queued job eventually completes its result is treated as stale (token != _hist_token), which can cause repeated churn and delayed/missed histogram updates after one long-running computation.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@faststack/app.py`:
- Around line 5956-5979: The force-reset logic for _hist_inflight currently
clears and resubmits even when the original histogram job is still running in
the single-worker _hist_executor, which merely queues a duplicate behind the
hung job; instead, record the Future when scheduling (e.g. assign the returned
Future to self._hist_inflight_future when submitting to _hist_executor) and in
this block check that self._hist_inflight_future is done() before
force-resetting/resubmitting; only clear _hist_inflight/_hist_inflight_token and
reschedule _hist_pending if the stored Future is done(), otherwise do not reset
and consider scheduling executor replacement logic (recreate _hist_executor) if
you need true hung-worker recovery; also update _apply_histogram_result to
clear/ignore self._hist_inflight_future consistently using the inflight token.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
…am is open, and if a batch is selected, drag and drop just moves the batch instead of the current image too
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@faststack/app.py`:
- Around line 6173-6196: The None-retry branch can act on stale completions;
restrict it to the current histogram token by adding a token check (same gate
used for setting histogram) so the retry logic only runs when token ==
self._hist_token; update the condition that tests
pending/hist/isHistogramVisible to also require token == self._hist_token before
incrementing self._hist_null_retries and assigning self._hist_pending =
self._hist_last_args (all inside the self._hist_lock guarded block).
- Around line 778-784: handle_key_from_histogram currently rebuilds a minimal
QKeyEvent and calls self.keybinder.handle_key_press() directly, bypassing
eventFilter() and the controller logic (escape-to-close, crop/editor gating,
grid pass-through) and losing event.text(); change handle_key_from_histogram to
accept and forward the full original QKeyEvent (including text) into the same
controller path as the main window by routing the event through eventFilter() or
calling the same controller handler that eventFilter() uses, and update the
QML-to-Python call to supply the full event text so faststack/ui/keystrokes.py
fallbacks still work; specifically modify handle_key_from_histogram, stop
reconstructing the event, and ensure it invokes the same handler used by
eventFilter() (rather than calling keybinder.handle_key_press() directly).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 0e1997b4-88aa-4705-af69-4b59ebbbf109
📒 Files selected for processing (2)
faststack/app.pyfaststack/qml/HistogramWindow.qml
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@faststack/app.py`:
- Around line 5504-5515: The current batch-selection logic in
start_drag_current_image() uses any non-empty self.batches and therefore selects
batches unrelated to the current image; change it to first check whether
self.current_index falls inside any defined batch range before adding that
batch's indices to files_to_drag, otherwise fall back to adding only
self.current_index. Concretely: iterate self.batches locating a batch where
start <= self.current_index <= end, and if found collect indices from that batch
into files_to_drag (using existing bounds checks against len(self.image_files));
if no containing batch is found, add self.current_index as before and do not
touch batch state.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 2485209d-52b6-4222-956c-400f8fb349a2
📒 Files selected for processing (2)
faststack/app.pyfaststack/qml/HistogramWindow.qml
| # Collect files to drag: batch files if any batches exist, otherwise current image | ||
| files_to_drag = set() | ||
| files_to_drag.add(self.current_index) | ||
|
|
||
| # Add all files from defined batches | ||
| for start, end in self.batches: | ||
| for idx in range(start, end + 1): | ||
| if 0 <= idx < len(self.image_files): | ||
| files_to_drag.add(idx) | ||
|
|
||
| # Only include current image if no batch files were selected | ||
| if not files_to_drag: | ||
| files_to_drag.add(self.current_index) |
There was a problem hiding this comment.
Only switch to batch drag when the current image is actually in a batch.
As written, any non-empty self.batches makes start_drag_current_image() drag those batch ranges instead of the current image. That means a stale batch elsewhere in the list can hijack a drag from an unrelated image, and on success you'll mark the wrong files as uploaded and clear the batch state. Gate the batch path on self.current_index belonging to a batch; otherwise fall back to the current image.
♻️ Suggested fix
- # Collect files to drag: batch files if any batches exist, otherwise current image
- files_to_drag = set()
-
- # Add all files from defined batches
- for start, end in self.batches:
- for idx in range(start, end + 1):
- if 0 <= idx < len(self.image_files):
- files_to_drag.add(idx)
-
- # Only include current image if no batch files were selected
- if not files_to_drag:
- files_to_drag.add(self.current_index)
+ files_to_drag = set()
+ current_in_batch = any(
+ start <= self.current_index <= end for start, end in self.batches
+ )
+
+ if current_in_batch:
+ for start, end in self.batches:
+ for idx in range(start, end + 1):
+ if 0 <= idx < len(self.image_files):
+ files_to_drag.add(idx)
+ else:
+ files_to_drag.add(self.current_index)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@faststack/app.py` around lines 5504 - 5515, The current batch-selection logic
in start_drag_current_image() uses any non-empty self.batches and therefore
selects batches unrelated to the current image; change it to first check whether
self.current_index falls inside any defined batch range before adding that
batch's indices to files_to_drag, otherwise fall back to adding only
self.current_index. Concretely: iterate self.batches locating a batch where
start <= self.current_index <= end, and if found collect indices from that batch
into files_to_drag (using existing bounds checks against len(self.image_files));
if no containing batch is found, add self.current_index as before and do not
touch batch state.
Summary by CodeRabbit
Bug Fixes
New Features