mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
Merge pull request #4447 from Freaky/dd-alarm-timer
dd: use an alarm thread instead of elapsed() calls
This commit is contained in:
commit
66723e0961
1 changed files with 54 additions and 8 deletions
|
@ -36,9 +36,12 @@ use std::os::unix::{
|
||||||
io::{AsRawFd, FromRawFd},
|
io::{AsRawFd, FromRawFd},
|
||||||
};
|
};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::mpsc;
|
use std::sync::{
|
||||||
|
atomic::{AtomicBool, Ordering::Relaxed},
|
||||||
|
mpsc, Arc,
|
||||||
|
};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time;
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use clap::{crate_version, Arg, Command};
|
use clap::{crate_version, Arg, Command};
|
||||||
use gcd::Gcd;
|
use gcd::Gcd;
|
||||||
|
@ -75,6 +78,45 @@ struct Settings {
|
||||||
status: Option<StatusLevel>,
|
status: Option<StatusLevel>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A timer which triggers on a given interval
|
||||||
|
///
|
||||||
|
/// After being constructed with [`Alarm::with_interval`], [`Alarm::is_triggered`]
|
||||||
|
/// will return true once per the given [`Duration`].
|
||||||
|
///
|
||||||
|
/// Can be cloned, but the trigger status is shared across all instances so only
|
||||||
|
/// the first caller each interval will yield true.
|
||||||
|
///
|
||||||
|
/// When all instances are dropped the background thread will exit on the next interval.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Alarm {
|
||||||
|
interval: Duration,
|
||||||
|
trigger: Arc<AtomicBool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Alarm {
|
||||||
|
pub fn with_interval(interval: Duration) -> Self {
|
||||||
|
let trigger = Arc::new(AtomicBool::default());
|
||||||
|
|
||||||
|
let weak_trigger = Arc::downgrade(&trigger);
|
||||||
|
thread::spawn(move || {
|
||||||
|
while let Some(trigger) = weak_trigger.upgrade() {
|
||||||
|
thread::sleep(interval);
|
||||||
|
trigger.store(true, Relaxed);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self { interval, trigger }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_triggered(&self) -> bool {
|
||||||
|
self.trigger.swap(false, Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_interval(&self) -> Duration {
|
||||||
|
self.interval
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A number in blocks or bytes
|
/// A number in blocks or bytes
|
||||||
///
|
///
|
||||||
/// Some values (seek, skip, iseek, oseek) can have values either in blocks or in bytes.
|
/// Some values (seek, skip, iseek, oseek) can have values either in blocks or in bytes.
|
||||||
|
@ -760,7 +802,7 @@ fn dd_copy(mut i: Input, mut o: Output) -> std::io::Result<()> {
|
||||||
// of its report includes the throughput in bytes per second,
|
// of its report includes the throughput in bytes per second,
|
||||||
// which requires knowing how long the process has been
|
// which requires knowing how long the process has been
|
||||||
// running.
|
// running.
|
||||||
let start = time::Instant::now();
|
let start = Instant::now();
|
||||||
|
|
||||||
// A good buffer size for reading.
|
// A good buffer size for reading.
|
||||||
//
|
//
|
||||||
|
@ -780,7 +822,6 @@ fn dd_copy(mut i: Input, mut o: Output) -> std::io::Result<()> {
|
||||||
// information.
|
// information.
|
||||||
let (prog_tx, rx) = mpsc::channel();
|
let (prog_tx, rx) = mpsc::channel();
|
||||||
let output_thread = thread::spawn(gen_prog_updater(rx, i.settings.status));
|
let output_thread = thread::spawn(gen_prog_updater(rx, i.settings.status));
|
||||||
let mut progress_as_secs = 0;
|
|
||||||
|
|
||||||
// Optimization: if no blocks are to be written, then don't
|
// Optimization: if no blocks are to be written, then don't
|
||||||
// bother allocating any buffers.
|
// bother allocating any buffers.
|
||||||
|
@ -813,6 +854,12 @@ fn dd_copy(mut i: Input, mut o: Output) -> std::io::Result<()> {
|
||||||
// This is the max size needed.
|
// This is the max size needed.
|
||||||
let mut buf = vec![BUF_INIT_BYTE; bsize];
|
let mut buf = vec![BUF_INIT_BYTE; bsize];
|
||||||
|
|
||||||
|
// Spawn a timer thread to provide a scheduled signal indicating when we
|
||||||
|
// should send an update of our progress to the reporting thread.
|
||||||
|
//
|
||||||
|
// This avoids the need to query the OS monotonic clock for every block.
|
||||||
|
let alarm = Alarm::with_interval(Duration::from_secs(1));
|
||||||
|
|
||||||
// Index in the input file where we are reading bytes and in
|
// Index in the input file where we are reading bytes and in
|
||||||
// the output file where we are writing bytes.
|
// the output file where we are writing bytes.
|
||||||
//
|
//
|
||||||
|
@ -871,9 +918,8 @@ fn dd_copy(mut i: Input, mut o: Output) -> std::io::Result<()> {
|
||||||
// error.
|
// error.
|
||||||
rstat += rstat_update;
|
rstat += rstat_update;
|
||||||
wstat += wstat_update;
|
wstat += wstat_update;
|
||||||
let prog_update = ProgUpdate::new(rstat, wstat, start.elapsed(), false);
|
if alarm.is_triggered() {
|
||||||
if prog_update.duration.as_secs() >= progress_as_secs {
|
let prog_update = ProgUpdate::new(rstat, wstat, start.elapsed(), false);
|
||||||
progress_as_secs = prog_update.duration.as_secs() + 1;
|
|
||||||
prog_tx.send(prog_update).unwrap_or(());
|
prog_tx.send(prog_update).unwrap_or(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -885,7 +931,7 @@ fn finalize<T>(
|
||||||
output: &mut Output,
|
output: &mut Output,
|
||||||
rstat: ReadStat,
|
rstat: ReadStat,
|
||||||
wstat: WriteStat,
|
wstat: WriteStat,
|
||||||
start: time::Instant,
|
start: Instant,
|
||||||
prog_tx: &mpsc::Sender<ProgUpdate>,
|
prog_tx: &mpsc::Sender<ProgUpdate>,
|
||||||
output_thread: thread::JoinHandle<T>,
|
output_thread: thread::JoinHandle<T>,
|
||||||
) -> std::io::Result<()> {
|
) -> std::io::Result<()> {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue