1mod estimate;
28
29pub use estimate::{OverallEstimate, OverallEstimator, PhaseRates};
30
31#[non_exhaustive]
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
37pub enum ProgressPhase {
38 ReceivingObjects,
40 EnumeratingObjects,
42 IndexingObjects,
44 ResolvingDeltas,
46 CopyingObjects,
48 CheckingOutFiles,
50 StagingFiles,
52 TransferringBundle,
57}
58
59impl ProgressPhase {
60 #[must_use]
62 pub fn default_unit(self) -> ProgressUnit {
63 match self {
64 Self::ReceivingObjects
65 | Self::EnumeratingObjects
66 | Self::IndexingObjects
67 | Self::ResolvingDeltas
68 | Self::CopyingObjects => ProgressUnit::Objects,
69 Self::CheckingOutFiles | Self::StagingFiles => ProgressUnit::Files,
70 Self::TransferringBundle => ProgressUnit::Bytes,
71 }
72 }
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
77pub enum ProgressUnit {
78 Objects,
80 Bytes,
82 Files,
84}
85
86#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
92pub struct ReceivedObjects {
93 pub received: u64,
95 pub total: u64,
97 pub deltas: u64,
99}
100
101pub trait ProgressSink {
108 fn start(&mut self, phase: ProgressPhase, total: Option<u64>) {
115 let _ = (phase, total);
116 }
117
118 fn start_with_unit(&mut self, phase: ProgressPhase, unit: ProgressUnit, total: Option<u64>) {
124 let _ = unit;
125 self.start(phase, total);
126 }
127
128 fn inc(&mut self, units: u64) {
130 let _ = units;
131 }
132
133 fn set(&mut self, current: u64) {
135 let _ = current;
136 }
137
138 fn finish(&mut self) {}
140
141 fn should_cancel(&self) -> bool {
147 false
148 }
149}
150
151impl<T: ProgressSink + ?Sized> ProgressSink for &mut T {
154 fn start(&mut self, phase: ProgressPhase, total: Option<u64>) {
155 (**self).start(phase, total);
156 }
157 fn start_with_unit(&mut self, phase: ProgressPhase, unit: ProgressUnit, total: Option<u64>) {
158 (**self).start_with_unit(phase, unit, total);
159 }
160 fn inc(&mut self, units: u64) {
161 (**self).inc(units);
162 }
163 fn set(&mut self, current: u64) {
164 (**self).set(current);
165 }
166 fn finish(&mut self) {
167 (**self).finish();
168 }
169 fn should_cancel(&self) -> bool {
170 (**self).should_cancel()
171 }
172}
173
174#[derive(Debug, Default, Clone, Copy)]
177pub struct NullProgress;
178
179impl ProgressSink for NullProgress {}
180
181pub trait Cancel {
184 fn is_cancelled(&self) -> bool;
186
187 fn shared_watch(&self) -> Option<std::sync::Arc<std::sync::atomic::AtomicBool>> {
190 None
191 }
192}
193
194#[derive(Debug, Default, Clone, Copy)]
197pub struct NeverCancel;
198
199impl Cancel for NeverCancel {
200 fn is_cancelled(&self) -> bool {
201 false
202 }
203}
204
205pub(crate) const OBJECT_PROGRESS_STRIDE: u64 = 256;
207
208pub(crate) const CANCEL_POLL_STRIDE: usize = 256;
210
211pub(crate) fn report_object_progress_stride(
216 sink: &mut dyn ProgressSink,
217 done: u64,
218 last_reported: &mut u64,
219 total: Option<u64>,
220) {
221 let stride = OBJECT_PROGRESS_STRIDE;
222 while done.saturating_sub(*last_reported) >= stride {
223 *last_reported += stride;
224 sink.set(*last_reported);
225 }
226 if total == Some(done) && *last_reported < done {
227 sink.set(done);
228 *last_reported = done;
229 }
230}
231
232#[derive(Debug, Clone)]
234pub struct FlagCancel(pub std::sync::Arc<std::sync::atomic::AtomicBool>);
235
236impl FlagCancel {
237 #[must_use]
239 pub fn new() -> Self {
240 Self(std::sync::Arc::new(std::sync::atomic::AtomicBool::new(
241 false,
242 )))
243 }
244
245 pub fn cancel(&self) {
247 self.0.store(true, std::sync::atomic::Ordering::Relaxed);
248 }
249}
250
251impl Default for FlagCancel {
252 fn default() -> Self {
253 Self::new()
254 }
255}
256
257impl Cancel for FlagCancel {
258 fn is_cancelled(&self) -> bool {
259 self.0.load(std::sync::atomic::Ordering::Relaxed)
260 }
261
262 fn shared_watch(&self) -> Option<std::sync::Arc<std::sync::atomic::AtomicBool>> {
263 Some(std::sync::Arc::clone(&self.0))
264 }
265}
266
267#[must_use]
273pub fn cancel_watch_arc(cancel: &dyn Cancel) -> std::sync::Arc<std::sync::atomic::AtomicBool> {
274 cancel.shared_watch().unwrap_or_else(|| {
275 std::sync::Arc::new(std::sync::atomic::AtomicBool::new(cancel.is_cancelled()))
276 })
277}
278
279#[inline]
281pub fn poll_watch(watch: &std::sync::atomic::AtomicBool) -> crate::error::Result<()> {
282 if watch.load(std::sync::atomic::Ordering::Relaxed) {
283 Err(crate::error::Error::Cancelled)
284 } else {
285 Ok(())
286 }
287}
288
289pub(crate) struct StackCancelPoll {
294 progress: usize,
295 watch: std::sync::Arc<std::sync::atomic::AtomicBool>,
296}
297
298impl StackCancelPoll {
299 #[must_use]
301 pub(crate) fn new(
302 progress: &mut WatchProgress<'_>,
303 watch: std::sync::Arc<std::sync::atomic::AtomicBool>,
304 ) -> Self {
305 Self {
306 progress: std::ptr::from_mut(progress).addr(),
307 watch,
308 }
309 }
310
311 pub(crate) fn poll_owner(&self) -> crate::error::Result<()> {
313 let progress = unsafe { &mut *(self.progress as *mut WatchProgress<'_>) };
315 if progress.should_cancel() {
316 self.watch.store(true, std::sync::atomic::Ordering::Relaxed);
317 return Err(crate::error::Error::Cancelled);
318 }
319 poll_watch(&self.watch)
320 }
321}
322
323pub(crate) struct StatScanCancel {
325 pub watch: Option<std::sync::Arc<std::sync::atomic::AtomicBool>>,
326 pub owner_poll: Option<std::sync::Arc<StackCancelPoll>>,
327}
328
329impl StatScanCancel {
330 pub(crate) fn poll(&self) -> crate::error::Result<()> {
332 if let Some(poll) = self.owner_poll.as_ref() {
333 poll.poll_owner()
334 } else if let Some(w) = self.watch.as_ref() {
335 poll_watch(w)
336 } else {
337 Ok(())
338 }
339 }
340}
341
342#[inline]
344pub fn poll_cancel_watch(
345 progress: &mut dyn ProgressSink,
346 cancel: &dyn Cancel,
347 watch: &std::sync::atomic::AtomicBool,
348) -> crate::error::Result<()> {
349 if cancel.is_cancelled() || progress.should_cancel() {
350 watch.store(true, std::sync::atomic::Ordering::Relaxed);
351 return Err(crate::error::Error::Cancelled);
352 }
353 poll_watch(watch)
354}
355
356pub(crate) struct CancelSignal<'a> {
363 cancel: &'a dyn Cancel,
364 flag: std::sync::Arc<std::sync::atomic::AtomicBool>,
365}
366
367impl<'a> CancelSignal<'a> {
368 pub(crate) fn new(cancel: &'a dyn Cancel) -> Self {
369 Self {
370 cancel,
371 flag: cancel_watch_arc(cancel),
372 }
373 }
374
375 pub(crate) fn never() -> CancelSignal<'static> {
377 CancelSignal::new(&NeverCancel)
378 }
379
380 #[inline]
382 pub(crate) fn poll(&self) -> crate::error::Result<()> {
383 if self.cancel.is_cancelled() {
384 self.flag.store(true, std::sync::atomic::Ordering::Relaxed);
385 }
386 poll_watch(&self.flag)
387 }
388
389 pub(crate) fn flag(&self) -> &std::sync::atomic::AtomicBool {
391 &self.flag
392 }
393
394 pub(crate) fn watch_arc(&self) -> std::sync::Arc<std::sync::atomic::AtomicBool> {
396 std::sync::Arc::clone(&self.flag)
397 }
398}
399
400pub struct WatchProgress<'a> {
402 inner: &'a mut dyn ProgressSink,
403 watch: std::sync::Arc<std::sync::atomic::AtomicBool>,
404}
405
406impl<'a> WatchProgress<'a> {
407 #[must_use]
409 pub fn new(
410 inner: &'a mut dyn ProgressSink,
411 watch: std::sync::Arc<std::sync::atomic::AtomicBool>,
412 ) -> Self {
413 Self { inner, watch }
414 }
415
416 fn note_cancel(&self) {
417 if self.inner.should_cancel() {
418 self.watch.store(true, std::sync::atomic::Ordering::Relaxed);
419 }
420 }
421}
422
423impl ProgressSink for WatchProgress<'_> {
424 fn start(&mut self, phase: ProgressPhase, total: Option<u64>) {
425 self.note_cancel();
426 self.inner.start(phase, total);
427 }
428 fn start_with_unit(&mut self, phase: ProgressPhase, unit: ProgressUnit, total: Option<u64>) {
429 self.note_cancel();
430 self.inner.start_with_unit(phase, unit, total);
431 }
432 fn inc(&mut self, units: u64) {
433 self.note_cancel();
434 self.inner.inc(units);
435 }
436 fn set(&mut self, current: u64) {
437 self.note_cancel();
438 self.inner.set(current);
439 }
440 fn finish(&mut self) {
441 self.note_cancel();
442 self.inner.finish();
443 }
444 fn should_cancel(&self) -> bool {
445 self.inner.should_cancel()
446 }
447}
448
449pub(crate) struct ProgressScope<'a> {
452 sink: WatchProgress<'a>,
453 cancel: &'a dyn Cancel,
454 watch: std::sync::Arc<std::sync::atomic::AtomicBool>,
455}
456
457impl<'a> ProgressScope<'a> {
458 pub(crate) fn new(sink: &'a mut dyn ProgressSink, cancel: &'a dyn Cancel) -> Self {
459 let watch = cancel_watch_arc(cancel);
460 Self {
461 sink: WatchProgress::new(sink, std::sync::Arc::clone(&watch)),
462 cancel,
463 watch,
464 }
465 }
466
467 pub(crate) fn poll(&mut self) -> crate::error::Result<()> {
469 poll_cancel_watch(&mut self.sink, self.cancel, &self.watch)
470 }
471
472 pub(crate) fn watch(&self) -> &std::sync::atomic::AtomicBool {
474 &self.watch
475 }
476
477 pub(crate) fn sink(&mut self) -> &mut dyn ProgressSink {
478 &mut self.sink
479 }
480}
481
482pub(crate) struct UncancellableProgress<'a> {
485 inner: &'a mut dyn ProgressSink,
486}
487
488impl<'a> UncancellableProgress<'a> {
489 pub(crate) fn new(inner: &'a mut dyn ProgressSink) -> Self {
490 Self { inner }
491 }
492}
493
494impl ProgressSink for UncancellableProgress<'_> {
495 fn start(&mut self, phase: ProgressPhase, total: Option<u64>) {
496 self.inner.start(phase, total);
497 }
498 fn start_with_unit(&mut self, phase: ProgressPhase, unit: ProgressUnit, total: Option<u64>) {
499 self.inner.start_with_unit(phase, unit, total);
500 }
501 fn inc(&mut self, units: u64) {
502 self.inner.inc(units);
503 }
504 fn set(&mut self, current: u64) {
505 self.inner.set(current);
506 }
507 fn finish(&mut self) {
508 self.inner.finish();
509 }
510}
511
512#[inline]
514pub fn poll_cancel(
515 progress: &mut dyn ProgressSink,
516 cancel: &dyn Cancel,
517) -> crate::error::Result<()> {
518 if cancel.is_cancelled() || progress.should_cancel() {
519 Err(crate::error::Error::Cancelled)
520 } else {
521 Ok(())
522 }
523}
524
525pub struct CancelProgress<'a> {
530 inner: &'a mut dyn ProgressSink,
531 cancel: &'a dyn Cancel,
532 stopped: std::cell::Cell<bool>,
533}
534
535impl<'a> CancelProgress<'a> {
536 pub fn new(inner: &'a mut dyn ProgressSink, cancel: &'a dyn Cancel) -> Self {
538 Self {
539 inner,
540 cancel,
541 stopped: std::cell::Cell::new(false),
542 }
543 }
544
545 #[must_use]
547 pub fn stopped(&self) -> bool {
548 self.stopped.get()
549 }
550
551 pub fn finish_poll(&self) -> crate::error::Result<()> {
553 if self.stopped() {
554 Err(crate::error::Error::Cancelled)
555 } else {
556 Ok(())
557 }
558 }
559
560 fn note_stop(&self) {
561 if self.cancel.is_cancelled() || self.inner.should_cancel() {
562 self.stopped.set(true);
563 }
564 }
565}
566
567impl ProgressSink for CancelProgress<'_> {
568 fn start(&mut self, phase: ProgressPhase, total: Option<u64>) {
569 self.note_stop();
570 if !self.stopped.get() {
571 self.inner.start(phase, total);
572 }
573 }
574
575 fn start_with_unit(&mut self, phase: ProgressPhase, unit: ProgressUnit, total: Option<u64>) {
576 self.note_stop();
577 if !self.stopped.get() {
578 self.inner.start_with_unit(phase, unit, total);
579 }
580 }
581
582 fn inc(&mut self, units: u64) {
583 self.note_stop();
584 if !self.stopped.get() {
585 self.inner.inc(units);
586 }
587 }
588
589 fn set(&mut self, current: u64) {
590 self.note_stop();
591 if !self.stopped.get() {
592 self.inner.set(current);
593 }
594 }
595
596 fn finish(&mut self) {
597 self.note_stop();
598 if !self.stopped.get() {
599 self.inner.finish();
600 }
601 }
602
603 fn should_cancel(&self) -> bool {
604 self.stopped.get() || self.cancel.is_cancelled() || self.inner.should_cancel()
605 }
606}
607
608#[cfg(test)]
609mod tests {
610 use super::*;
611 use crate::fetch::{NoProgress, Progress};
612
613 #[derive(Default)]
616 struct PhaseRecorder {
617 started: Vec<(ProgressPhase, Option<u64>)>,
618 incs: u64,
619 finished: usize,
620 }
621
622 impl ProgressSink for PhaseRecorder {
623 fn start(&mut self, phase: ProgressPhase, total: Option<u64>) {
624 self.started.push((phase, total));
625 }
626 fn inc(&mut self, units: u64) {
627 self.incs += units;
628 }
629 fn finish(&mut self) {
630 self.finished += 1;
631 }
632 }
633
634 fn drive_typed(sink: &mut impl ProgressSink) {
635 sink.start(ProgressPhase::ReceivingObjects, Some(3));
636 sink.inc(1);
637 sink.inc(2);
638 sink.finish();
639 }
640
641 #[test]
642 fn null_progress_is_inert() {
643 drive_typed(&mut NullProgress);
644 }
645
646 #[test]
647 fn no_progress_is_inert() {
648 drive_typed(&mut NoProgress);
649 }
650
651 #[test]
652 fn typed_phase_recorder_roundtrip() {
653 let mut rec = PhaseRecorder::default();
654 drive_typed(&mut &mut rec);
655 assert_eq!(
656 rec.started,
657 vec![(ProgressPhase::ReceivingObjects, Some(3))]
658 );
659 assert_eq!(rec.incs, 3);
660 assert_eq!(rec.finished, 1);
661 }
662
663 #[test]
664 fn progress_supertrait_upcasts() {
665 struct SidebandCap(Vec<u8>);
666
667 impl ProgressSink for SidebandCap {}
668
669 impl Progress for SidebandCap {
670 fn message(&mut self, bytes: &[u8]) {
671 self.0.extend_from_slice(bytes);
672 }
673 }
674
675 fn takes_sink(sink: &mut dyn ProgressSink) {
676 sink.start(ProgressPhase::IndexingObjects, None);
677 sink.finish();
678 }
679
680 let mut cap = SidebandCap(Vec::new());
681 let progress: &mut dyn Progress = &mut cap;
682 takes_sink(progress);
683 }
684
685 #[test]
686 fn never_cancel_never_cancels() {
687 assert!(!NeverCancel.is_cancelled());
688 }
689
690 #[test]
691 fn poll_cancel_returns_typed_error() {
692 let flag = FlagCancel::new();
693 flag.cancel();
694 let mut sink = NullProgress;
695 let err = poll_cancel(&mut sink, &flag).expect_err("cancelled");
696 assert!(matches!(err, crate::error::Error::Cancelled));
697 }
698
699 #[test]
700 fn phase_default_unit() {
701 assert_eq!(
702 ProgressPhase::ReceivingObjects.default_unit(),
703 ProgressUnit::Objects
704 );
705 assert_eq!(
706 ProgressPhase::CheckingOutFiles.default_unit(),
707 ProgressUnit::Files
708 );
709 assert_eq!(
710 ProgressPhase::StagingFiles.default_unit(),
711 ProgressUnit::Files
712 );
713 }
714}