kvbm_engine/offload/
queue.rs1use std::sync::atomic::{AtomicUsize, Ordering};
11
12use crossbeam_queue::SegQueue;
13use dashmap::DashSet;
14use tokio::sync::Notify;
15
16use super::handle::TransferId;
17
18pub struct QueueItem<T> {
20 pub transfer_id: TransferId,
22 pub data: T,
24}
25
26impl<T> QueueItem<T> {
27 pub fn new(transfer_id: TransferId, data: T) -> Self {
29 Self { transfer_id, data }
30 }
31}
32
33pub struct CancellableQueue<T> {
51 inner: SegQueue<QueueItem<T>>,
53 cancelled: DashSet<TransferId>,
55 len: AtomicUsize,
57 notify: Notify,
59}
60
61impl<T> CancellableQueue<T> {
62 pub fn new() -> Self {
64 Self {
65 inner: SegQueue::new(),
66 cancelled: DashSet::new(),
67 len: AtomicUsize::new(0),
68 notify: Notify::new(),
69 }
70 }
71
72 pub fn push(&self, transfer_id: TransferId, data: T) -> bool {
77 if self.cancelled.contains(&transfer_id) {
79 return false;
80 }
81
82 self.inner.push(QueueItem::new(transfer_id, data));
83 self.len.fetch_add(1, Ordering::Relaxed);
84 self.notify.notify_one();
85 true
86 }
87
88 pub async fn notified(&self) {
90 self.notify.notified().await;
91 }
92
93 pub fn pop(&self) -> Option<QueueItem<T>> {
99 let item = self.inner.pop();
100 if item.is_some() {
101 self.len.fetch_sub(1, Ordering::Relaxed);
102 }
103 item
104 }
105
106 pub fn pop_valid(&self) -> Option<QueueItem<T>> {
111 loop {
112 match self.inner.pop() {
113 Some(item) => {
114 self.len.fetch_sub(1, Ordering::Relaxed);
115 if self.cancelled.contains(&item.transfer_id) {
116 continue;
118 }
119 return Some(item);
120 }
121 None => return None,
122 }
123 }
124 }
125
126 pub fn mark_cancelled(&self, transfer_id: TransferId) {
133 self.cancelled.insert(transfer_id);
134 self.notify.notify_waiters();
135 }
136
137 pub fn is_cancelled(&self, transfer_id: TransferId) -> bool {
139 self.cancelled.contains(&transfer_id)
140 }
141
142 pub fn sweep(&self) -> usize {
156 if self.cancelled.is_empty() {
157 return 0;
158 }
159
160 let mut removed = 0;
162 let mut kept = Vec::new();
163
164 while let Some(item) = self.inner.pop() {
165 if self.cancelled.contains(&item.transfer_id) {
166 removed += 1;
167 } else {
169 kept.push(item);
170 }
171 }
172
173 for item in kept {
175 self.inner.push(item);
176 }
177
178 if removed > 0 {
180 self.len.fetch_sub(removed, Ordering::Relaxed);
181 }
182
183 removed
184 }
185
186 pub fn clear_cancelled(&self, transfer_id: TransferId) {
190 self.cancelled.remove(&transfer_id);
191 }
192
193 pub fn len_approx(&self) -> usize {
197 self.len.load(Ordering::Relaxed)
198 }
199
200 pub fn is_empty_approx(&self) -> bool {
202 self.len_approx() == 0
203 }
204
205 pub fn cancelled_count(&self) -> usize {
207 self.cancelled.len()
208 }
209}
210
211impl<T> Default for CancellableQueue<T> {
212 fn default() -> Self {
213 Self::new()
214 }
215}
216
217#[cfg(test)]
218mod tests {
219 use super::*;
220
221 #[test]
222 fn test_basic_push_pop() {
223 let queue: CancellableQueue<i32> = CancellableQueue::new();
224 let id = TransferId::new();
225
226 assert!(queue.push(id, 42));
227 assert_eq!(queue.len_approx(), 1);
228
229 let item = queue.pop().unwrap();
230 assert_eq!(item.transfer_id, id);
231 assert_eq!(item.data, 42);
232 assert_eq!(queue.len_approx(), 0);
233 }
234
235 #[test]
236 fn test_cancelled_push_rejected() {
237 let queue: CancellableQueue<i32> = CancellableQueue::new();
238 let id = TransferId::new();
239
240 queue.mark_cancelled(id);
241 assert!(!queue.push(id, 42));
242 assert_eq!(queue.len_approx(), 0);
243 }
244
245 #[test]
246 fn test_pop_valid_skips_cancelled() {
247 let queue: CancellableQueue<i32> = CancellableQueue::new();
248 let id1 = TransferId::new();
249 let id2 = TransferId::new();
250
251 queue.push(id1, 1);
252 queue.push(id2, 2);
253 queue.push(id1, 3);
254
255 queue.mark_cancelled(id1);
256
257 let item = queue.pop_valid().unwrap();
259 assert_eq!(item.transfer_id, id2);
260 assert_eq!(item.data, 2);
261
262 assert!(queue.pop_valid().is_none());
264 }
265
266 #[test]
267 fn test_sweep_removes_cancelled() {
268 let queue: CancellableQueue<i32> = CancellableQueue::new();
269 let id1 = TransferId::new();
270 let id2 = TransferId::new();
271
272 queue.push(id1, 1);
273 queue.push(id2, 2);
274 queue.push(id1, 3);
275 queue.push(id2, 4);
276
277 assert_eq!(queue.len_approx(), 4);
278
279 queue.mark_cancelled(id1);
280 let removed = queue.sweep();
281
282 assert_eq!(removed, 2);
283 assert_eq!(queue.len_approx(), 2);
284
285 let item1 = queue.pop().unwrap();
287 let item2 = queue.pop().unwrap();
288 assert_eq!(item1.transfer_id, id2);
289 assert_eq!(item2.transfer_id, id2);
290 }
291
292 #[test]
293 fn test_sweep_empty_cancelled_set() {
294 let queue: CancellableQueue<i32> = CancellableQueue::new();
295 let id = TransferId::new();
296
297 queue.push(id, 1);
298 queue.push(id, 2);
299
300 let removed = queue.sweep();
302 assert_eq!(removed, 0);
303 assert_eq!(queue.len_approx(), 2);
304 }
305
306 #[test]
307 fn test_clear_cancelled() {
308 let queue: CancellableQueue<i32> = CancellableQueue::new();
309 let id = TransferId::new();
310
311 queue.mark_cancelled(id);
312 assert!(queue.is_cancelled(id));
313 assert_eq!(queue.cancelled_count(), 1);
314
315 queue.clear_cancelled(id);
316 assert!(!queue.is_cancelled(id));
317 assert_eq!(queue.cancelled_count(), 0);
318 }
319
320 #[test]
322 fn test_multiple_transfers_interleaved() {
323 let queue: CancellableQueue<i32> = CancellableQueue::new();
324 let id1 = TransferId::new();
325 let id2 = TransferId::new();
326 let id3 = TransferId::new();
327
328 queue.push(id1, 1);
330 queue.push(id2, 2);
331 queue.push(id1, 3);
332 queue.push(id3, 4);
333 queue.push(id2, 5);
334 queue.push(id3, 6);
335
336 assert_eq!(queue.len_approx(), 6);
337
338 queue.mark_cancelled(id2);
340 let removed = queue.sweep();
341 assert_eq!(removed, 2); assert_eq!(queue.len_approx(), 4);
343
344 queue.mark_cancelled(id1);
346 let removed = queue.sweep();
347 assert_eq!(removed, 2); assert_eq!(queue.len_approx(), 2);
349
350 let item1 = queue.pop().unwrap();
352 let item2 = queue.pop().unwrap();
353 assert_eq!(item1.transfer_id, id3);
354 assert_eq!(item2.transfer_id, id3);
355 }
356
357 #[test]
359 fn test_sweep_empty_queue() {
360 let queue: CancellableQueue<i32> = CancellableQueue::new();
361 let id = TransferId::new();
362
363 queue.mark_cancelled(id);
364 let removed = queue.sweep();
365 assert_eq!(removed, 0);
366 assert!(queue.is_empty_approx());
367 }
368
369 #[test]
371 fn test_pop_valid_exhausts_cancelled() {
372 let queue: CancellableQueue<i32> = CancellableQueue::new();
373 let id = TransferId::new();
374
375 queue.push(id, 1);
376 queue.push(id, 2);
377 queue.push(id, 3);
378
379 queue.mark_cancelled(id);
380
381 assert!(queue.pop_valid().is_none());
383 assert_eq!(queue.len_approx(), 0);
385 }
386
387 #[test]
389 fn test_sweep_drops_items() {
390 use std::sync::Arc;
391 use std::sync::atomic::{AtomicUsize, Ordering};
392
393 struct DropCounter {
394 counter: Arc<AtomicUsize>,
395 }
396
397 impl Drop for DropCounter {
398 fn drop(&mut self) {
399 self.counter.fetch_add(1, Ordering::SeqCst);
400 }
401 }
402
403 let drop_count = Arc::new(AtomicUsize::new(0));
404 let queue: CancellableQueue<DropCounter> = CancellableQueue::new();
405 let id = TransferId::new();
406
407 queue.push(
408 id,
409 DropCounter {
410 counter: drop_count.clone(),
411 },
412 );
413 queue.push(
414 id,
415 DropCounter {
416 counter: drop_count.clone(),
417 },
418 );
419 queue.push(
420 id,
421 DropCounter {
422 counter: drop_count.clone(),
423 },
424 );
425
426 assert_eq!(drop_count.load(Ordering::SeqCst), 0);
427
428 queue.mark_cancelled(id);
429 let removed = queue.sweep();
430
431 assert_eq!(removed, 3);
432 assert_eq!(drop_count.load(Ordering::SeqCst), 3);
433 }
434}