Switch to ImDocument in BevyManifest (#18272)

# Objective

When reviewing #18263, I noticed that `BevyManifest` internally stores a
`DocumentMut`, a mutable TOML document, instead of an `ImDocument`, an
immutable one. The process of creating a `DocumentMut` first involves
creating a `ImDocument` and then cloning all the referenced spans of
text into their own allocations (internally referred to as `despan` in
`toml_edit`). As such, using a `DocumentMut` without mutation is
strictly additional overhead.

In addition, I noticed that the filesystem operations associated with
reading a manifest and parsing it were written to be completed _while_ a
write-lock was held on `MANIFESTS`. This likely doesn't translate into a
performance or deadlock issue as the manifest files are generally small
and can be read quickly, but it is generally considered a bad practice.

## Solution

- Switched to `ImDocument<Box<str>>` instead of `DocumentMut`
- Re-ordered operations in `BevyManifest::shared` to minimise time spent
holding open the write-lock on `MANIFESTS`

## Testing

- CI

---

## Notes

I wasn't able to measure a meaningful performance difference with this
PR, so this is purely a code quality change and not one for performance.

---------

Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
Zachary Harrold 2025-03-13 07:15:39 +11:00 committed by GitHub
parent 81c0900cf2
commit dc7cb2a3e1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -8,12 +8,12 @@ use std::{
path::{Path, PathBuf},
time::SystemTime,
};
use toml_edit::{DocumentMut, Item};
use toml_edit::{ImDocument, Item};
/// The path to the `Cargo.toml` file for the Bevy project.
#[derive(Debug)]
pub struct BevyManifest {
manifest: DocumentMut,
manifest: ImDocument<Box<str>>,
modified_time: SystemTime,
}
@ -34,14 +34,15 @@ impl BevyManifest {
return manifest;
}
}
let manifest = BevyManifest {
manifest: Self::read_manifest(&manifest_path),
modified_time,
};
let key = manifest_path.clone();
let mut manifests = MANIFESTS.write();
manifests.insert(
manifest_path.clone(),
BevyManifest {
manifest: Self::read_manifest(&manifest_path),
modified_time,
},
);
manifests.insert(key, manifest);
RwLockReadGuard::map(RwLockWriteGuard::downgrade(manifests), |manifests| {
manifests.get(&manifest_path).unwrap()
@ -69,11 +70,11 @@ impl BevyManifest {
std::fs::metadata(cargo_manifest_path).and_then(|metadata| metadata.modified())
}
fn read_manifest(path: &Path) -> DocumentMut {
fn read_manifest(path: &Path) -> ImDocument<Box<str>> {
let manifest = std::fs::read_to_string(path)
.unwrap_or_else(|_| panic!("Unable to read cargo manifest: {}", path.display()));
manifest
.parse::<DocumentMut>()
.unwrap_or_else(|_| panic!("Unable to read cargo manifest: {}", path.display()))
.into_boxed_str();
ImDocument::parse(manifest)
.unwrap_or_else(|_| panic!("Failed to parse cargo manifest: {}", path.display()))
}