Repository: sharkdp/fd (language: rust)

Issue:
Use scoped threads in `spawn_receiver`
Once our MSRV supports the scoped threads API, we should switch to using scoped threads in the `spawn_receiver` function to wait for child threads to complete. 


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml
index 594706191..efc1bd30f 100644
--- a/.github/workflows/CICD.yml
+++ b/.github/workflows/CICD.yml
@@ -1,7 +1,7 @@
 name: CICD
 
 env:
-  MIN_SUPPORTED_RUST_VERSION: "1.64.0"
+  MIN_SUPPORTED_RUST_VERSION: "1.67.0"
   CICD_INTERMEDIATES_DIR: "_cicd-intermediates"
 
 on:
diff --git a/Cargo.toml b/Cargo.toml
index dabe48a76..4ffb4ac7e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,7 +18,7 @@ readme = "README.md"
 repository = "https://github.com/sharkdp/fd"
 version = "8.7.0"
 edition= "2021"
-rust-version = "1.64.0"
+rust-version = "1.67.0"
 
 [badges.appveyor]
 repository = "sharkdp/fd"
diff --git a/clippy.toml b/clippy.toml
index 22fd4be73..91884d3a0 100644
--- a/clippy.toml
+++ b/clippy.toml
@@ -1,1 +1,1 @@
-msrv = "1.64.0"
+msrv = "1.67.0"
diff --git a/src/cli.rs b/src/cli.rs
index bb00a1cbb..a60260e87 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -695,7 +695,7 @@ impl Opts {
     pub fn max_results(&self) -> Option<usize> {
         self.max_results
             .filter(|&m| m > 0)
-            .or_else(|| self.max_one_result.then(|| 1))
+            .or_else(|| self.max_one_result.then_some(1))
     }
 
     #[cfg(feature = "completions")]
diff --git a/src/exec/job.rs b/src/exec/job.rs
index b2ea0a40c..af603cc63 100644
--- a/src/exec/job.rs
+++ b/src/exec/job.rs
@@ -1,4 +1,4 @@
-use std::sync::{Arc, Mutex};
+use std::sync::Mutex;
 
 use crossbeam_channel::Receiver;
 
@@ -15,8 +15,8 @@ use super::CommandSet;
 /// be executed, and this process will continue until the receiver's sender has closed.
 pub fn job(
     rx: Receiver<WorkerResult>,
-    cmd: Arc<CommandSet>,
-    out_perm: Arc<Mutex<()>>,
+    cmd: &CommandSet,
+    out_perm: &Mutex<()>,
     config: &Config,
 ) -> ExitCode {
     // Output should be buffered when only running a single thread
@@ -41,7 +41,7 @@ pub fn job(
         results.push(cmd.execute(
             dir_entry.stripped_path(config),
             config.path_separator.as_deref(),
-            Arc::clone(&out_perm),
+            out_perm,
             buffer_output,
         ))
     }
diff --git a/src/exec/mod.rs b/src/exec/mod.rs
index 17060b373..0fa4d4c23 100644
--- a/src/exec/mod.rs
+++ b/src/exec/mod.rs
@@ -9,7 +9,7 @@ use std::io;
 use std::iter;
 use std::path::{Component, Path, PathBuf, Prefix};
 use std::process::Stdio;
-use std::sync::{Arc, Mutex};
+use std::sync::Mutex;
 
 use anyhow::{bail, Result};
 use argmax::Command;
@@ -86,14 +86,14 @@ impl CommandSet {
         &self,
         input: &Path,
         path_separator: Option<&str>,
-        out_perm: Arc<Mutex<()>>,
+        out_perm: &Mutex<()>,
         buffer_output: bool,
     ) -> ExitCode {
         let commands = self
             .commands
             .iter()
             .map(|c| c.generate(input, path_separator));
-        execute_commands(commands, &out_perm, buffer_output)
+        execute_commands(commands, out_perm, buffer_output)
     }
 
     pub fn execute_batch<I>(&self, paths: I, limit: usize, path_separator: Option<&str>) -> ExitCode
diff --git a/src/walk.rs b/src/walk.rs
index 2b03fa9a6..0e66b9ab5 100644
--- a/src/walk.rs
+++ b/src/walk.rs
@@ -347,28 +347,23 @@ fn spawn_receiver(
             if cmd.in_batch_mode() {
                 exec::batch(rx, cmd, &config)
             } else {
-                let out_perm = Arc::new(Mutex::new(()));
+                let out_perm = Mutex::new(());
 
-                // Each spawned job will store it's thread handle in here.
-                let mut handles = Vec::with_capacity(threads);
-                for _ in 0..threads {
-                    let config = Arc::clone(&config);
-                    let rx = rx.clone();
-                    let cmd = Arc::clone(cmd);
-                    let out_perm = Arc::clone(&out_perm);
+                thread::scope(|scope| {
+                    // Each spawned job will store it's thread handle in here.
+                    let mut handles = Vec::with_capacity(threads);
+                    for _ in 0..threads {
+                        let rx = rx.clone();
 
-                    // Spawn a job thread that will listen for and execute inputs.
-                    let handle = thread::spawn(move || exec::job(rx, cmd, out_perm, &config));
+                        // Spawn a job thread that will listen for and execute inputs.
+                        let handle = scope.spawn(|| exec::job(rx, cmd, &out_perm, &config));
 
-                    // Push the handle of the spawned thread into the vector for later joining.
-                    handles.push(handle);
-                }
-
-                let exit_codes = handles
-                    .into_iter()
-                    .map(|handle| handle.join().unwrap())
-                    .collect::<Vec<_>>();
-                merge_exitcodes(exit_codes)
+                        // Push the handle of the spawned thread into the vector for later joining.
+                        handles.push(handle);
+                    }
+                    let exit_codes = handles.into_iter().map(|handle| handle.join().unwrap());
+                    merge_exitcodes(exit_codes)
+                })
             }
         } else {
             let stdout = io::stdout();


Test patch: