Merge pull request #76 from vorner/cancel-guard

A cancel guard
This commit is contained in:
Francis Lalonde 2019-07-12 15:39:15 -04:00 committed by GitHub
commit fc4d7e8058
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 83 additions and 1 deletions

View File

@ -10,10 +10,47 @@ use std::sync::{Arc, Condvar, Mutex};
use std::thread;
use std::time::{Duration, Instant};
/// A guard canceling the inner handle when dropped.
///
/// See [Cancel::into_guard](trait.Cancel.html#method.into_guard) to create it.
pub struct CancelGuard<C: Cancel> {
// This is Option, so disarm can work.
// The problem is, Rust won't let us destructure self because we have a destructor.
inner: Option<C>,
}
impl<C: Cancel> CancelGuard<C> {
/// Disarms the guard.
///
/// This disposes of the guard without performing the cancelation. This is similar to calling
/// `forget` on it, but doesn't leak resources, while forget potentially could.
pub fn disarm(mut self) -> C {
self.inner.take().expect("The borrowchecker shouldn't allow anyone to call disarm twice")
}
}
impl<C: Cancel> Drop for CancelGuard<C> {
fn drop(&mut self) {
if let Some(inner) = self.inner.take() {
inner.cancel();
}
}
}
/// A deferred, repeatable, background action that can be cancelled.
pub trait Cancel {
/// Cancel the action.
fn cancel(&self);
/// Create a guard that cancels when it is dropped.
fn into_guard(self) -> CancelGuard<Self>
where
Self: Sized,
{
CancelGuard {
inner: Some(self)
}
}
}
/// A handle to cancel a scheduled task if required.
@ -188,6 +225,51 @@ pub mod test {
assert_eq!(3, trig1a.load(SeqCst));
}
#[test]
fn schedule_and_cancel_by_guard() {
let trig1a = Arc::new(AtomicUsize::new(0));
let trig1b = trig1a.clone();
let sched = Scheduler::new();
let handle1 = sched.schedule(Duration::from_millis(50), move |_| {
trig1b.fetch_add(1, SeqCst);
});
{
let _guard = handle1.into_guard();
assert_eq!(sched.task_count(), 1);
thread::sleep(Duration::from_millis(170));
assert_eq!(3, trig1a.load(SeqCst));
} // Here, the guard is dropped, cancelling
thread::sleep(Duration::from_millis(70));
assert_eq!(sched.task_count(), 0);
assert_eq!(3, trig1a.load(SeqCst));
}
#[test]
fn schedule_and_disarm_guard() {
let trig1a = Arc::new(AtomicUsize::new(0));
let trig1b = trig1a.clone();
let sched = Scheduler::new();
let handle1 = sched.schedule(Duration::from_millis(50), move |_| {
trig1b.fetch_add(1, SeqCst);
});
{
let guard = handle1.into_guard();
assert_eq!(sched.task_count(), 1);
thread::sleep(Duration::from_millis(170));
assert_eq!(3, trig1a.load(SeqCst));
guard.disarm();
}
thread::sleep(Duration::from_millis(70));
assert_eq!(sched.task_count(), 1); // Not canceled
}
#[test]
fn schedule_two_and_cancel() {
let trig1a = Arc::new(AtomicUsize::new(0));

View File

@ -64,7 +64,7 @@ pub use crate::core::label::{AppLabel, Labels, ThreadLabel};
pub use crate::core::locking::LockingOutput;
pub use crate::core::name::{MetricName, NameParts};
pub use crate::core::output::{Output, OutputDyn, OutputMetric, OutputScope};
pub use crate::core::scheduler::{Cancel, CancelHandle, ScheduleFlush};
pub use crate::core::scheduler::{Cancel, CancelGuard, CancelHandle, ScheduleFlush};
pub use crate::core::void::Void;
pub use crate::core::{Flush, MetricValue};