Skip to content

Commit 47c1c6b

Browse files
authored
Add Signal Handling (#8)
clean up the thread
1 parent f46e818 commit 47c1c6b

File tree

4 files changed

+82
-25
lines changed

4 files changed

+82
-25
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## 0.3.1
2+
- Pull Requests
3+
- https://github.com/gsquire/topngx/pull/8
4+
- Make sure to clean the tail reading thread up.
5+
- Remove the internal buffering count to get a quicker update while tailing.
6+
17
## 0.3.0
28
- Pull Requests
39
- https://github.com/gsquire/topngx/pull/7

Cargo.lock

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

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "topngx"
3-
version = "0.3.0"
3+
version = "0.3.1"
44
authors = ["Garrett Squire <github@garrettsquire.com>"]
55
edition = "2018"
66
description = "Top for NGINX"
@@ -13,6 +13,7 @@ anyhow = "1.0"
1313
atty = "0.2"
1414
crossbeam-channel = "0.4"
1515
crossterm = "0.17"
16+
ctrlc = "3.1"
1617
env_logger = "0.7"
1718
log = "0.4"
1819
once_cell = "1.4"

src/main.rs

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
use std::fs::File;
22
use std::io::{self, BufRead, BufReader, Seek, SeekFrom, Write};
3+
use std::sync::atomic::{AtomicBool, Ordering};
4+
use std::sync::Arc;
35
use std::thread;
46
use std::time::Duration;
57

68
use anyhow::{anyhow, Result};
7-
use crossbeam_channel::{select, tick, unbounded};
9+
use crossbeam_channel::{bounded, select, tick, unbounded};
810
use crossterm::cursor::SavePosition;
911
use crossterm::execute;
1012
use crossterm::terminal::{Clear, ClearType};
@@ -117,7 +119,6 @@ fn tail(
117119
queries: Option<Vec<String>>,
118120
) -> Result<()> {
119121
const SLEEP: u64 = 100;
120-
const CHUNK: usize = 5;
121122

122123
// Save our cursor position.
123124
execute!(io::stdout(), SavePosition)?;
@@ -133,42 +134,61 @@ fn tail(
133134
let (tx, rx) = unbounded();
134135
let ticker = tick(Duration::from_secs(opts.interval));
135136

136-
thread::spawn(move || -> Result<()> {
137+
// The interrupt handling plumbing.
138+
let (stop_tx, stop_rx) = bounded(0);
139+
let running = Arc::new(AtomicBool::new(true));
140+
let handler_r = Arc::clone(&running);
141+
142+
ctrlc::set_handler(move || {
143+
handler_r.store(false, Ordering::SeqCst);
144+
})?;
145+
146+
let reader_handle = thread::spawn(move || -> Result<()> {
137147
loop {
138-
let mut line = String::new();
139-
let n_read = tail_reader.read_line(&mut line)?;
140-
141-
if n_read > 0 {
142-
len += n_read as u64;
143-
tail_reader.seek(SeekFrom::Start(len))?;
144-
line.pop(); // Remove the newline character.
145-
debug!("tail read: {}", line);
146-
tx.send(line)?;
147-
} else {
148-
debug!("tail sleeping for {} milliseconds", SLEEP);
149-
thread::sleep(Duration::from_millis(SLEEP));
148+
select! {
149+
recv(stop_rx) -> _ => { return Ok(()); }
150+
default => {
151+
let mut line = String::new();
152+
let n_read = tail_reader.read_line(&mut line)?;
153+
154+
if n_read > 0 {
155+
len += n_read as u64;
156+
tail_reader.seek(SeekFrom::Start(len))?;
157+
line.pop(); // Remove the newline character.
158+
debug!("tail read: {}", line);
159+
tx.send(line)?;
160+
} else {
161+
debug!("tail sleeping for {} milliseconds", SLEEP);
162+
thread::sleep(Duration::from_millis(SLEEP));
163+
}
164+
}
150165
}
151166
}
152167
});
153168

154-
let mut lines = Vec::with_capacity(CHUNK);
155-
loop {
169+
let mut lines = Vec::new();
170+
while running.load(Ordering::SeqCst) {
156171
select! {
157172
recv(rx) -> line => {
158173
lines.push(line?);
159-
// If we have reached our internal buffering size, write them to the SQL engine and
160-
// reset the vector.
161-
if lines.len() == CHUNK {
162-
parse_input(&lines, &pattern, &processor)?;
163-
lines.clear();
164-
}
174+
parse_input(&lines, &pattern, &processor)?;
175+
lines.clear();
165176
}
166177
recv(ticker) -> _ => {
167178
execute!(io::stdout(), Clear(ClearType::All))?;
168179
processor.report(opts.follow)?;
169180
}
170181
}
171182
}
183+
184+
// We got an interrupt, so stop the reading thread.
185+
stop_tx.send(())?;
186+
187+
// The join will panic if the thread panics but otherwise it will propagate the return value up
188+
// to the main thread.
189+
reader_handle
190+
.join()
191+
.expect("the file reading thread should not have panicked")
172192
}
173193

174194
// Either read from STDIN or the file specified.

0 commit comments

Comments
 (0)