Skip to content

Commit 9eec5f5

Browse files
committed
fix: store cached LocatorResult as Arc to avoid deep clone on hit (PR #458)
1 parent 27d5e26 commit 9eec5f5

2 files changed

Lines changed: 64 additions & 46 deletions

File tree

crates/pet-windows-registry/src/environments.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,7 @@ fn get_registry_pythons_for_hive(
142142
.into_iter()
143143
.map(|(company, company_key)| {
144144
// Build the panic-warning label up-front so a panicking
145-
// company thread is identifiable in logs (issue #454
146-
// Copilot review feedback).
145+
// company thread is identifiable in logs (issue #454).
147146
let label = format!("{name}\\Software\\Python\\{company}");
148147
let handle = s.spawn(move || {
149148
// Trace order is intentionally relaxed: companies are

crates/pet-windows-registry/src/lib.rs

Lines changed: 63 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ pub struct WindowsRegistry {
1919
#[allow(dead_code)]
2020
conda_locator: Arc<dyn CondaLocator>,
2121
#[allow(dead_code)]
22-
search_result: Arc<Mutex<Option<LocatorResult>>>,
22+
search_result: Arc<Mutex<Option<Arc<LocatorResult>>>>,
2323
}
2424

2525
impl WindowsRegistry {
@@ -30,17 +30,17 @@ impl WindowsRegistry {
3030
}
3131
}
3232
#[cfg(windows)]
33-
fn find_with_cache(&self, reporter: Option<&dyn Reporter>) -> Option<LocatorResult> {
33+
fn find_with_cache(&self, reporter: Option<&dyn Reporter>) -> Option<Arc<LocatorResult>> {
3434
let mut result = self
3535
.search_result
3636
.lock()
3737
.expect("search_result mutex poisoned");
38-
if let Some(result) = result.clone() {
39-
return Some(result);
38+
if let Some(result) = result.as_ref() {
39+
return Some(Arc::clone(result));
4040
}
4141

42-
let registry_result = get_registry_pythons(&self.conda_locator, &reporter);
43-
result.replace(registry_result.clone());
42+
let registry_result = Arc::new(get_registry_pythons(&self.conda_locator, &reporter));
43+
result.replace(Arc::clone(&registry_result));
4444

4545
Some(registry_result)
4646
}
@@ -111,10 +111,10 @@ impl Locator for WindowsRegistry {
111111
#[cfg(windows)]
112112
if let Some(result) = self.find_with_cache(None) {
113113
// Find the same env here
114-
for found_env in result.environments {
114+
for found_env in &result.environments {
115115
if let Some(ref python_executable_path) = found_env.executable {
116116
if python_executable_path == &env.executable {
117-
return Some(found_env);
117+
return Some(found_env.clone());
118118
}
119119
}
120120
}
@@ -135,12 +135,15 @@ impl Locator for WindowsRegistry {
135135
// replay the cached environments/managers to the reporter
136136
// ourselves — otherwise WindowsRegistry discoveries would silently
137137
// disappear on every refresh after the first.
138+
//
139+
// The cache stores an `Arc<LocatorResult>`, so the lookup is a
140+
// cheap pointer clone — no deep copy of the underlying vectors.
138141
let cached = {
139142
let result = self
140143
.search_result
141144
.lock()
142145
.expect("search_result mutex poisoned");
143-
result.clone()
146+
result.as_ref().map(Arc::clone)
144147
};
145148
if let Some(cached) = cached {
146149
for manager in &cached.managers {
@@ -221,24 +224,28 @@ mod tests {
221224
let shared = create_locator();
222225
let refreshed = create_locator();
223226

224-
shared.search_result.lock().unwrap().replace(LocatorResult {
225-
managers: vec![],
226-
environments: vec![PythonEnvironment {
227-
name: Some("stale".to_string()),
228-
..Default::default()
229-
}],
230-
});
227+
shared
228+
.search_result
229+
.lock()
230+
.unwrap()
231+
.replace(Arc::new(LocatorResult {
232+
managers: vec![],
233+
environments: vec![PythonEnvironment {
234+
name: Some("stale".to_string()),
235+
..Default::default()
236+
}],
237+
}));
231238
refreshed
232239
.search_result
233240
.lock()
234241
.unwrap()
235-
.replace(LocatorResult {
242+
.replace(Arc::new(LocatorResult {
236243
managers: vec![],
237244
environments: vec![PythonEnvironment {
238245
name: Some("fresh".to_string()),
239246
..Default::default()
240247
}],
241-
});
248+
}));
242249

243250
shared.sync_refresh_state_from(&refreshed, &RefreshStateSyncScope::Full);
244251

@@ -251,24 +258,28 @@ mod tests {
251258
let shared = create_locator();
252259
let refreshed = create_locator();
253260

254-
shared.search_result.lock().unwrap().replace(LocatorResult {
255-
managers: vec![],
256-
environments: vec![PythonEnvironment {
257-
name: Some("stale".to_string()),
258-
..Default::default()
259-
}],
260-
});
261+
shared
262+
.search_result
263+
.lock()
264+
.unwrap()
265+
.replace(Arc::new(LocatorResult {
266+
managers: vec![],
267+
environments: vec![PythonEnvironment {
268+
name: Some("stale".to_string()),
269+
..Default::default()
270+
}],
271+
}));
261272
refreshed
262273
.search_result
263274
.lock()
264275
.unwrap()
265-
.replace(LocatorResult {
276+
.replace(Arc::new(LocatorResult {
266277
managers: vec![],
267278
environments: vec![PythonEnvironment {
268279
name: Some("fresh".to_string()),
269280
..Default::default()
270281
}],
271-
});
282+
}));
272283

273284
shared.sync_refresh_state_from(&refreshed, &RefreshStateSyncScope::Workspace);
274285

@@ -281,24 +292,28 @@ mod tests {
281292
let shared = create_locator();
282293
let refreshed = create_locator();
283294

284-
shared.search_result.lock().unwrap().replace(LocatorResult {
285-
managers: vec![],
286-
environments: vec![PythonEnvironment {
287-
name: Some("stale".to_string()),
288-
..Default::default()
289-
}],
290-
});
295+
shared
296+
.search_result
297+
.lock()
298+
.unwrap()
299+
.replace(Arc::new(LocatorResult {
300+
managers: vec![],
301+
environments: vec![PythonEnvironment {
302+
name: Some("stale".to_string()),
303+
..Default::default()
304+
}],
305+
}));
291306
refreshed
292307
.search_result
293308
.lock()
294309
.unwrap()
295-
.replace(LocatorResult {
310+
.replace(Arc::new(LocatorResult {
296311
managers: vec![],
297312
environments: vec![PythonEnvironment {
298313
name: Some("fresh".to_string()),
299314
..Default::default()
300315
}],
301-
});
316+
}));
302317

303318
shared.sync_refresh_state_from(
304319
&refreshed,
@@ -307,13 +322,17 @@ mod tests {
307322
let result = shared.search_result.lock().unwrap().clone().unwrap();
308323
assert_eq!(result.environments[0].name.as_deref(), Some("fresh"));
309324

310-
shared.search_result.lock().unwrap().replace(LocatorResult {
311-
managers: vec![],
312-
environments: vec![PythonEnvironment {
313-
name: Some("stale".to_string()),
314-
..Default::default()
315-
}],
316-
});
325+
shared
326+
.search_result
327+
.lock()
328+
.unwrap()
329+
.replace(Arc::new(LocatorResult {
330+
managers: vec![],
331+
environments: vec![PythonEnvironment {
332+
name: Some("stale".to_string()),
333+
..Default::default()
334+
}],
335+
}));
317336

318337
shared.sync_refresh_state_from(
319338
&refreshed,
@@ -402,7 +421,7 @@ mod tests {
402421
.search_result
403422
.lock()
404423
.unwrap()
405-
.replace(cached.clone());
424+
.replace(Arc::new(cached.clone()));
406425

407426
let reporter = RecordingReporter::default();
408427
locator.find(&reporter);

0 commit comments

Comments
 (0)