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 SendingObjects,
59}
60
61impl ProgressPhase {
62 #[must_use]
64 pub fn default_unit(self) -> ProgressUnit {
65 match self {
66 Self::ReceivingObjects
67 | Self::EnumeratingObjects
68 | Self::IndexingObjects
69 | Self::ResolvingDeltas
70 | Self::CopyingObjects => ProgressUnit::Objects,
71 Self::CheckingOutFiles | Self::StagingFiles => ProgressUnit::Files,
72 Self::TransferringBundle | Self::SendingObjects => ProgressUnit::Bytes,
73 }
74 }
75}
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
79pub enum ProgressUnit {
80 Objects,
82 Bytes,
84 Files,
86}
87
88#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
94pub struct ReceivedObjects {
95 pub received: u64,
97 pub total: u64,
99 pub deltas: u64,
101}
102
103pub trait ProgressSink {
110 fn start(&mut self, phase: ProgressPhase, total: Option<u64>) {
117 let _ = (phase, total);
118 }
119
120 fn start_with_unit(&mut self, phase: ProgressPhase, unit: ProgressUnit, total: Option<u64>) {
126 let _ = unit;
127 self.start(phase, total);
128 }
129
130 fn inc(&mut self, units: u64) {
132 let _ = units;
133 }
134
135 fn set(&mut self, current: u64) {
137 let _ = current;
138 }
139
140 fn finish(&mut self) {}
142
143 fn should_cancel(&self) -> bool {
149 false
150 }
151}
152
153impl<T: ProgressSink + ?Sized> ProgressSink for &mut T {
156 fn start(&mut self, phase: ProgressPhase, total: Option<u64>) {
157 (**self).start(phase, total);
158 }
159 fn start_with_unit(&mut self, phase: ProgressPhase, unit: ProgressUnit, total: Option<u64>) {
160 (**self).start_with_unit(phase, unit, total);
161 }
162 fn inc(&mut self, units: u64) {
163 (**self).inc(units);
164 }
165 fn set(&mut self, current: u64) {
166 (**self).set(current);
167 }
168 fn finish(&mut self) {
169 (**self).finish();
170 }
171 fn should_cancel(&self) -> bool {
172 (**self).should_cancel()
173 }
174}
175
176#[derive(Debug, Default, Clone, Copy)]
179pub struct NullProgress;
180
181impl ProgressSink for NullProgress {}
182
183pub trait Cancel {
186 fn is_cancelled(&self) -> bool;
188
189 fn shared_watch(&self) -> Option<std::sync::Arc<std::sync::atomic::AtomicBool>> {
192 None
193 }
194
195 fn poll_before_wait(&self) {}
197}
198
199#[derive(Debug, Default, Clone, Copy)]
202pub struct NeverCancel;
203
204impl Cancel for NeverCancel {
205 fn is_cancelled(&self) -> bool {
206 false
207 }
208}
209
210pub(crate) const OBJECT_PROGRESS_STRIDE: u64 = 256;
212
213pub(crate) const CANCEL_POLL_STRIDE: usize = 256;
215
216pub(crate) struct ProgressWrite<'a, W: ?Sized> {
219 inner: &'a mut W,
220 sink: &'a mut dyn ProgressSink,
221 started: bool,
222}
223
224impl<'a, W: ?Sized + std::io::Write> ProgressWrite<'a, W> {
225 pub fn new(inner: &'a mut W, sink: &'a mut dyn ProgressSink) -> Self {
227 Self {
228 inner,
229 sink,
230 started: false,
231 }
232 }
233
234 pub fn finish(&mut self) {
236 if self.started {
237 self.sink.finish();
238 self.started = false;
239 }
240 }
241}
242
243impl<W: ?Sized + std::io::Write> std::io::Write for ProgressWrite<'_, W> {
244 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
245 let n = self.inner.write(buf)?;
246 if n > 0 {
247 if !self.started {
248 self.sink
249 .start_with_unit(ProgressPhase::SendingObjects, ProgressUnit::Bytes, None);
250 self.started = true;
251 }
252 self.sink.inc(n as u64);
253 }
254 Ok(n)
255 }
256
257 fn flush(&mut self) -> std::io::Result<()> {
258 self.inner.flush()
259 }
260}
261
262pub(crate) fn report_object_progress_stride(
267 sink: &mut dyn ProgressSink,
268 done: u64,
269 last_reported: &mut u64,
270 total: Option<u64>,
271) {
272 let stride = OBJECT_PROGRESS_STRIDE;
273 while done.saturating_sub(*last_reported) >= stride {
274 *last_reported += stride;
275 sink.set(*last_reported);
276 }
277 if total == Some(done) && *last_reported < done {
278 sink.set(done);
279 *last_reported = done;
280 }
281}
282
283#[derive(Debug, Clone)]
285pub struct FlagCancel(pub std::sync::Arc<std::sync::atomic::AtomicBool>);
286
287impl FlagCancel {
288 #[must_use]
290 pub fn new() -> Self {
291 Self(std::sync::Arc::new(std::sync::atomic::AtomicBool::new(
292 false,
293 )))
294 }
295
296 pub fn cancel(&self) {
298 self.0.store(true, std::sync::atomic::Ordering::Relaxed);
299 }
300}
301
302impl Default for FlagCancel {
303 fn default() -> Self {
304 Self::new()
305 }
306}
307
308impl Cancel for FlagCancel {
309 fn is_cancelled(&self) -> bool {
310 self.0.load(std::sync::atomic::Ordering::Relaxed)
311 }
312
313 fn shared_watch(&self) -> Option<std::sync::Arc<std::sync::atomic::AtomicBool>> {
314 Some(std::sync::Arc::clone(&self.0))
315 }
316}
317
318#[inline]
320pub(crate) fn mirror_stack_cancel(cancel: &dyn Cancel, watch: &std::sync::atomic::AtomicBool) {
321 cancel.poll_before_wait();
322 if cancel.is_cancelled() {
323 watch.store(true, std::sync::atomic::Ordering::Relaxed);
324 }
325}
326
327pub(crate) struct HttpStackWatch(pub(crate) std::sync::Arc<std::sync::atomic::AtomicBool>);
329
330impl HttpStackWatch {
331 #[must_use]
332 pub fn new(cancel: &dyn Cancel) -> Self {
333 Self(cancel.shared_watch().unwrap_or_else(|| {
334 std::sync::Arc::new(std::sync::atomic::AtomicBool::new(cancel.is_cancelled()))
335 }))
336 }
337}
338
339pub(crate) struct HttpWaitGuard {
341 _watch: std::sync::Arc<std::sync::atomic::AtomicBool>,
342}
343
344impl HttpWaitGuard {
345 pub(crate) fn bind(
346 cancel: &dyn Cancel,
347 ) -> (std::sync::Arc<std::sync::atomic::AtomicBool>, Self) {
348 let watch = HttpStackWatch::new(cancel).0;
349 (std::sync::Arc::clone(&watch), Self { _watch: watch })
350 }
351}
352
353#[must_use]
359pub(crate) fn connect_cancel_arc(
360 cancel: &dyn Cancel,
361 stack_watch: std::sync::Arc<std::sync::atomic::AtomicBool>,
362) -> std::sync::Arc<dyn Cancel + Send + Sync> {
363 mirror_stack_cancel(cancel, &stack_watch);
364 if let Some(flag) = cancel.shared_watch() {
365 return std::sync::Arc::new(FlagCancel(flag));
366 }
367 std::sync::Arc::new(ConnectOperationCancel { watch: stack_watch })
368}
369
370struct ConnectOperationCancel {
372 watch: std::sync::Arc<std::sync::atomic::AtomicBool>,
373}
374
375impl Cancel for ConnectOperationCancel {
376 fn is_cancelled(&self) -> bool {
377 self.watch.load(std::sync::atomic::Ordering::Relaxed)
378 }
379
380 fn shared_watch(&self) -> Option<std::sync::Arc<std::sync::atomic::AtomicBool>> {
381 Some(std::sync::Arc::clone(&self.watch))
382 }
383}
384
385#[must_use]
391pub fn cancel_watch_arc(cancel: &dyn Cancel) -> std::sync::Arc<std::sync::atomic::AtomicBool> {
392 cancel.shared_watch().unwrap_or_else(|| {
393 std::sync::Arc::new(std::sync::atomic::AtomicBool::new(cancel.is_cancelled()))
394 })
395}
396
397#[inline]
399pub fn poll_watch(watch: &std::sync::atomic::AtomicBool) -> crate::error::Result<()> {
400 if watch.load(std::sync::atomic::Ordering::Relaxed) {
401 Err(crate::error::Error::Cancelled)
402 } else {
403 Ok(())
404 }
405}
406
407pub(crate) struct StackCancelPoll {
412 progress: usize,
413 watch: std::sync::Arc<std::sync::atomic::AtomicBool>,
414}
415
416impl StackCancelPoll {
417 #[must_use]
419 pub fn new(
420 progress: &mut WatchProgress<'_>,
421 watch: std::sync::Arc<std::sync::atomic::AtomicBool>,
422 ) -> Self {
423 Self {
424 progress: std::ptr::from_mut(progress).addr(),
425 watch,
426 }
427 }
428
429 pub(crate) fn poll_owner(&self) -> crate::error::Result<()> {
431 let progress = unsafe { &mut *(self.progress as *mut WatchProgress<'_>) };
433 if progress.should_cancel() {
434 self.watch.store(true, std::sync::atomic::Ordering::Relaxed);
435 return Err(crate::error::Error::Cancelled);
436 }
437 poll_watch(&self.watch)
438 }
439}
440
441pub(crate) struct StatScanCancel {
443 pub watch: Option<std::sync::Arc<std::sync::atomic::AtomicBool>>,
444 pub owner_poll: Option<std::sync::Arc<StackCancelPoll>>,
445}
446
447impl StatScanCancel {
448 pub(crate) fn poll(&self) -> crate::error::Result<()> {
450 if let Some(poll) = self.owner_poll.as_ref() {
451 poll.poll_owner()
452 } else if let Some(w) = self.watch.as_ref() {
453 poll_watch(w)
454 } else {
455 Ok(())
456 }
457 }
458}
459
460#[inline]
462pub fn poll_cancel_watch(
463 progress: &mut dyn ProgressSink,
464 cancel: &dyn Cancel,
465 watch: &std::sync::atomic::AtomicBool,
466) -> crate::error::Result<()> {
467 if cancel.is_cancelled() || progress.should_cancel() {
468 watch.store(true, std::sync::atomic::Ordering::Relaxed);
469 return Err(crate::error::Error::Cancelled);
470 }
471 poll_watch(watch)
472}
473
474pub(crate) struct CancelSignal<'a> {
481 cancel: &'a dyn Cancel,
482 flag: std::sync::Arc<std::sync::atomic::AtomicBool>,
483}
484
485impl<'a> CancelSignal<'a> {
486 pub fn new(cancel: &'a dyn Cancel) -> Self {
487 Self {
488 cancel,
489 flag: cancel_watch_arc(cancel),
490 }
491 }
492
493 pub(crate) fn never() -> CancelSignal<'static> {
495 CancelSignal::new(&NeverCancel)
496 }
497
498 #[inline]
500 pub(crate) fn poll(&self) -> crate::error::Result<()> {
501 self.cancel.poll_before_wait();
502 if self.cancel.is_cancelled() {
503 self.flag.store(true, std::sync::atomic::Ordering::Relaxed);
504 }
505 poll_watch(&self.flag)
506 }
507
508 pub(crate) fn flag(&self) -> &std::sync::atomic::AtomicBool {
510 &self.flag
511 }
512
513 pub(crate) fn watch_arc(&self) -> std::sync::Arc<std::sync::atomic::AtomicBool> {
515 std::sync::Arc::clone(&self.flag)
516 }
517}
518
519pub struct WatchProgress<'a> {
521 inner: &'a mut dyn ProgressSink,
522 watch: std::sync::Arc<std::sync::atomic::AtomicBool>,
523}
524
525impl<'a> WatchProgress<'a> {
526 #[must_use]
528 pub fn new(
529 inner: &'a mut dyn ProgressSink,
530 watch: std::sync::Arc<std::sync::atomic::AtomicBool>,
531 ) -> Self {
532 Self { inner, watch }
533 }
534
535 fn note_cancel(&self) {
536 if self.inner.should_cancel() {
537 self.watch.store(true, std::sync::atomic::Ordering::Relaxed);
538 }
539 }
540}
541
542impl ProgressSink for WatchProgress<'_> {
543 fn start(&mut self, phase: ProgressPhase, total: Option<u64>) {
544 self.note_cancel();
545 self.inner.start(phase, total);
546 }
547 fn start_with_unit(&mut self, phase: ProgressPhase, unit: ProgressUnit, total: Option<u64>) {
548 self.note_cancel();
549 self.inner.start_with_unit(phase, unit, total);
550 }
551 fn inc(&mut self, units: u64) {
552 self.note_cancel();
553 self.inner.inc(units);
554 }
555 fn set(&mut self, current: u64) {
556 self.note_cancel();
557 self.inner.set(current);
558 }
559 fn finish(&mut self) {
560 self.note_cancel();
561 self.inner.finish();
562 }
563 fn should_cancel(&self) -> bool {
564 self.inner.should_cancel()
565 }
566}
567
568pub(crate) struct ProgressScope<'a> {
571 sink: WatchProgress<'a>,
572 cancel: &'a dyn Cancel,
573 watch: std::sync::Arc<std::sync::atomic::AtomicBool>,
574}
575
576impl<'a> ProgressScope<'a> {
577 pub fn new(sink: &'a mut dyn ProgressSink, cancel: &'a dyn Cancel) -> Self {
578 let watch = cancel_watch_arc(cancel);
579 Self {
580 sink: WatchProgress::new(sink, std::sync::Arc::clone(&watch)),
581 cancel,
582 watch,
583 }
584 }
585
586 pub(crate) fn poll(&mut self) -> crate::error::Result<()> {
588 poll_cancel_watch(&mut self.sink, self.cancel, &self.watch)
589 }
590
591 pub(crate) fn watch(&self) -> &std::sync::atomic::AtomicBool {
593 &self.watch
594 }
595
596 pub(crate) fn sink(&mut self) -> &mut dyn ProgressSink {
597 &mut self.sink
598 }
599}
600
601pub(crate) struct UncancellableProgress<'a> {
604 inner: &'a mut dyn ProgressSink,
605}
606
607impl<'a> UncancellableProgress<'a> {
608 pub fn new(inner: &'a mut dyn ProgressSink) -> Self {
609 Self { inner }
610 }
611}
612
613impl ProgressSink for UncancellableProgress<'_> {
614 fn start(&mut self, phase: ProgressPhase, total: Option<u64>) {
615 self.inner.start(phase, total);
616 }
617 fn start_with_unit(&mut self, phase: ProgressPhase, unit: ProgressUnit, total: Option<u64>) {
618 self.inner.start_with_unit(phase, unit, total);
619 }
620 fn inc(&mut self, units: u64) {
621 self.inner.inc(units);
622 }
623 fn set(&mut self, current: u64) {
624 self.inner.set(current);
625 }
626 fn finish(&mut self) {
627 self.inner.finish();
628 }
629}
630
631#[inline]
633pub fn poll_cancel(
634 progress: &mut dyn ProgressSink,
635 cancel: &dyn Cancel,
636) -> crate::error::Result<()> {
637 cancel.poll_before_wait();
638 if cancel.is_cancelled() || progress.should_cancel() {
639 Err(crate::error::Error::Cancelled)
640 } else {
641 Ok(())
642 }
643}
644
645pub struct CancelProgress<'a> {
650 inner: &'a mut dyn ProgressSink,
651 cancel: &'a dyn Cancel,
652 stopped: std::cell::Cell<bool>,
653}
654
655impl<'a> CancelProgress<'a> {
656 pub fn new(inner: &'a mut dyn ProgressSink, cancel: &'a dyn Cancel) -> Self {
658 Self {
659 inner,
660 cancel,
661 stopped: std::cell::Cell::new(false),
662 }
663 }
664
665 #[must_use]
667 pub fn stopped(&self) -> bool {
668 self.stopped.get()
669 }
670
671 pub fn finish_poll(&self) -> crate::error::Result<()> {
673 if self.stopped() {
674 Err(crate::error::Error::Cancelled)
675 } else {
676 Ok(())
677 }
678 }
679
680 fn note_stop(&self) {
681 if self.cancel.is_cancelled() || self.inner.should_cancel() {
682 self.stopped.set(true);
683 }
684 }
685}
686
687impl ProgressSink for CancelProgress<'_> {
688 fn start(&mut self, phase: ProgressPhase, total: Option<u64>) {
689 self.note_stop();
690 if !self.stopped.get() {
691 self.inner.start(phase, total);
692 }
693 }
694
695 fn start_with_unit(&mut self, phase: ProgressPhase, unit: ProgressUnit, total: Option<u64>) {
696 self.note_stop();
697 if !self.stopped.get() {
698 self.inner.start_with_unit(phase, unit, total);
699 }
700 }
701
702 fn inc(&mut self, units: u64) {
703 self.note_stop();
704 if !self.stopped.get() {
705 self.inner.inc(units);
706 }
707 }
708
709 fn set(&mut self, current: u64) {
710 self.note_stop();
711 if !self.stopped.get() {
712 self.inner.set(current);
713 }
714 }
715
716 fn finish(&mut self) {
717 self.note_stop();
718 if !self.stopped.get() {
719 self.inner.finish();
720 }
721 }
722
723 fn should_cancel(&self) -> bool {
724 self.stopped.get() || self.cancel.is_cancelled() || self.inner.should_cancel()
725 }
726}
727
728#[cfg(test)]
729mod tests {
730 use super::*;
731 use crate::fetch::{NoProgress, Progress};
732
733 #[derive(Default)]
736 struct PhaseRecorder {
737 started: Vec<(ProgressPhase, Option<u64>)>,
738 incs: u64,
739 finished: usize,
740 }
741
742 impl ProgressSink for PhaseRecorder {
743 fn start(&mut self, phase: ProgressPhase, total: Option<u64>) {
744 self.started.push((phase, total));
745 }
746 fn inc(&mut self, units: u64) {
747 self.incs += units;
748 }
749 fn finish(&mut self) {
750 self.finished += 1;
751 }
752 }
753
754 fn drive_typed(sink: &mut impl ProgressSink) {
755 sink.start(ProgressPhase::ReceivingObjects, Some(3));
756 sink.inc(1);
757 sink.inc(2);
758 sink.finish();
759 }
760
761 #[test]
762 fn null_progress_is_inert() {
763 drive_typed(&mut NullProgress);
764 }
765
766 #[test]
767 fn no_progress_is_inert() {
768 drive_typed(&mut NoProgress);
769 }
770
771 #[test]
772 fn typed_phase_recorder_roundtrip() {
773 let mut rec = PhaseRecorder::default();
774 drive_typed(&mut &mut rec);
775 assert_eq!(
776 rec.started,
777 vec![(ProgressPhase::ReceivingObjects, Some(3))]
778 );
779 assert_eq!(rec.incs, 3);
780 assert_eq!(rec.finished, 1);
781 }
782
783 #[test]
784 fn progress_supertrait_upcasts() {
785 struct SidebandCap(Vec<u8>);
786
787 impl ProgressSink for SidebandCap {}
788
789 impl Progress for SidebandCap {
790 fn message(&mut self, bytes: &[u8]) {
791 self.0.extend_from_slice(bytes);
792 }
793 }
794
795 fn takes_sink(sink: &mut dyn ProgressSink) {
796 sink.start(ProgressPhase::IndexingObjects, None);
797 sink.finish();
798 }
799
800 let mut cap = SidebandCap(Vec::new());
801 let progress: &mut dyn Progress = &mut cap;
802 takes_sink(progress);
803 }
804
805 #[test]
806 fn never_cancel_never_cancels() {
807 assert!(!NeverCancel.is_cancelled());
808 }
809
810 #[test]
811 fn poll_cancel_returns_typed_error() {
812 let flag = FlagCancel::new();
813 flag.cancel();
814 let mut sink = NullProgress;
815 let err = poll_cancel(&mut sink, &flag).expect_err("cancelled");
816 assert!(matches!(err, crate::error::Error::Cancelled));
817 }
818
819 #[test]
820 fn phase_default_unit() {
821 assert_eq!(
822 ProgressPhase::ReceivingObjects.default_unit(),
823 ProgressUnit::Objects
824 );
825 assert_eq!(
826 ProgressPhase::CheckingOutFiles.default_unit(),
827 ProgressUnit::Files
828 );
829 assert_eq!(
830 ProgressPhase::StagingFiles.default_unit(),
831 ProgressUnit::Files
832 );
833 }
834}