Skip to content

fix race condition in histogram display#60

Merged
AlanRockefeller merged 3 commits intomainfrom
test
Mar 27, 2026
Merged

fix race condition in histogram display#60
AlanRockefeller merged 3 commits intomainfrom
test

Conversation

@AlanRockefeller
Copy link
Copy Markdown
Owner

@AlanRockefeller AlanRockefeller commented Mar 26, 2026

Summary by CodeRabbit

  • Bug Fixes

    • Improved histogram reliability with automatic detection and recovery from stuck jobs.
    • Enhanced retry logic for failed histogram calculations (up to 3 attempts) and reset of retry counters on new requests.
    • Fixed stale completions interfering with newer histogram requests and strengthened error handling so the UI accurately reflects histogram state.
    • Invalidate cached preview when indexes diverge to prevent mismatched previews.
  • New Features

    • Histogram window now forwards key presses into the app for consistent key handling.
    • Dragging now respects batch selections, dragging selected batches when present.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 26, 2026

Walkthrough

Adds 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

Cohort / File(s) Summary
Histogram worker & controller
faststack/app.py
Added inflight fields (_hist_inflight_since, _hist_inflight_token, _hist_inflight_future, _hist_null_retries, _hist_last_args); reset null-retry on new queue; detect/reset stuck inflight jobs (~10s); bind submitted token to futures and emit done with token; record worker start time at execution; match tokens before clearing inflight; retry None results up to 3 times when appropriate; added handle_key_from_histogram(...) Qt slot; changed start_drag_current_image() to prefer batch-selected images.
Histogram window key handling
faststack/qml/HistogramWindow.qml
Forward non-toggle key presses (key, modifiers, text) to controller.handle_key_from_histogram(...) and accept the event to prevent default/unhandled behavior.

Sequence Diagram

sequenceDiagram
    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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix race condition in histogram display' directly and concisely describes the main change: addressing race condition issues in the histogram functionality. The PR summary confirms extensive histogram lifecycle and inflight state management improvements.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread faststack/app.py Outdated
Comment on lines +5977 to +5979
self._hist_inflight = True
self._hist_inflight_token = token
self._hist_inflight_since = time.monotonic()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e4e549a5-dca0-483b-8021-ea141d72e9a0

📥 Commits

Reviewing files that changed from the base of the PR and between b9ee8c4 and e0bfeed.

📒 Files selected for processing (1)
  • faststack/app.py

Comment thread faststack/app.py Outdated
…am is open, and if a batch is selected, drag and drop just moves the batch instead of the current image too
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between e0bfeed and b0545d5.

📒 Files selected for processing (2)
  • faststack/app.py
  • faststack/qml/HistogramWindow.qml

Comment thread faststack/app.py Outdated
Comment thread faststack/app.py
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between b0545d5 and 10a6618.

📒 Files selected for processing (2)
  • faststack/app.py
  • faststack/qml/HistogramWindow.qml

Comment thread faststack/app.py
Comment on lines +5504 to +5515
# 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)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

@AlanRockefeller AlanRockefeller merged commit ac66a22 into main Mar 27, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant