Skip to content

Commit 7ad7689

Browse files
authored
Merge pull request #196 from jacek-kurlit/statup_performance
improved startup performance and refresh speed
2 parents 8cdfd7f + d334e0a commit 7ad7689

7 files changed

Lines changed: 83 additions & 40 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "pik"
3-
version = "0.26.0"
3+
version = "0.26.1"
44
edition = "2024"
55
authors = ["Jacek Kurlit"]
66
keywords = ["terminal", "process", "linux", "system", "kill"]

pik.spec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Name: pik
2-
Version: 0.26.0
2+
Version: 0.26.1
33
Release: 1%{?dist}
44
License: MIT
55
Summary: Process Interactive Kill is a tool that helps to find and kill process

src/processes.rs

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
use std::cmp::Ordering;
22
use std::collections::{HashMap, HashSet};
3+
use std::str::FromStr;
34
use std::time::SystemTime;
45

5-
use anyhow::Result;
6+
use anyhow::{Ok, Result};
67
use itertools::Itertools;
78
use listeners::Listener;
9+
use sysinfo::ProcessRefreshKind;
810
use sysinfo::{Pid, System, Uid, Users};
9-
use sysinfo::{ProcessRefreshKind, RefreshKind};
1011

1112
mod daemon;
1213
mod filters;
@@ -130,22 +131,41 @@ impl ProcessSearchResults {
130131

131132
impl ProcessManager {
132133
pub fn new() -> Result<Self> {
133-
let sys = System::new_with_specifics(
134-
RefreshKind::default().with_processes(process_refresh_kind()),
135-
);
136-
let users = Users::new_with_refreshed_list();
137-
let process_ports = refresh_ports();
138-
let current_user_id = find_current_process_user(&sys)?;
134+
let sys = System::new();
135+
let users = Users::new();
136+
let process_ports = Default::default();
139137
Ok(Self {
140138
sys,
141139
users,
142140
process_ports,
143-
current_user_id,
141+
current_user_id: Uid::from_str("0")?,
144142
})
145143
}
146144

147-
pub fn find_processes(&mut self, query: &str, ignore: &IgnoreOptions) -> ProcessSearchResults {
145+
/// Initializes the process manager by refreshing system information and finding processes.
146+
/// This also sets the current user id which is used to filter processes.
147+
/// To be honest this method only exits so we don't try to find current user id on every search
148+
/// but only once at the beginning.
149+
pub fn inital_search(
150+
&mut self,
151+
query: &str,
152+
ignore: &IgnoreOptions,
153+
) -> Result<ProcessSearchResults> {
154+
self.refresh();
155+
self.current_user_id = find_current_process_user(&self.sys)?;
156+
Ok(self.find_processes(query, ignore))
157+
}
158+
159+
pub fn refresh_and_find_processes(
160+
&mut self,
161+
query: &str,
162+
ignore: &IgnoreOptions,
163+
) -> ProcessSearchResults {
148164
self.refresh();
165+
self.find_processes(query, ignore)
166+
}
167+
168+
fn find_processes(&mut self, query: &str, ignore: &IgnoreOptions) -> ProcessSearchResults {
149169
let query_filter = QueryFilter::new(query);
150170
let ignored_processes_filter = IgnoreProcessesFilter::new(ignore, &self.current_user_id);
151171

@@ -169,15 +189,18 @@ impl ProcessManager {
169189
ProcessSearchResults { items }
170190
}
171191

192+
/// Refreshes the system information, including processes and their associated ports.
193+
/// This method spawns a separate thread to refresh the ports, as it speeds up the overall refresh process.
172194
fn refresh(&mut self) {
195+
let ports_refresh = std::thread::spawn(refresh_ports);
173196
self.sys.refresh_processes_specifics(
174197
sysinfo::ProcessesToUpdate::All,
175198
true,
176199
process_refresh_kind(),
177200
);
178-
// TODO: do we really need to refresh users?
201+
179202
self.users.refresh();
180-
self.process_ports = refresh_ports();
203+
self.process_ports = ports_refresh.join().unwrap_or_default();
181204
}
182205

183206
fn create_process_info(&self, prc: &impl ProcessInfo, ports: Option<&String>) -> Process {
@@ -235,12 +258,12 @@ fn process_refresh_kind() -> ProcessRefreshKind {
235258

236259
fn refresh_ports() -> HashMap<u32, String> {
237260
let ports = listeners::get_all()
238-
//NOTE: we ignore errors comming from listeners
261+
//NOTE: we ignore errors coming from listeners
239262
.unwrap_or_default();
240263
create_sorted_process_ports(ports)
241264
}
242265

243-
//NOTE: we sort this so order of ports is deterministic and doesn't change durig refresh
266+
//NOTE: we sort this so order of ports is deterministic and doesn't change during refresh
244267
fn create_sorted_process_ports(ports: HashSet<Listener>) -> ProcessPorts {
245268
ports
246269
.into_iter()

src/processes/daemon.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,15 @@ fn process_loop(
5454
for operation in operations.unwrap() {
5555
match operation {
5656
Operations::Search(query) => {
57-
let result = process_manager.find_processes(&query, &ignore_options);
57+
let result =
58+
process_manager.refresh_and_find_processes(&query, &ignore_options);
5859
send_result(OperationResult::SearchCompleted(result), &result_sender);
5960
last_query = query;
6061
}
6162
Operations::KillProcess(pid) => {
6263
if process_manager.kill_process(pid) {
63-
let mut search_results =
64-
process_manager.find_processes(&last_query, &ignore_options);
64+
let mut search_results = process_manager
65+
.refresh_and_find_processes(&last_query, &ignore_options);
6566
//NOTE: cache refresh takes time and process may reappear in list!
6667
search_results.remove(pid);
6768
send_result(

src/tui/components/processes_view.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,13 @@ impl ProcessesViewComponent {
4242
ignore_options: IgnoreOptions,
4343
initial_query: String,
4444
) -> Result<Self> {
45-
let (ops_sender, results_receiver) = start(ProcessManager::new()?, ignore_options);
46-
ops_sender.send(Operations::Search(initial_query.clone()))?;
45+
let mut process_manager = ProcessManager::new()?;
46+
let initial_results = process_manager.inital_search(&initial_query, &ignore_options)?;
47+
let (ops_sender, results_receiver) = start(process_manager, ignore_options);
4748
let mut component = Self {
4849
ops_sender,
4950
results_receiver,
50-
search_results: ProcessSearchResults::empty(),
51+
search_results: initial_results,
5152
process_table_component: ProcessTableComponent::new(
5253
ui_config.icons.get_icons(),
5354
// cloning for sake of simplicity
@@ -62,7 +63,7 @@ impl ProcessesViewComponent {
6263
ui_config.icons.get_icons().search_prompt.as_str(),
6364
),
6465
};
65-
component.search_for_processess();
66+
component.update_process_table_state();
6667
Ok(component)
6768
}
6869

tests/processes_search.rs

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ use fuzzy_matcher::{FuzzyMatcher, skim::SkimMatcherV2};
77
#[test]
88
fn should_find_cargo_process_by_cmd_name() {
99
let mut process_manager = ProcessManager::new().unwrap();
10-
let results = process_manager.find_processes("cargo", &IgnoreOptions::default());
10+
let results = process_manager
11+
.inital_search("cargo", &IgnoreOptions::default())
12+
.unwrap();
1113
assert!(!results.is_empty());
1214
assert!(
1315
results
@@ -20,7 +22,9 @@ fn should_find_cargo_process_by_cmd_name() {
2022
#[test]
2123
fn should_find_cargo_process_by_cmd_path() {
2224
let mut process_manager = ProcessManager::new().unwrap();
23-
let results = process_manager.find_processes("/cargo", &IgnoreOptions::default());
25+
let results = process_manager
26+
.inital_search("/cargo", &IgnoreOptions::default())
27+
.unwrap();
2428
assert!(!results.is_empty());
2529
assert!(
2630
results
@@ -33,7 +37,9 @@ fn should_find_cargo_process_by_cmd_path() {
3337
#[test]
3438
fn should_find_cargo_process_by_name_path_or_args() {
3539
let mut process_manager = ProcessManager::new().unwrap();
36-
let results = process_manager.find_processes("~cargo", &IgnoreOptions::default());
40+
let results = process_manager
41+
.inital_search("~cargo", &IgnoreOptions::default())
42+
.unwrap();
3743
assert!(!results.is_empty());
3844
assert!(results.iter().all(|item| fuzzy_matches(
3945
item.process.cmd_path.as_ref().unwrap(),
@@ -46,7 +52,9 @@ fn should_find_cargo_process_by_name_path_or_args() {
4652
#[test]
4753
fn should_find_cargo_process_by_args() {
4854
let mut process_manager = ProcessManager::new().unwrap();
49-
let results = process_manager.find_processes("-test", &IgnoreOptions::default());
55+
let results = process_manager
56+
.inital_search("-test", &IgnoreOptions::default())
57+
.unwrap();
5058
assert!(!results.is_empty());
5159
assert!(
5260
results
@@ -64,7 +72,9 @@ fn should_find_cargo_process_by_port() {
6472
// NOTE: Sometimes system needs time to notice the port is in use
6573
thread::sleep(Duration::from_millis(250));
6674
let mut process_manager = ProcessManager::new().unwrap();
67-
let results = process_manager.find_processes(&format!(":{}", port), &IgnoreOptions::default());
75+
let results = process_manager
76+
.inital_search(&format!(":{}", port), &IgnoreOptions::default())
77+
.unwrap();
6878
assert!(!results.is_empty());
6979
assert!(results.iter().all(|item| {
7080
fuzzy_matches(
@@ -78,13 +88,17 @@ fn should_find_cargo_process_by_port() {
7888
#[test]
7989
fn should_find_cargo_process_by_pid() {
8090
let mut process_manager = ProcessManager::new().unwrap();
81-
let results = process_manager.find_processes("cargo", &IgnoreOptions::default());
91+
let results = process_manager
92+
.inital_search("cargo", &IgnoreOptions::default())
93+
.unwrap();
8294
let cargo_process_pid = results.nth(Some(0)).map(|r| r.pid).unwrap();
8395

84-
let restults = process_manager.find_processes(
85-
&format!("!{}", cargo_process_pid),
86-
&IgnoreOptions::default(),
87-
);
96+
let restults = process_manager
97+
.inital_search(
98+
&format!("!{}", cargo_process_pid),
99+
&IgnoreOptions::default(),
100+
)
101+
.unwrap();
88102
assert_eq!(restults.len(), 1);
89103
assert_eq!(restults.nth(Some(0)).unwrap().pid, cargo_process_pid);
90104
assert!(results_are_sorted_by_match_type(results));
@@ -93,13 +107,17 @@ fn should_find_cargo_process_by_pid() {
93107
#[test]
94108
fn should_find_cargo_process_by_process_family() {
95109
let mut process_manager = ProcessManager::new().unwrap();
96-
let results = process_manager.find_processes("cargo", &IgnoreOptions::default());
110+
let results = process_manager
111+
.inital_search("cargo", &IgnoreOptions::default())
112+
.unwrap();
97113
let cargo_process_pid = results.nth(Some(0)).map(|r| r.pid).unwrap();
98114

99-
let results = process_manager.find_processes(
100-
&format!("@{}", cargo_process_pid),
101-
&IgnoreOptions::default(),
102-
);
115+
let results = process_manager
116+
.inital_search(
117+
&format!("@{}", cargo_process_pid),
118+
&IgnoreOptions::default(),
119+
)
120+
.unwrap();
103121
assert!(!results.is_empty());
104122
assert!(
105123
results
@@ -120,7 +138,7 @@ fn should_ignore_processes_in_usr_dir() {
120138
paths: vec![Regex::new("/usr/*").unwrap()],
121139
..Default::default()
122140
};
123-
let results = process_manager.find_processes("", &ignore);
141+
let results = process_manager.inital_search("", &ignore).unwrap();
124142
assert!(!results.is_empty());
125143
assert!(results.iter().all(|item| {
126144
item.process

0 commit comments

Comments
 (0)