1use crate::protocol::DownloadId;
8use parking_lot::Mutex;
9use std::collections::{BinaryHeap, HashMap};
10use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
11use std::sync::Arc;
12use tokio::sync::{Notify, OwnedSemaphorePermit, Semaphore};
13
14pub use crate::protocol::DownloadPriority;
16
17#[derive(Debug, Clone, Eq, PartialEq)]
19struct QueueEntry {
20 id: DownloadId,
21 priority: DownloadPriority,
22 sequence: u64,
24}
25
26impl Ord for QueueEntry {
27 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
28 match self.priority.cmp(&other.priority) {
30 std::cmp::Ordering::Equal => other.sequence.cmp(&self.sequence), other => other,
32 }
33 }
34}
35
36impl PartialOrd for QueueEntry {
37 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
38 Some(self.cmp(other))
39 }
40}
41
42pub struct PriorityPermit {
45 _permit: OwnedSemaphorePermit,
46 id: DownloadId,
47 queue: Arc<PriorityQueue>,
48}
49
50impl Drop for PriorityPermit {
51 fn drop(&mut self) {
52 self.queue.inner.lock().active.remove(&self.id);
54 self.queue.notify.notify_waiters();
56 }
57}
58
59struct PriorityQueueInner {
61 waiting: BinaryHeap<QueueEntry>,
63 active: HashMap<DownloadId, DownloadPriority>,
65 waiting_priorities: HashMap<DownloadId, DownloadPriority>,
67}
68
69pub struct PriorityQueue {
75 semaphore: Arc<Semaphore>,
77 max_concurrent: AtomicUsize,
79 inner: Mutex<PriorityQueueInner>,
81 sequence: AtomicU64,
83 notify: Notify,
85}
86
87impl PriorityQueue {
88 pub fn new(max_concurrent: usize) -> Arc<Self> {
90 Arc::new(Self {
91 semaphore: Arc::new(Semaphore::new(max_concurrent)),
92 max_concurrent: AtomicUsize::new(max_concurrent),
93 inner: Mutex::new(PriorityQueueInner {
94 waiting: BinaryHeap::new(),
95 active: HashMap::new(),
96 waiting_priorities: HashMap::new(),
97 }),
98 sequence: AtomicU64::new(0),
99 notify: Notify::new(),
100 })
101 }
102
103 pub async fn acquire(
119 self: &Arc<Self>,
120 id: DownloadId,
121 priority: DownloadPriority,
122 ) -> PriorityPermit {
123 let sequence = self.sequence.fetch_add(1, Ordering::Relaxed);
125 {
126 let mut inner = self.inner.lock();
127 inner.waiting.push(QueueEntry {
128 id,
129 priority,
130 sequence,
131 });
132 inner.waiting_priorities.insert(id, priority);
133 }
134
135 loop {
136 {
138 let inner = self.inner.lock();
139 if let Some(next) = inner.waiting.peek() {
140 if next.id == id
141 && inner.active.len() < self.max_concurrent.load(Ordering::Relaxed)
142 {
143 drop(inner); if let Ok(permit) = self.semaphore.clone().try_acquire_owned() {
148 let mut inner = self.inner.lock();
150 inner.waiting.pop();
151 inner.waiting_priorities.remove(&id);
152 inner.active.insert(id, priority);
153
154 return PriorityPermit {
155 _permit: permit,
156 id,
157 queue: Arc::clone(self),
158 };
159 }
160 }
161 }
162 }
163
164 self.notify.notified().await;
166 }
167 }
168
169 pub fn try_acquire(
191 self: &Arc<Self>,
192 id: DownloadId,
193 priority: DownloadPriority,
194 ) -> Option<PriorityPermit> {
195 let mut inner = self.inner.lock();
196 if inner.active.len() >= self.max_concurrent.load(Ordering::Relaxed) {
197 return None;
198 }
199
200 if let Some(next) = inner.waiting.peek() {
202 if next.priority > priority {
203 return None; }
205 }
206
207 match self.semaphore.clone().try_acquire_owned() {
209 Ok(permit) => {
210 inner.active.insert(id, priority);
211 Some(PriorityPermit {
212 _permit: permit,
213 id,
214 queue: Arc::clone(self),
215 })
216 }
217 Err(_) => None,
218 }
219 }
220
221 pub fn set_priority(&self, id: DownloadId, new_priority: DownloadPriority) -> bool {
226 let mut inner = self.inner.lock();
227
228 if inner.waiting_priorities.contains_key(&id) {
230 let entries: Vec<_> = inner.waiting.drain().collect();
232 for entry in entries {
233 if entry.id == id {
234 inner.waiting.push(QueueEntry {
235 id: entry.id,
236 priority: new_priority,
237 sequence: entry.sequence,
238 });
239 } else {
240 inner.waiting.push(entry);
241 }
242 }
243 inner.waiting_priorities.insert(id, new_priority);
244 drop(inner);
245
246 self.notify.notify_waiters();
248 return true;
249 }
250
251 if let Some(priority) = inner.active.get_mut(&id) {
253 *priority = new_priority;
254 return true;
255 }
256
257 false
258 }
259
260 pub fn remove(&self, id: DownloadId) {
264 let mut inner = self.inner.lock();
265 inner.waiting_priorities.remove(&id);
266 let entries: Vec<_> = inner.waiting.drain().filter(|e| e.id != id).collect();
268 for entry in entries {
269 inner.waiting.push(entry);
270 }
271 }
272
273 pub fn get_priority(&self, id: DownloadId) -> Option<DownloadPriority> {
275 let inner = self.inner.lock();
276 inner
277 .waiting_priorities
278 .get(&id)
279 .or_else(|| inner.active.get(&id))
280 .copied()
281 }
282
283 pub fn set_max_concurrent(&self, max_concurrent: usize) {
285 let previous = self.max_concurrent.swap(max_concurrent, Ordering::Relaxed);
286 if max_concurrent > previous {
287 self.semaphore.add_permits(max_concurrent - previous);
288 }
289 self.notify.notify_waiters();
290 }
291
292 pub fn active_count(&self) -> usize {
294 self.inner.lock().active.len()
295 }
296
297 pub fn waiting_count(&self) -> usize {
299 self.inner.lock().waiting.len()
300 }
301
302 pub fn queue_position(&self, id: DownloadId) -> Option<usize> {
304 let inner = self.inner.lock();
305 if !inner.waiting_priorities.contains_key(&id) {
306 return None;
307 }
308 let mut sorted: Vec<_> = inner.waiting.iter().cloned().collect();
310 sorted.sort_by(|a, b| b.cmp(a)); sorted.iter().position(|e| e.id == id).map(|p| p + 1)
312 }
313
314 pub fn stats(&self) -> PriorityQueueStats {
316 let inner = self.inner.lock();
317 let mut by_priority = HashMap::new();
318 for priority in inner.waiting_priorities.values() {
319 *by_priority.entry(*priority).or_insert(0) += 1;
320 }
321 PriorityQueueStats {
322 active: inner.active.len(),
323 waiting: inner.waiting.len(),
324 waiting_by_priority: by_priority,
325 }
326 }
327}
328
329#[derive(Debug, Clone)]
331pub struct PriorityQueueStats {
332 pub active: usize,
334 pub waiting: usize,
336 pub waiting_by_priority: HashMap<DownloadPriority, usize>,
338}
339
340#[cfg(test)]
341mod tests {
342 use super::*;
343
344 #[test]
345 fn test_priority_ordering() {
346 assert!(DownloadPriority::Critical > DownloadPriority::High);
347 assert!(DownloadPriority::High > DownloadPriority::Normal);
348 assert!(DownloadPriority::Normal > DownloadPriority::Low);
349 }
350
351 #[test]
352 fn test_priority_from_str() {
353 assert_eq!(
354 "low".parse::<DownloadPriority>().unwrap(),
355 DownloadPriority::Low
356 );
357 assert_eq!(
358 "normal".parse::<DownloadPriority>().unwrap(),
359 DownloadPriority::Normal
360 );
361 assert_eq!(
362 "high".parse::<DownloadPriority>().unwrap(),
363 DownloadPriority::High
364 );
365 assert_eq!(
366 "critical".parse::<DownloadPriority>().unwrap(),
367 DownloadPriority::Critical
368 );
369 }
370
371 #[test]
372 fn test_queue_entry_ordering() {
373 let entry1 = QueueEntry {
374 id: DownloadId::new(),
375 priority: DownloadPriority::Normal,
376 sequence: 1,
377 };
378 let entry2 = QueueEntry {
379 id: DownloadId::new(),
380 priority: DownloadPriority::High,
381 sequence: 2,
382 };
383 let entry3 = QueueEntry {
384 id: DownloadId::new(),
385 priority: DownloadPriority::Normal,
386 sequence: 0,
387 };
388
389 assert!(entry2 > entry1);
391
392 assert!(entry3 > entry1);
394 }
395
396 #[tokio::test]
397 async fn test_priority_queue_basic() {
398 let queue = PriorityQueue::new(2);
399 let id1 = DownloadId::new();
400 let id2 = DownloadId::new();
401
402 let permit1 = queue.clone().acquire(id1, DownloadPriority::Normal).await;
404 let permit2 = queue.clone().acquire(id2, DownloadPriority::Normal).await;
405
406 assert_eq!(queue.active_count(), 2);
407
408 drop(permit1);
410 drop(permit2);
411
412 assert_eq!(queue.active_count(), 0);
413 }
414
415 #[tokio::test]
416 async fn test_priority_queue_priority_ordering() {
417 let queue = PriorityQueue::new(1);
418 let id_low = DownloadId::new();
419 let id_high = DownloadId::new();
420
421 let permit1 = queue
423 .clone()
424 .acquire(DownloadId::new(), DownloadPriority::Normal)
425 .await;
426
427 let queue_clone = queue.clone();
429 let low_handle =
430 tokio::spawn(async move { queue_clone.acquire(id_low, DownloadPriority::Low).await });
431
432 tokio::time::sleep(std::time::Duration::from_millis(10)).await;
434
435 let queue_clone = queue.clone();
437 let high_handle =
438 tokio::spawn(async move { queue_clone.acquire(id_high, DownloadPriority::High).await });
439
440 tokio::time::sleep(std::time::Duration::from_millis(10)).await;
442
443 assert_eq!(queue.waiting_count(), 2);
444
445 drop(permit1);
447
448 let high_permit = tokio::time::timeout(std::time::Duration::from_millis(100), high_handle)
450 .await
451 .expect("timeout")
452 .expect("join error");
453
454 assert_eq!(queue.active_count(), 1);
455 assert_eq!(queue.waiting_count(), 1);
456
457 drop(high_permit);
459
460 let _low_permit = tokio::time::timeout(std::time::Duration::from_millis(100), low_handle)
462 .await
463 .expect("timeout")
464 .expect("join error");
465
466 assert_eq!(queue.active_count(), 1);
467 assert_eq!(queue.waiting_count(), 0);
468 }
469
470 #[test]
471 fn test_set_priority() {
472 let queue = PriorityQueue::new(1);
473 let id = DownloadId::new();
474
475 {
477 let mut inner = queue.inner.lock();
478 inner.waiting.push(QueueEntry {
479 id,
480 priority: DownloadPriority::Low,
481 sequence: 0,
482 });
483 inner.waiting_priorities.insert(id, DownloadPriority::Low);
484 }
485
486 assert_eq!(queue.get_priority(id), Some(DownloadPriority::Low));
487
488 assert!(queue.set_priority(id, DownloadPriority::High));
490
491 assert_eq!(queue.get_priority(id), Some(DownloadPriority::High));
492 }
493
494 #[test]
495 fn test_remove() {
496 let queue = PriorityQueue::new(1);
497 let id = DownloadId::new();
498
499 {
501 let mut inner = queue.inner.lock();
502 inner.waiting.push(QueueEntry {
503 id,
504 priority: DownloadPriority::Normal,
505 sequence: 0,
506 });
507 inner
508 .waiting_priorities
509 .insert(id, DownloadPriority::Normal);
510 }
511
512 assert_eq!(queue.waiting_count(), 1);
513
514 queue.remove(id);
516
517 assert_eq!(queue.waiting_count(), 0);
518 assert_eq!(queue.get_priority(id), None);
519 }
520
521 #[test]
522 fn test_stats() {
523 let queue = PriorityQueue::new(2);
524
525 {
527 let mut inner = queue.inner.lock();
528 for i in 0..3 {
529 let id = DownloadId::new();
530 let priority = match i % 3 {
531 0 => DownloadPriority::Low,
532 1 => DownloadPriority::Normal,
533 _ => DownloadPriority::High,
534 };
535 inner.waiting.push(QueueEntry {
536 id,
537 priority,
538 sequence: i,
539 });
540 inner.waiting_priorities.insert(id, priority);
541 }
542 }
543
544 let stats = queue.stats();
545 assert_eq!(stats.waiting, 3);
546 assert_eq!(stats.active, 0);
547 }
548}