queue: add drain for SegQueue#1243
Conversation
f2a9e20 to
6f20ef9
Compare
|
I feel like tests should also define what should happen in case someone runs
|
|
Thanks @laycookie for raising the mem::forget concern. I've updated the implementation so that the queue is reset to empty at Drain creation. Now if mem::forget is called, the queue is left in a consistent empty state (leaked but not corrupted). A new drain_mem_forget test covers this, skipped under Miri via #[cfg_attr(miri, ignore)] since the leaks are intentional by design. |
Summary
Closes #1228.
This PR adds a
drainmethod toSegQueue, as requested in issue #1228. The implementation uses&mut selfas suggested by @taiki-e, allowing all internal operations to usepush_mut()andpop_mut(), bypassing atomic operations entirely, consistent with the approach introduced in #1191.API
Accepts any
RangeBounds<usize>, supporting all standard range variants:drain(..)drain(..n)nelements, keep the restdrain(a..b)a, drain nextb-a, keep suffixdrain(a..)a, drain everything afterImplementation Notes
&mut selfandpush_mut()/pop_mut()Since
drainrequires exclusive access, all internal operations use exclusive versions ofpushandpopwhich avoids CAS loops, atomic loads/stores, and backoff — the same reasoning as in #1191.Performance note
The current implementation removes elements one by one via
pop_mut. This is equivalent in performance to callingpop_mutN times manually, so the primary benefit ofdrainhere is convenience and drop safety (elements outside the range are always preserved, and unconsumed elements in the range are dropped correctly whenDrainis dropped). A potential optimization would be block-level bulk removal, grabbing entire blocks at once when the drain range covers them fully. It's possible to be done inside this PR, or left for a follow-up.Prefix handling (
drain(a..b))At
Draincreation, the firstaelements are popped into a temporarySegQueue<T>calledprefix. The drain range is then yielded viapop_mut()on the original queue. OnDrain::drop, the suffix (whatever remains in the original queue after the range) is moved intoprefix, and thenmem::swapis called to restore the original queue toprefix + suffixin O(1).Drop safety
If
Drainis dropped before being fully consumed, the remaining elements within the range are dropped and blocks are freed viadestroy_mut. Elements outside the range are always preserved.size_hintandExactSizeIteratorCredit to @laycookie (the original issue author) for suggesting
size_hint/ExactSizeIteratorin their draft implementation shared in #1228.Testing
New tests cover:
drain(..))drain(..n))drain(a..b))drain(a..))BLOCK_CAP = 31)drain(n..n))