Skip to content

Commit 5d092de

Browse files
author
Jon Gjengset
committed
Don't yield the same package many times
Note that this could be a decent amount nicer with dtolnay/semver#170 This currently ends up in some kind of loop for cargo-like deps: yielding already-fetched rustversion yielding already-fetched futures yielding already-fetched thiserror yielding already-fetched anyhow yielding already-fetched rustversion yielding already-fetched futures yielding already-fetched thiserror Everything _up_ to that point terminates pretty quickly.
1 parent 6b1bd18 commit 5d092de

File tree

3 files changed

+34
-65
lines changed

3 files changed

+34
-65
lines changed

src/cargo/sources/registry/http_remote.rs

+29-61
Original file line numberDiff line numberDiff line change
@@ -346,19 +346,18 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> {
346346
use std::collections::btree_map::Entry;
347347
match self.downloads.eager.entry(path.to_path_buf()) {
348348
Entry::Occupied(mut o) => {
349-
let o = o.get_mut();
350-
if &o.primary.req != req {
351-
o.others.insert(req.clone());
352-
}
349+
o.get_mut().reqs.insert(req.clone());
353350
}
354351
Entry::Vacant(v) => {
355-
v.insert(MultiVersionFetched {
356-
primary: Fetched {
357-
path: path.to_path_buf(),
358-
name,
359-
req: req.clone(),
360-
},
361-
others: HashSet::new(),
352+
if self.prefetched.contains(path) {
353+
debug!("yielding already-prefetched {}", name);
354+
}
355+
let mut reqs = HashSet::new();
356+
reqs.insert(req.clone());
357+
v.insert(Fetched {
358+
path: path.to_path_buf(),
359+
name,
360+
reqs,
362361
});
363362
}
364363
}
@@ -399,14 +398,10 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> {
399398
.pending
400399
.get_mut(token)
401400
.expect("invalid token");
402-
if &dl.req != req {
403-
dl.additional_reqs.insert(req.clone());
404-
}
401+
dl.reqs.insert(req.clone());
405402
return Ok(());
406403
} else if let Some(f) = self.downloads.eager.get_mut(path) {
407-
if &f.primary.req != req {
408-
f.others.insert(req.clone());
409-
}
404+
f.reqs.insert(req.clone());
410405
return Ok(());
411406
}
412407

@@ -459,13 +454,14 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> {
459454
None,
460455
"path queued for download more than once"
461456
);
457+
let mut reqs = HashSet::new();
458+
reqs.insert(req.clone());
462459
let dl = Download {
463460
token,
464461
data: RefCell::new(Vec::new()),
465462
path: path.to_path_buf(),
466463
name,
467-
req: req.clone(),
468-
additional_reqs: HashSet::new(),
464+
reqs,
469465
etag: RefCell::new(None),
470466
last_modified: RefCell::new(None),
471467
};
@@ -540,26 +536,8 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> {
540536
//
541537
// TODO: Use the nightly BTreeMap::pop_first when stable.
542538
if let Some(path) = self.downloads.eager.keys().next().cloned() {
543-
use std::collections::btree_map::Entry;
544-
let mut fetched = if let Entry::Occupied(o) = self.downloads.eager.entry(path) {
545-
o
546-
} else {
547-
unreachable!();
548-
};
549-
550-
return if let Some(req) = fetched.get().others.iter().next().cloned() {
551-
let fetched = fetched.get_mut();
552-
fetched.others.remove(&req);
553-
let ret = Ok(Some(Fetched {
554-
path: fetched.primary.path.clone(),
555-
name: fetched.primary.name,
556-
req,
557-
}));
558-
ret
559-
} else {
560-
let fetched = fetched.remove();
561-
Ok(Some(fetched.primary))
562-
};
539+
let fetched = self.downloads.eager.remove(&path).unwrap();
540+
return Ok(Some(fetched));
563541
}
564542

565543
// We don't have any fetched results immediately ready to be yielded,
@@ -606,31 +584,28 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> {
606584
let mut handle = self.prefetch.remove(handle)?;
607585
self.downloads.pending_ids.remove(&dl.path);
608586

609-
let fetched = MultiVersionFetched {
610-
primary: Fetched {
611-
path: dl.path,
612-
name: dl.name,
613-
req: dl.req,
614-
},
615-
others: dl.additional_reqs,
587+
let fetched = Fetched {
588+
path: dl.path,
589+
name: dl.name,
590+
reqs: dl.reqs,
616591
};
617592
assert!(
618-
self.prefetched.insert(fetched.primary.path.clone()),
593+
self.prefetched.insert(fetched.path.clone()),
619594
"downloaded the same path twice during prefetching"
620595
);
621596

622597
let code = handle.response_code()?;
623598
debug!(
624599
"index file for {} downloaded with status code {}",
625-
fetched.primary.name,
600+
fetched.name,
626601
handle.response_code()?
627602
);
628603
match code {
629604
200 => {
630605
// We got data back, hooray!
631606
// Let's update the index file.
632607
let path = self.config.assert_package_cache_locked(&self.index_path);
633-
let pkg = path.join(&fetched.primary.path);
608+
let pkg = path.join(&fetched.path);
634609
paths::create_dir_all(pkg.parent().expect("pkg is a file"))?;
635610
let mut file = paths::create(pkg)?;
636611
file.write_all(dl.etag.into_inner().as_deref().unwrap_or("\n").as_bytes())?;
@@ -647,7 +622,7 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> {
647622
assert!(
648623
self.downloads
649624
.eager
650-
.insert(fetched.primary.path.clone(), fetched)
625+
.insert(fetched.path.clone(), fetched)
651626
.is_none(),
652627
"download finished for already-finished path"
653628
);
@@ -659,7 +634,7 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> {
659634
assert!(
660635
self.downloads
661636
.eager
662-
.insert(fetched.primary.path.clone(), fetched)
637+
.insert(fetched.path.clone(), fetched)
663638
.is_none(),
664639
"download finished for already-finished path"
665640
);
@@ -1502,11 +1477,6 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> {
15021477
}
15031478
}
15041479

1505-
struct MultiVersionFetched {
1506-
primary: Fetched,
1507-
others: HashSet<semver::VersionReq>,
1508-
}
1509-
15101480
// NOTE: what follows is lifted from src/cargo/core/package.rs and tweaked
15111481

15121482
/// Helper for downloading crates.
@@ -1526,7 +1496,7 @@ pub struct Downloads {
15261496
results: Vec<(usize, Result<(), curl::Error>)>,
15271497
/// Prefetch requests that we already have a response to.
15281498
/// NOTE: Should this maybe be some kind of heap?
1529-
eager: BTreeMap<PathBuf, MultiVersionFetched>,
1499+
eager: BTreeMap<PathBuf, Fetched>,
15301500
/// The next ID to use for creating a token (see `Download::token`).
15311501
next: usize,
15321502
}
@@ -1543,10 +1513,8 @@ struct Download {
15431513
name: InternedString,
15441514

15451515
/// The version requirements for the dependency line that triggered this fetch.
1546-
req: semver::VersionReq,
1547-
1548-
/// Additional version requirements for same package.
1549-
additional_reqs: HashSet<semver::VersionReq>,
1516+
// NOTE: with https://github.com/steveklabnik/semver/issues/170 the HashSet is unnecessary
1517+
reqs: HashSet<semver::VersionReq>,
15501518

15511519
/// Actual downloaded data, updated throughout the lifetime of this download.
15521520
data: RefCell<Vec<u8>>,

src/cargo/sources/registry/index.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,7 @@ impl<'cfg> RegistryIndex<'cfg> {
537537
};
538538

539539
for (version, maybe_summary) in &mut summaries.versions {
540-
if !fetched.version_req().matches(&version) {
540+
if !fetched.version_reqs().any(|vr| vr.matches(&version)) {
541541
// The crate that pulled in this crate as a dependency did not care about this
542542
// particular version, so we don't need to walk its dependencies.
543543
//

src/cargo/sources/registry/mod.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,8 @@ impl<'a> RegistryDependency<'a> {
375375
pub struct Fetched {
376376
name: InternedString,
377377
path: PathBuf,
378-
req: semver::VersionReq,
378+
// NOTE: with https://github.com/steveklabnik/semver/issues/170 the HashSet is unnecessary
379+
reqs: HashSet<semver::VersionReq>,
379380
}
380381

381382
impl Fetched {
@@ -387,8 +388,8 @@ impl Fetched {
387388
&self.path
388389
}
389390

390-
pub fn version_req(&self) -> &semver::VersionReq {
391-
&self.req
391+
pub fn version_reqs(&self) -> impl Iterator<Item = &semver::VersionReq> {
392+
self.reqs.iter()
392393
}
393394
}
394395

0 commit comments

Comments
 (0)