Commit 82e47f0
Refactor: Replace unbounded thread spawning with rayon parallel iterators (#300)
Several locations spawn unbounded threads during environment discovery,
which can cause thread exhaustion on systems with many conda
environments or symlinks.
## Changes
- **pet-conda/lib.rs**: `get_conda_environments()` now uses `par_iter()`
instead of spawning one thread per path
- **pet-conda/environment_locations.rs**:
`get_conda_environment_paths()` uses `par_iter()` for environment
enumeration
- **pet-homebrew/sym_links.rs**: `get_known_symlinks()` uses
`par_iter()` for symlink resolution
Added `rayon = "1.11.0"` to both crates.
## Before/After
```rust
// Before: unbounded thread spawning
let mut threads = vec![];
for path in paths {
threads.push(thread::spawn(move || get_conda_environment_info(&path, &mgr)));
}
// join all...
// After: bounded parallel iteration via rayon
paths.par_iter()
.filter_map(|path| get_conda_environment_info(path, manager))
.collect()
```
`crates/pet/src/find.rs` left unchanged—it uses `thread::scope` with a
fixed number of locators/directories, which is already bounded.
<!-- START COPILOT ORIGINAL PROMPT -->
<details>
<summary>Original prompt</summary>
>
> ----
>
> *This section details on the original issue you should resolve*
>
> <issue_title>Refactor: Consider using thread pool instead of unbounded
thread spawning</issue_title>
> <issue_description>## Summary
> Several locations spawn an unbounded number of threads during
environment discovery. On systems with many Python environments (e.g.,
many conda envs or large PATH), this could lead to thread exhaustion or
excessive context switching.
>
> ## Affected Locations
>
> ### 1. `crates/pet-conda/src/lib.rs` (lines 368-378)
> ```rust
> fn get_conda_environments(paths: &Vec<PathBuf>, manager:
&Option<CondaManager>) -> Vec<CondaEnvironment> {
> let mut threads = vec![];
> for path in paths {
> let path = path.clone();
> let mgr = manager.clone();
> threads.push(thread::spawn(move || {
> // ...
> }));
> }
> // ...
> }
> ```
>
> ### 2. `crates/pet-homebrew/src/sym_links.rs` (lines 33-55)
> ```rust
> let threads = symlinks
> .iter()
> .map(|symlink| {
> std::thread::spawn(move || {
> // ...
> })
> })
> .collect::<Vec<_>>();
> ```
>
> ### 3. `crates/pet/src/find.rs`
> Multiple `thread::scope` with spawns for each path/locator.
>
> ## Proposed Solutions
>
> ### Option 1: Use `rayon` for parallel iteration
> ```rust
> use rayon::prelude::*;
>
> fn get_conda_environments(paths: &[PathBuf], manager:
&Option<CondaManager>) -> Vec<CondaEnvironment> {
> paths.par_iter()
> .filter_map(|path| get_conda_environment_info(path, manager))
> .collect()
> }
> ```
>
> ### Option 2: Use bounded thread pool
> ```rust
> use std::sync::mpsc;
> use threadpool::ThreadPool;
>
> let pool = ThreadPool::new(num_cpus::get());
> for path in paths {
> pool.execute(move || {
> // ...
> });
> }
> pool.join();
> ```
>
> ### Option 3: Use `thread::scope` with chunking
> ```rust
> thread::scope(|s| {
> for chunk in paths.chunks(num_cpus::get()) {
> s.spawn(|| {
> for path in chunk {
> // process path
> }
> });
> }
> });
> ```
>
> ## Benefits
> - Controlled parallelism based on CPU count
> - Better resource management
> - Avoid thread exhaustion on systems with hundreds of environments
> - `rayon` provides work-stealing for better load balancing
>
> ## Considerations
> - `rayon` adds a dependency but is widely used and well-maintained
> - `thread::scope` (used in many places already) is good for structured
concurrency
> - The impact depends on typical environment counts
>
> ## Priority
> Low - Current implementation works but could cause issues at
scale.</issue_description>
>
> <agent_instructions>Fix the issue. See if the recommendation makes
sense and create a fix.</agent_instructions>
>
> ## Comments on the Issue (you are @copilot in this section)
>
> <comments>
> </comments>
>
</details>
<!-- START COPILOT CODING AGENT SUFFIX -->
- Fixes #288
<!-- START COPILOT CODING AGENT TIPS -->
---
💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.
---------
Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: karthiknadig <[email protected]>1 parent 72a6b2e commit 82e47f0
File tree
6 files changed
+85
-57
lines changed- crates
- pet-conda
- src
- pet-homebrew
- src
6 files changed
+85
-57
lines changedSome generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
| 22 | + | |
22 | 23 | | |
23 | 24 | | |
24 | 25 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| 12 | + | |
12 | 13 | | |
13 | 14 | | |
14 | 15 | | |
| |||
43 | 44 | | |
44 | 45 | | |
45 | 46 | | |
46 | | - | |
47 | | - | |
48 | | - | |
49 | | - | |
50 | | - | |
51 | | - | |
52 | | - | |
53 | | - | |
54 | | - | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
59 | 52 | | |
60 | 53 | | |
61 | 54 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
| 21 | + | |
21 | 22 | | |
22 | 23 | | |
23 | 24 | | |
| |||
380 | 381 | | |
381 | 382 | | |
382 | 383 | | |
383 | | - | |
384 | | - | |
385 | | - | |
386 | | - | |
387 | | - | |
388 | | - | |
389 | | - | |
390 | | - | |
391 | | - | |
392 | | - | |
393 | | - | |
394 | | - | |
395 | | - | |
396 | | - | |
397 | | - | |
398 | | - | |
399 | | - | |
400 | | - | |
401 | | - | |
402 | | - | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
403 | 388 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
| 21 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| 7 | + | |
7 | 8 | | |
8 | 9 | | |
9 | 10 | | |
| |||
30 | 31 | | |
31 | 32 | | |
32 | 33 | | |
33 | | - | |
34 | | - | |
35 | | - | |
36 | | - | |
37 | | - | |
38 | | - | |
39 | | - | |
40 | | - | |
41 | | - | |
42 | | - | |
43 | | - | |
44 | | - | |
45 | | - | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
46 | 46 | | |
47 | | - | |
48 | | - | |
49 | | - | |
50 | | - | |
51 | | - | |
52 | | - | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
53 | 52 | | |
54 | | - | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
| 53 | + | |
59 | 54 | | |
60 | 55 | | |
61 | 56 | | |
| |||
0 commit comments