|
@@ -1,5 +1,5 @@
|
|
|
#![feature(collections, convert, core, exit_status, file_type, fs_ext, fs_mode)]
|
|
#![feature(collections, convert, core, exit_status, file_type, fs_ext, fs_mode)]
|
|
|
-#![feature(metadata_ext, raw_ext, scoped, symlink_metadata)]
|
|
|
|
|
|
|
+#![feature(metadata_ext, raw_ext, symlink_metadata)]
|
|
|
|
|
|
|
|
extern crate ansi_term;
|
|
extern crate ansi_term;
|
|
|
extern crate datetime;
|
|
extern crate datetime;
|
|
@@ -10,6 +10,7 @@ extern crate natord;
|
|
|
extern crate num_cpus;
|
|
extern crate num_cpus;
|
|
|
extern crate number_prefix;
|
|
extern crate number_prefix;
|
|
|
extern crate pad;
|
|
extern crate pad;
|
|
|
|
|
+extern crate threadpool;
|
|
|
extern crate users;
|
|
extern crate users;
|
|
|
extern crate unicode_width;
|
|
extern crate unicode_width;
|
|
|
|
|
|
|
@@ -19,8 +20,9 @@ extern crate git2;
|
|
|
use std::env;
|
|
use std::env;
|
|
|
use std::fs;
|
|
use std::fs;
|
|
|
use std::path::{Component, Path, PathBuf};
|
|
use std::path::{Component, Path, PathBuf};
|
|
|
-use std::sync::mpsc::{channel, sync_channel};
|
|
|
|
|
-use std::thread;
|
|
|
|
|
|
|
+
|
|
|
|
|
+use threadpool::ThreadPool;
|
|
|
|
|
+use std::sync::mpsc::channel;
|
|
|
|
|
|
|
|
use dir::Dir;
|
|
use dir::Dir;
|
|
|
use file::File;
|
|
use file::File;
|
|
@@ -36,6 +38,7 @@ mod options;
|
|
|
mod output;
|
|
mod output;
|
|
|
mod term;
|
|
mod term;
|
|
|
|
|
|
|
|
|
|
+
|
|
|
#[cfg(not(test))]
|
|
#[cfg(not(test))]
|
|
|
struct Exa<'dir> {
|
|
struct Exa<'dir> {
|
|
|
count: usize,
|
|
count: usize,
|
|
@@ -56,15 +59,13 @@ impl<'dir> Exa<'dir> {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
fn load(&mut self, files: &[String]) {
|
|
fn load(&mut self, files: &[String]) {
|
|
|
|
|
+
|
|
|
// Separate the user-supplied paths into directories and files.
|
|
// Separate the user-supplied paths into directories and files.
|
|
|
// Files are shown first, and then each directory is expanded
|
|
// Files are shown first, and then each directory is expanded
|
|
|
// and listed second.
|
|
// and listed second.
|
|
|
-
|
|
|
|
|
let is_tree = self.options.dir_action.is_tree() || self.options.dir_action.is_as_file();
|
|
let is_tree = self.options.dir_action.is_tree() || self.options.dir_action.is_as_file();
|
|
|
let total_files = files.len();
|
|
let total_files = files.len();
|
|
|
|
|
|
|
|
- // Denotes the maxinum number of concurrent threads
|
|
|
|
|
- let (thread_capacity_tx, thread_capacity_rs) = sync_channel(8 * num_cpus::get());
|
|
|
|
|
|
|
|
|
|
// Communication between consumer thread and producer threads
|
|
// Communication between consumer thread and producer threads
|
|
|
enum StatResult<'dir> {
|
|
enum StatResult<'dir> {
|
|
@@ -73,39 +74,17 @@ impl<'dir> Exa<'dir> {
|
|
|
Error
|
|
Error
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- let (results_tx, results_rx) = channel();
|
|
|
|
|
-
|
|
|
|
|
- // Spawn consumer thread
|
|
|
|
|
- let _consumer = thread::scoped(move || {
|
|
|
|
|
- for _ in 0..total_files {
|
|
|
|
|
-
|
|
|
|
|
- // Make room for more producer threads
|
|
|
|
|
- let _ = thread_capacity_rs.recv();
|
|
|
|
|
-
|
|
|
|
|
- // Receive a producer's result
|
|
|
|
|
- match results_rx.recv() {
|
|
|
|
|
- Ok(result) => match result {
|
|
|
|
|
- StatResult::File(file) => self.files.push(file),
|
|
|
|
|
- StatResult::Dir(path) => self.dirs.push(path),
|
|
|
|
|
- StatResult::Error => ()
|
|
|
|
|
- },
|
|
|
|
|
- Err(_) => unreachable!(),
|
|
|
|
|
- }
|
|
|
|
|
- self.count += 1;
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ let pool = ThreadPool::new(8 * num_cpus::get());
|
|
|
|
|
+ let (tx, rx) = channel();
|
|
|
|
|
|
|
|
for file in files.iter() {
|
|
for file in files.iter() {
|
|
|
|
|
+ let tx = tx.clone();
|
|
|
let file = file.clone();
|
|
let file = file.clone();
|
|
|
- let results_tx = results_tx.clone();
|
|
|
|
|
-
|
|
|
|
|
- // Block until there is room for another thread
|
|
|
|
|
- let _ = thread_capacity_tx.send(());
|
|
|
|
|
|
|
|
|
|
// Spawn producer thread
|
|
// Spawn producer thread
|
|
|
- thread::spawn(move || {
|
|
|
|
|
|
|
+ pool.execute(move || {
|
|
|
let path = Path::new(&*file);
|
|
let path = Path::new(&*file);
|
|
|
- let _ = results_tx.send(match fs::metadata(&path) {
|
|
|
|
|
|
|
+ let _ = tx.send(match fs::metadata(&path) {
|
|
|
Ok(metadata) => {
|
|
Ok(metadata) => {
|
|
|
if !metadata.is_dir() {
|
|
if !metadata.is_dir() {
|
|
|
StatResult::File(File::with_metadata(metadata, &path, None, false))
|
|
StatResult::File(File::with_metadata(metadata, &path, None, false))
|
|
@@ -124,6 +103,16 @@ impl<'dir> Exa<'dir> {
|
|
|
});
|
|
});
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // Spawn consumer thread
|
|
|
|
|
+ for result in rx.iter().take(total_files) {
|
|
|
|
|
+ match result {
|
|
|
|
|
+ StatResult::File(file) => self.files.push(file),
|
|
|
|
|
+ StatResult::Dir(path) => self.dirs.push(path),
|
|
|
|
|
+ StatResult::Error => ()
|
|
|
|
|
+ }
|
|
|
|
|
+ self.count += 1;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
fn print_files(&self) {
|
|
fn print_files(&self) {
|