@@ -28,7 +28,8 @@ use crate::ops;
28
28
use crate :: util:: config:: PackageCacheLock ;
29
29
use crate :: util:: errors:: { CargoResult , HttpNotSuccessful } ;
30
30
use crate :: util:: interning:: InternedString ;
31
- use crate :: util:: network:: Retry ;
31
+ use crate :: util:: network:: retry:: { Retry , RetryResult } ;
32
+ use crate :: util:: network:: sleep:: SleepTracker ;
32
33
use crate :: util:: { self , internal, Config , Progress , ProgressStyle } ;
33
34
34
35
pub const MANIFEST_PREAMBLE : & str = "\
@@ -319,6 +320,8 @@ pub struct Downloads<'a, 'cfg> {
319
320
/// Set of packages currently being downloaded. This should stay in sync
320
321
/// with `pending`.
321
322
pending_ids : HashSet < PackageId > ,
323
+ /// Downloads that have failed and are waiting to retry again later.
324
+ sleeping : SleepTracker < ( Download < ' cfg > , Easy ) > ,
322
325
/// The final result of each download. A pair `(token, result)`. This is a
323
326
/// temporary holding area, needed because curl can report multiple
324
327
/// downloads at once, but the main loop (`wait`) is written to only
@@ -442,6 +445,7 @@ impl<'cfg> PackageSet<'cfg> {
442
445
next : 0 ,
443
446
pending : HashMap :: new ( ) ,
444
447
pending_ids : HashSet :: new ( ) ,
448
+ sleeping : SleepTracker :: new ( ) ,
445
449
results : Vec :: new ( ) ,
446
450
progress : RefCell :: new ( Some ( Progress :: with_style (
447
451
"Downloading" ,
@@ -800,7 +804,7 @@ impl<'a, 'cfg> Downloads<'a, 'cfg> {
800
804
801
805
/// Returns the number of crates that are still downloading.
802
806
pub fn remaining ( & self ) -> usize {
803
- self . pending . len ( )
807
+ self . pending . len ( ) + self . sleeping . len ( )
804
808
}
805
809
806
810
/// Blocks the current thread waiting for a package to finish downloading.
@@ -831,51 +835,52 @@ impl<'a, 'cfg> Downloads<'a, 'cfg> {
831
835
let ret = {
832
836
let timed_out = & dl. timed_out ;
833
837
let url = & dl. url ;
834
- dl. retry
835
- . r#try ( || {
836
- if let Err ( e) = result {
837
- // If this error is "aborted by callback" then that's
838
- // probably because our progress callback aborted due to
839
- // a timeout. We'll find out by looking at the
840
- // `timed_out` field, looking for a descriptive message.
841
- // If one is found we switch the error code (to ensure
842
- // it's flagged as spurious) and then attach our extra
843
- // information to the error.
844
- if !e. is_aborted_by_callback ( ) {
845
- return Err ( e. into ( ) ) ;
846
- }
838
+ dl. retry . r#try ( || {
839
+ if let Err ( e) = result {
840
+ // If this error is "aborted by callback" then that's
841
+ // probably because our progress callback aborted due to
842
+ // a timeout. We'll find out by looking at the
843
+ // `timed_out` field, looking for a descriptive message.
844
+ // If one is found we switch the error code (to ensure
845
+ // it's flagged as spurious) and then attach our extra
846
+ // information to the error.
847
+ if !e. is_aborted_by_callback ( ) {
848
+ return Err ( e. into ( ) ) ;
849
+ }
847
850
848
- return Err ( match timed_out. replace ( None ) {
849
- Some ( msg) => {
850
- let code = curl_sys:: CURLE_OPERATION_TIMEDOUT ;
851
- let mut err = curl:: Error :: new ( code) ;
852
- err. set_extra ( msg) ;
853
- err
854
- }
855
- None => e,
851
+ return Err ( match timed_out. replace ( None ) {
852
+ Some ( msg) => {
853
+ let code = curl_sys:: CURLE_OPERATION_TIMEDOUT ;
854
+ let mut err = curl:: Error :: new ( code) ;
855
+ err. set_extra ( msg) ;
856
+ err
856
857
}
857
- . into ( ) ) ;
858
+ None => e ,
858
859
}
860
+ . into ( ) ) ;
861
+ }
859
862
860
- let code = handle. response_code ( ) ?;
861
- if code != 200 && code != 0 {
862
- let url = handle. effective_url ( ) ?. unwrap_or ( url) ;
863
- return Err ( HttpNotSuccessful {
864
- code,
865
- url : url. to_string ( ) ,
866
- body : data,
867
- }
868
- . into ( ) ) ;
863
+ let code = handle. response_code ( ) ?;
864
+ if code != 200 && code != 0 {
865
+ let url = handle. effective_url ( ) ?. unwrap_or ( url) ;
866
+ return Err ( HttpNotSuccessful {
867
+ code,
868
+ url : url. to_string ( ) ,
869
+ body : data,
869
870
}
870
- Ok ( data)
871
- } )
872
- . with_context ( || format ! ( "failed to download from `{}`" , dl. url) ) ?
871
+ . into ( ) ) ;
872
+ }
873
+ Ok ( data)
874
+ } )
873
875
} ;
874
876
match ret {
875
- Some ( data) => break ( dl, data) ,
876
- None => {
877
- self . pending_ids . insert ( dl. id ) ;
878
- self . enqueue ( dl, handle) ?
877
+ RetryResult :: Success ( data) => break ( dl, data) ,
878
+ RetryResult :: Err ( e) => {
879
+ return Err ( e. context ( format ! ( "failed to download from `{}`" , dl. url) ) )
880
+ }
881
+ RetryResult :: Retry ( sleep) => {
882
+ debug ! ( "download retry {} for {sleep}ms" , dl. url) ;
883
+ self . sleeping . push ( sleep, ( dl, handle) ) ;
879
884
}
880
885
}
881
886
} ;
@@ -963,6 +968,7 @@ impl<'a, 'cfg> Downloads<'a, 'cfg> {
963
968
// actually block waiting for I/O to happen, which we achieve with the
964
969
// `wait` method on `multi`.
965
970
loop {
971
+ self . add_sleepers ( ) ?;
966
972
let n = tls:: set ( self , || {
967
973
self . set
968
974
. multi
@@ -985,17 +991,31 @@ impl<'a, 'cfg> Downloads<'a, 'cfg> {
985
991
if let Some ( pair) = results. pop ( ) {
986
992
break Ok ( pair) ;
987
993
}
988
- assert ! ( !self . pending. is_empty( ) ) ;
989
- let min_timeout = Duration :: new ( 1 , 0 ) ;
990
- let timeout = self . set . multi . get_timeout ( ) ?. unwrap_or ( min_timeout) ;
991
- let timeout = timeout. min ( min_timeout) ;
992
- self . set
993
- . multi
994
- . wait ( & mut [ ] , timeout)
995
- . with_context ( || "failed to wait on curl `Multi`" ) ?;
994
+ assert_ne ! ( self . remaining( ) , 0 ) ;
995
+ if self . pending . is_empty ( ) {
996
+ let delay = self . sleeping . time_to_next ( ) . unwrap ( ) ;
997
+ debug ! ( "sleeping main thread for {delay:?}" ) ;
998
+ std:: thread:: sleep ( delay) ;
999
+ } else {
1000
+ let min_timeout = Duration :: new ( 1 , 0 ) ;
1001
+ let timeout = self . set . multi . get_timeout ( ) ?. unwrap_or ( min_timeout) ;
1002
+ let timeout = timeout. min ( min_timeout) ;
1003
+ self . set
1004
+ . multi
1005
+ . wait ( & mut [ ] , timeout)
1006
+ . with_context ( || "failed to wait on curl `Multi`" ) ?;
1007
+ }
996
1008
}
997
1009
}
998
1010
1011
+ fn add_sleepers ( & mut self ) -> CargoResult < ( ) > {
1012
+ for ( dl, handle) in self . sleeping . to_retry ( ) {
1013
+ self . pending_ids . insert ( dl. id ) ;
1014
+ self . enqueue ( dl, handle) ?;
1015
+ }
1016
+ Ok ( ( ) )
1017
+ }
1018
+
999
1019
fn progress ( & self , token : usize , total : u64 , cur : u64 ) -> bool {
1000
1020
let dl = & self . pending [ & token] . 0 ;
1001
1021
dl. total . set ( total) ;
@@ -1061,7 +1081,7 @@ impl<'a, 'cfg> Downloads<'a, 'cfg> {
1061
1081
return Ok ( ( ) ) ;
1062
1082
}
1063
1083
}
1064
- let pending = self . pending . len ( ) ;
1084
+ let pending = self . remaining ( ) ;
1065
1085
let mut msg = if pending == 1 {
1066
1086
format ! ( "{} crate" , pending)
1067
1087
} else {
0 commit comments