fix(qt): break the activeTab binding loop, and time the ephemeral open
BINDING LOOP. The Binding's `when` read `viewLoader.item.activeTab` —
through `typeof`, which still registers a dependency — while the Binding
WRITES that same property. Every flyout navigation logged
QML Binding: Binding loop detected for property "when"
Pre-existing, not from the two-phase commit: reproduced on the
pre-2026-08-21 binary by navigating the same way, where it logs from the
same Binding at its own line number. The guard now lives in a handler on
the Loader's `item`, which runs once per mount and registers no
dependency. Verified by log: 0 "Binding loop", and 0 QML complaints of
any kind on a full navigation pass.
EPHEMERAL OPEN — INSTRUMENTED, NOT SOLVED. Two timers split the work
either side of the parallel read phase, because the remaining ~9 s has no
confirmed owner. The owner reports fooyin opening the SAME NAS folder
near-instantly, which rules out the network as the ceiling and makes this
our code — the obvious suspect is that we read each file's tags in
`extract` and then open and read it AGAIN in `extract_artwork`, plus
decode and re-encode a thumbnail, where a grid-only player needs none of
that up front. That is a design change, not a tweak, so it is measured
rather than guessed at.
The numbers arrive on the next real open ("[ephemeral][perf] parallel tag
reads" / "serial bookkeeping"). I could not trigger one from the test
harness: the entry point is a native folder picker, and the rehydrate
path did not fire from the surface the harness can reach.
cc
This commit is contained in:
2 files changed
+35
-2
No files matched your search
@@ -282,6 +282,7 @@ impl EphemeralLibraryState {
|
||||
// `std::thread::scope`, not a new dependency: this is I/O-bound, the
|
||||
// work items are independent, and borrowing `scan.audio_files` across
|
||||
// the scope needs no Arc.
|
||||
let t_probe = std::time::Instant::now();
|
||||
let probed: Vec<(std::path::PathBuf, Result<LocalTrack, LibraryError>)> = {
|
||||
let files: &[std::path::PathBuf] = &scan.audio_files;
|
||||
let workers = std::thread::available_parallelism()
|
||||
@@ -316,6 +317,12 @@ impl EphemeralLibraryState {
|
||||
out.into_iter().flatten().collect()
|
||||
};
|
||||
|
||||
log::info!(
|
||||
"[ephemeral][perf] parallel tag reads: {:?} for {} files",
|
||||
t_probe.elapsed(),
|
||||
probed.len()
|
||||
);
|
||||
let t_rest = std::time::Instant::now();
|
||||
for (audio_file, (canonical_audio, extracted)) in
|
||||
scan.audio_files.iter().zip(probed.into_iter())
|
||||
{
|
||||
@@ -405,6 +412,11 @@ impl EphemeralLibraryState {
|
||||
}
|
||||
}
|
||||
|
||||
log::info!(
|
||||
"[ephemeral][perf] serial bookkeeping (artwork + ids): {:?}",
|
||||
t_rest.elapsed()
|
||||
);
|
||||
|
||||
// Musical order (album, then disc/track/title — same as the DB-backed
|
||||
// folder view): the extraction order above is readdir order, which is
|
||||
// arbitrary. Ids must FOLLOW the display order because
|
||||
|
||||
Reference in new issue
Block a user