Skip to content

Conversation

@lalinsky
Copy link
Owner

@lalinsky lalinsky commented Aug 24, 2025

Summary

  • Add QueueClosed error to PopError enum
  • Fix pop() method to properly handle queue closure in wait loop
  • Fix waitAndGetSlice() method with same closure handling
  • Add fast path to pop() for non-blocking calls (timeout_ms == 0) to avoid timedWait overhead
  • Add comprehensive tests to verify blocking operations handle queue closure

Problem

Previously, when a queue was closed while threads were waiting in blocking operations (pop() with timeout or waitAndGetSlice()), the threads would either hang indefinitely or timeout instead of immediately returning with an appropriate error.

Additionally, non-blocking pop() calls (timeout_ms == 0) were unnecessarily calling timedWait which adds overhead.

Solution

  1. Modified the wait loops to check both items_available == 0 and !is_closed, and return PopError.QueueClosed when the queue is closed with no available items
  2. Added fast path for non-blocking pop operations to immediately check queue state without calling timedWait

Test plan

  • All existing tests pass
  • New tests verify proper closure handling in blocking operations
  • Tests confirm threads wake up immediately when queue is closed
  • Fast path optimization preserves existing behavior for non-blocking calls

Summary by CodeRabbit

  • New Features

    • Queues can be closed and queried for closed/data state; buffers expose the same timed retrieval semantics.
    • Added a non-blocking retrieval option and support for finite, non-blocking, and indefinite waits.
  • Bug Fixes

    • Pop and slice retrieval now report “queue closed” vs “empty” correctly across non-blocking, timed, and blocking waits.
    • Timed waits return “empty” on timeout and “closed” when closed with no items.
  • Tests

    • Added coverage for closure behavior and new timeout/wait scenarios.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 24, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Adds QueueClosed to PopError/PushError; introduces timed and non-blocking getSlice/pop semantics (including tryGetSlice), queue lifecycle APIs (close/isClosed/hasData/getItemsAvailable), and updates buffer wrapper and tests to handle closure and timeouts.

Changes

Cohort / File(s) Summary of changes
Queue core & errors
src/queue.zig
Added PopError.QueueClosed and PushError.QueueClosed; revised pop to distinguish non-blocking (0), timed, and indefinite waits; return QueueClosed when closed and empty; timeout returns QueueEmpty.
Slice API (get/try)
src/queue.zig
Replaced waitAndGetSlice with getSlice(self, timeout_ms) PopError!View supporting 0 / finite / maxInt timeouts; added tryGetSlice(self) as getSlice(0) wrapper; introduced getSlice fast-paths and timed-wait logic.
ConcurrentWriteBuffer wrapper
src/queue.zig
Replaced buffer waitAndGetSlice with getSlice(timeout_ms) that forwards to underlying queue; honors same PopError semantics.
Lifecycle & introspection APIs
src/queue.zig
Added pub fn close(self: *Self) void, pub fn isClosed(self: *Self) bool, pub fn hasData(self: *Self) bool, and pub fn getItemsAvailable(self: *Self) usize.
Tests
test/...
Updated/added tests for queue closure, blocking pop on close, getSlice with indefinite wait, timed-wait behavior, and buffer-related scenarios.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant C as Caller
  participant Q as ConcurrentQueue
  participant B as CondVar/Wait
  participant T as Timer

  rect rgba(230,245,255,0.6)
  Note over C,Q: Non-blocking pop / tryGetSlice (timeout_ms = 0)
  C->>Q: pop(0) / tryGetSlice()
  alt has item(s)
    Q-->>C: return item / View
  else closed AND empty
    Q-->>C: error QueueClosed
  else no items
    Q-->>C: error QueueEmpty
  end
  end

  rect rgba(235,255,235,0.6)
  Note over C,Q: Timed wait (timeout_ms > 0)
  C->>Q: pop(timeout_ms) / getSlice(timeout_ms)
  loop wait until item, closed, or timeout
    Q->>B: timedWait()
    B->>T: start/track timer
    T-->>B: wake on timeout or signal
    B-->>Q: wake
  end
  alt item available
    Q-->>C: return item / View
  else closed AND empty
    Q-->>C: error QueueClosed
  else timeout
    Q-->>C: error QueueEmpty
  end
  end

  rect rgba(255,245,230,0.6)
  Note over C,Q: Indefinite wait (timeout_ms = maxInt)
  C->>Q: getSlice(maxInt)
  loop until item or closed
    Q->>B: wait()
    B-->>Q: wake
  end
  alt item available
    Q-->>C: return View
  else closed AND empty
    Q-->>C: error QueueClosed
  end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–30 minutes

Possibly related PRs

Poem

I nibble bytes and hop in queues,
Timeout ticks and wakeful views —
Closed doors I mark with gentle thud,
Empty hops in twilight mud.
Slices found, I drum my feet — hooray! 🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 467d32b and 0ae5d1f.

📒 Files selected for processing (1)
  • src/queue.zig (6 hunks)
✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/queue-closure-blocking-operations

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@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: 0

🧹 Nitpick comments (4)
src/queue.zig (4)

6-9: Adding QueueClosed to PopError is a sensible API improvement; confirm downstream impact and update docs

This changes the public error set of pop-related APIs. Callers that pattern-match on error sets (e.g., switch over PopError) may require updates. Please:

  • Document the new error in pop() and waitAndGetSlice() doc comments.
  • Note the change in the changelog and consider semver implications if this is a published library.

285-296: Bound total wait time and guard against overflow in timeout conversion

Current loop reuses the same relative timeout on each iteration. With spurious wakeups, total wait can exceed timeout_ms. Also, timeout_ms * ns_per_ms can overflow u64. Recommend tracking a deadline and recomputing the remaining time each iteration, with overflow-safe multiplication.

Apply this diff:

-                const timeout_ns = timeout_ms * std.time.ns_per_ms;
-
-                while (self.items_available == 0 and !self.is_closed) {
-                    self.data_cond.timedWait(&self.mutex, timeout_ns) catch {
-                        return PopError.QueueEmpty;
-                    };
-                }
+                const timeout_ns = std.math.mul(u64, timeout_ms, std.time.ns_per_ms) catch std.math.maxInt(u64);
+                const deadline: i128 = std.time.nanoTimestamp() + @as(i128, timeout_ns);
+
+                while (self.items_available == 0 and !self.is_closed) {
+                    const now: i128 = std.time.nanoTimestamp();
+                    if (now >= deadline) return PopError.QueueEmpty;
+                    const remaining_i128 = deadline - now;
+                    const remaining_ns = std.math.cast(u64, remaining_i128) orelse 0;
+                    self.data_cond.timedWait(&self.mutex, remaining_ns) catch {
+                        return PopError.QueueEmpty;
+                    };
+                }

329-336: Constrain waitAndGetSlice error set to PopError for a tighter API

waitAndGetSlice currently returns an inferred error set. Since it now returns PopError.QueueClosed, consider making the signature explicit to avoid leaking anyerror to callers:

pub fn waitAndGetSlice(self: *Self) PopError!View { ... }

Also, update the doc comment to mention it may return QueueClosed when the queue is closed and empty.


720-743: Add a small test for the non-blocking fast path on closed+empty

To fully cover the new fast path, add a test that calls pop(0) on a closed, empty queue and expects QueueClosed:

test "non-blocking pop returns QueueClosed on closed empty queue" {
    const allocator = std.testing.allocator;

    const Queue = ConcurrentQueue(i32, 4);
    var queue = Queue.init(allocator, .{});
    defer queue.deinit();

    queue.close();

    const result = queue.pop(0);
    try std.testing.expectError(PopError.QueueClosed, result);
}

Also applies to: 744-767

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a2fd523 and 467d32b.

📒 Files selected for processing (1)
  • src/queue.zig (4 hunks)
🔇 Additional comments (4)
src/queue.zig (4)

276-284: Non-blocking fast path is clean and correct

Good early-exit logic: closed+empty returns QueueClosed; otherwise empty returns QueueEmpty; avoids timedWait overhead for timeout_ms == 0. Nice win for hot paths.


329-336: Correct closure handling in waitAndGetSlice

The loop condition and post-loop check are right: threads wake promptly on close and return QueueClosed when empty. Good fix.


720-743: Test: blocking pop handles queue closure — solid coverage

This exercises the wake-on-close path and asserts the new QueueClosed error. Looks good.


744-767: Test: waitAndGetSlice handles queue closure — solid coverage

This complements the pop() test and validates the slice path. Looks good.

- Add QueueClosed error to PopError enum
- Fix pop() method to check closure state in wait loop and return QueueClosed when queue is closed with no items
- Add fast path to pop() for non-blocking calls (timeout_ms == 0) to avoid timedWait overhead
- Replace waitAndGetSlice() with unified getSlice(timeout_ms) API for consistency with pop()
- Fix getSlice() method with same closure handling and fast path optimization
- Update tryGetSlice() to use getSlice(0) implementation
- Update ConcurrentWriteBuffer to use new getSlice API
- Add comprehensive tests to verify blocking operations handle queue closure properly

This prevents threads from hanging indefinitely when the queue is closed while waiting, optimizes non-blocking operations, and provides a cleaner, more consistent API.
@lalinsky lalinsky force-pushed the fix/queue-closure-blocking-operations branch from 467d32b to 0ae5d1f Compare August 24, 2025 17:39
@lalinsky lalinsky merged commit 36d97d7 into main Aug 24, 2025
1 of 2 checks passed
@lalinsky lalinsky deleted the fix/queue-closure-blocking-operations branch August 24, 2025 17:41
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