1*e1997b9aSAndroid Build Coastguard Worker // Copyright 2021, The Android Open Source Project 2*e1997b9aSAndroid Build Coastguard Worker // 3*e1997b9aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); 4*e1997b9aSAndroid Build Coastguard Worker // you may not use this file except in compliance with the License. 5*e1997b9aSAndroid Build Coastguard Worker // You may obtain a copy of the License at 6*e1997b9aSAndroid Build Coastguard Worker // 7*e1997b9aSAndroid Build Coastguard Worker // http://www.apache.org/licenses/LICENSE-2.0 8*e1997b9aSAndroid Build Coastguard Worker // 9*e1997b9aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software 10*e1997b9aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, 11*e1997b9aSAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*e1997b9aSAndroid Build Coastguard Worker // See the License for the specific language governing permissions and 13*e1997b9aSAndroid Build Coastguard Worker // limitations under the License. 14*e1997b9aSAndroid Build Coastguard Worker 15*e1997b9aSAndroid Build Coastguard Worker // Can be removed when instrumentations are added to keystore. 16*e1997b9aSAndroid Build Coastguard Worker #![allow(dead_code)] 17*e1997b9aSAndroid Build Coastguard Worker 18*e1997b9aSAndroid Build Coastguard Worker //! This module implements a watchdog thread. 19*e1997b9aSAndroid Build Coastguard Worker 20*e1997b9aSAndroid Build Coastguard Worker use std::{ 21*e1997b9aSAndroid Build Coastguard Worker cmp::min, 22*e1997b9aSAndroid Build Coastguard Worker collections::HashMap, 23*e1997b9aSAndroid Build Coastguard Worker sync::Arc, 24*e1997b9aSAndroid Build Coastguard Worker sync::{Condvar, Mutex, MutexGuard}, 25*e1997b9aSAndroid Build Coastguard Worker thread, 26*e1997b9aSAndroid Build Coastguard Worker }; 27*e1997b9aSAndroid Build Coastguard Worker use std::{ 28*e1997b9aSAndroid Build Coastguard Worker marker::PhantomData, 29*e1997b9aSAndroid Build Coastguard Worker time::{Duration, Instant}, 30*e1997b9aSAndroid Build Coastguard Worker }; 31*e1997b9aSAndroid Build Coastguard Worker 32*e1997b9aSAndroid Build Coastguard Worker #[cfg(test)] 33*e1997b9aSAndroid Build Coastguard Worker mod tests; 34*e1997b9aSAndroid Build Coastguard Worker 35*e1997b9aSAndroid Build Coastguard Worker /// Represents a Watchdog record. It can be created with `Watchdog::watch` or 36*e1997b9aSAndroid Build Coastguard Worker /// `Watchdog::watch_with`. It disarms the record when dropped. 37*e1997b9aSAndroid Build Coastguard Worker pub struct WatchPoint { 38*e1997b9aSAndroid Build Coastguard Worker id: &'static str, 39*e1997b9aSAndroid Build Coastguard Worker wd: Arc<Watchdog>, 40*e1997b9aSAndroid Build Coastguard Worker not_send: PhantomData<*mut ()>, // WatchPoint must not be Send. 41*e1997b9aSAndroid Build Coastguard Worker } 42*e1997b9aSAndroid Build Coastguard Worker 43*e1997b9aSAndroid Build Coastguard Worker impl Drop for WatchPoint { drop(&mut self)44*e1997b9aSAndroid Build Coastguard Worker fn drop(&mut self) { 45*e1997b9aSAndroid Build Coastguard Worker self.wd.disarm(self.id) 46*e1997b9aSAndroid Build Coastguard Worker } 47*e1997b9aSAndroid Build Coastguard Worker } 48*e1997b9aSAndroid Build Coastguard Worker 49*e1997b9aSAndroid Build Coastguard Worker #[derive(Debug, PartialEq, Eq)] 50*e1997b9aSAndroid Build Coastguard Worker enum State { 51*e1997b9aSAndroid Build Coastguard Worker NotRunning, 52*e1997b9aSAndroid Build Coastguard Worker Running, 53*e1997b9aSAndroid Build Coastguard Worker } 54*e1997b9aSAndroid Build Coastguard Worker 55*e1997b9aSAndroid Build Coastguard Worker #[derive(Debug, Clone, Hash, PartialEq, Eq)] 56*e1997b9aSAndroid Build Coastguard Worker struct Index { 57*e1997b9aSAndroid Build Coastguard Worker tid: thread::ThreadId, 58*e1997b9aSAndroid Build Coastguard Worker id: &'static str, 59*e1997b9aSAndroid Build Coastguard Worker } 60*e1997b9aSAndroid Build Coastguard Worker 61*e1997b9aSAndroid Build Coastguard Worker struct Record { 62*e1997b9aSAndroid Build Coastguard Worker started: Instant, 63*e1997b9aSAndroid Build Coastguard Worker deadline: Instant, 64*e1997b9aSAndroid Build Coastguard Worker context: Option<Box<dyn std::fmt::Debug + Send + 'static>>, 65*e1997b9aSAndroid Build Coastguard Worker } 66*e1997b9aSAndroid Build Coastguard Worker 67*e1997b9aSAndroid Build Coastguard Worker struct WatchdogState { 68*e1997b9aSAndroid Build Coastguard Worker state: State, 69*e1997b9aSAndroid Build Coastguard Worker thread: Option<thread::JoinHandle<()>>, 70*e1997b9aSAndroid Build Coastguard Worker /// How long to wait before dropping the watchdog thread when idle. 71*e1997b9aSAndroid Build Coastguard Worker idle_timeout: Duration, 72*e1997b9aSAndroid Build Coastguard Worker records: HashMap<Index, Record>, 73*e1997b9aSAndroid Build Coastguard Worker last_report: Option<Instant>, 74*e1997b9aSAndroid Build Coastguard Worker noisy_timeout: Duration, 75*e1997b9aSAndroid Build Coastguard Worker } 76*e1997b9aSAndroid Build Coastguard Worker 77*e1997b9aSAndroid Build Coastguard Worker impl WatchdogState { 78*e1997b9aSAndroid Build Coastguard Worker /// If we have overdue records, we want to log them but slowly backoff 79*e1997b9aSAndroid Build Coastguard Worker /// so that we do not clog the logs. We start with logs every 80*e1997b9aSAndroid Build Coastguard Worker /// `MIN_REPORT_TIMEOUT` sec then increment the timeout by 5 up 81*e1997b9aSAndroid Build Coastguard Worker /// to a maximum of `MAX_REPORT_TIMEOUT`. 82*e1997b9aSAndroid Build Coastguard Worker const MIN_REPORT_TIMEOUT: Duration = Duration::from_secs(1); 83*e1997b9aSAndroid Build Coastguard Worker const MAX_REPORT_TIMEOUT: Duration = Duration::from_secs(30); 84*e1997b9aSAndroid Build Coastguard Worker reset_noisy_timeout(&mut self)85*e1997b9aSAndroid Build Coastguard Worker fn reset_noisy_timeout(&mut self) { 86*e1997b9aSAndroid Build Coastguard Worker self.noisy_timeout = Self::MIN_REPORT_TIMEOUT; 87*e1997b9aSAndroid Build Coastguard Worker } 88*e1997b9aSAndroid Build Coastguard Worker update_noisy_timeout(&mut self)89*e1997b9aSAndroid Build Coastguard Worker fn update_noisy_timeout(&mut self) { 90*e1997b9aSAndroid Build Coastguard Worker let noisy_update = self.noisy_timeout + Duration::from_secs(5); 91*e1997b9aSAndroid Build Coastguard Worker self.noisy_timeout = min(Self::MAX_REPORT_TIMEOUT, noisy_update); 92*e1997b9aSAndroid Build Coastguard Worker } 93*e1997b9aSAndroid Build Coastguard Worker overdue_and_next_timeout(&self) -> (bool, Option<Duration>)94*e1997b9aSAndroid Build Coastguard Worker fn overdue_and_next_timeout(&self) -> (bool, Option<Duration>) { 95*e1997b9aSAndroid Build Coastguard Worker let now = Instant::now(); 96*e1997b9aSAndroid Build Coastguard Worker let mut next_timeout: Option<Duration> = None; 97*e1997b9aSAndroid Build Coastguard Worker let mut has_overdue = false; 98*e1997b9aSAndroid Build Coastguard Worker for (_, r) in self.records.iter() { 99*e1997b9aSAndroid Build Coastguard Worker let timeout = r.deadline.saturating_duration_since(now); 100*e1997b9aSAndroid Build Coastguard Worker if timeout == Duration::new(0, 0) { 101*e1997b9aSAndroid Build Coastguard Worker // This timeout has passed. 102*e1997b9aSAndroid Build Coastguard Worker has_overdue = true; 103*e1997b9aSAndroid Build Coastguard Worker } else { 104*e1997b9aSAndroid Build Coastguard Worker // This timeout is still to come; see if it's the closest one to now. 105*e1997b9aSAndroid Build Coastguard Worker next_timeout = match next_timeout { 106*e1997b9aSAndroid Build Coastguard Worker Some(nt) if timeout < nt => Some(timeout), 107*e1997b9aSAndroid Build Coastguard Worker Some(nt) => Some(nt), 108*e1997b9aSAndroid Build Coastguard Worker None => Some(timeout), 109*e1997b9aSAndroid Build Coastguard Worker }; 110*e1997b9aSAndroid Build Coastguard Worker } 111*e1997b9aSAndroid Build Coastguard Worker } 112*e1997b9aSAndroid Build Coastguard Worker (has_overdue, next_timeout) 113*e1997b9aSAndroid Build Coastguard Worker } 114*e1997b9aSAndroid Build Coastguard Worker log_report(&mut self, has_overdue: bool)115*e1997b9aSAndroid Build Coastguard Worker fn log_report(&mut self, has_overdue: bool) { 116*e1997b9aSAndroid Build Coastguard Worker if !has_overdue { 117*e1997b9aSAndroid Build Coastguard Worker // Nothing to report. 118*e1997b9aSAndroid Build Coastguard Worker self.last_report = None; 119*e1997b9aSAndroid Build Coastguard Worker return; 120*e1997b9aSAndroid Build Coastguard Worker } 121*e1997b9aSAndroid Build Coastguard Worker // Something to report... 122*e1997b9aSAndroid Build Coastguard Worker if let Some(reported_at) = self.last_report { 123*e1997b9aSAndroid Build Coastguard Worker if reported_at.elapsed() < self.noisy_timeout { 124*e1997b9aSAndroid Build Coastguard Worker // .. but it's too soon since the last report. 125*e1997b9aSAndroid Build Coastguard Worker self.last_report = None; 126*e1997b9aSAndroid Build Coastguard Worker return; 127*e1997b9aSAndroid Build Coastguard Worker } 128*e1997b9aSAndroid Build Coastguard Worker } 129*e1997b9aSAndroid Build Coastguard Worker self.update_noisy_timeout(); 130*e1997b9aSAndroid Build Coastguard Worker self.last_report = Some(Instant::now()); 131*e1997b9aSAndroid Build Coastguard Worker log::warn!("### Keystore Watchdog report - BEGIN ###"); 132*e1997b9aSAndroid Build Coastguard Worker 133*e1997b9aSAndroid Build Coastguard Worker let now = Instant::now(); 134*e1997b9aSAndroid Build Coastguard Worker let mut overdue_records: Vec<(&Index, &Record)> = self 135*e1997b9aSAndroid Build Coastguard Worker .records 136*e1997b9aSAndroid Build Coastguard Worker .iter() 137*e1997b9aSAndroid Build Coastguard Worker .filter(|(_, r)| r.deadline.saturating_duration_since(now) == Duration::new(0, 0)) 138*e1997b9aSAndroid Build Coastguard Worker .collect(); 139*e1997b9aSAndroid Build Coastguard Worker 140*e1997b9aSAndroid Build Coastguard Worker log::warn!("When extracting from a bug report, please include this header"); 141*e1997b9aSAndroid Build Coastguard Worker log::warn!("and all {} records below.", overdue_records.len()); 142*e1997b9aSAndroid Build Coastguard Worker 143*e1997b9aSAndroid Build Coastguard Worker // Watch points can be nested, i.e., a single thread may have multiple armed 144*e1997b9aSAndroid Build Coastguard Worker // watch points. And the most recent on each thread (thread recent) is closest to the point 145*e1997b9aSAndroid Build Coastguard Worker // where something is blocked. Furthermore, keystore2 has various critical section 146*e1997b9aSAndroid Build Coastguard Worker // and common backend resources KeyMint that can only be entered serialized. So if one 147*e1997b9aSAndroid Build Coastguard Worker // thread hangs, the others will soon follow suite. Thus the oldest "thread recent" watch 148*e1997b9aSAndroid Build Coastguard Worker // point is most likely pointing toward the culprit. 149*e1997b9aSAndroid Build Coastguard Worker // Thus, sort by start time first. 150*e1997b9aSAndroid Build Coastguard Worker overdue_records.sort_unstable_by(|(_, r1), (_, r2)| r1.started.cmp(&r2.started)); 151*e1997b9aSAndroid Build Coastguard Worker // Then we groups all of the watch points per thread preserving the order within 152*e1997b9aSAndroid Build Coastguard Worker // groups. 153*e1997b9aSAndroid Build Coastguard Worker let groups = overdue_records.iter().fold( 154*e1997b9aSAndroid Build Coastguard Worker HashMap::<thread::ThreadId, Vec<(&Index, &Record)>>::new(), 155*e1997b9aSAndroid Build Coastguard Worker |mut acc, (i, r)| { 156*e1997b9aSAndroid Build Coastguard Worker acc.entry(i.tid).or_default().push((i, r)); 157*e1997b9aSAndroid Build Coastguard Worker acc 158*e1997b9aSAndroid Build Coastguard Worker }, 159*e1997b9aSAndroid Build Coastguard Worker ); 160*e1997b9aSAndroid Build Coastguard Worker // Put the groups back into a vector. 161*e1997b9aSAndroid Build Coastguard Worker let mut groups: Vec<Vec<(&Index, &Record)>> = groups.into_values().collect(); 162*e1997b9aSAndroid Build Coastguard Worker // Sort the groups by start time of the most recent (.last()) of each group. 163*e1997b9aSAndroid Build Coastguard Worker // It is panic safe to use unwrap() here because we never add empty vectors to 164*e1997b9aSAndroid Build Coastguard Worker // the map. 165*e1997b9aSAndroid Build Coastguard Worker groups.sort_by(|v1, v2| v1.last().unwrap().1.started.cmp(&v2.last().unwrap().1.started)); 166*e1997b9aSAndroid Build Coastguard Worker 167*e1997b9aSAndroid Build Coastguard Worker for g in groups.iter() { 168*e1997b9aSAndroid Build Coastguard Worker for (i, r) in g.iter() { 169*e1997b9aSAndroid Build Coastguard Worker match &r.context { 170*e1997b9aSAndroid Build Coastguard Worker Some(ctx) => { 171*e1997b9aSAndroid Build Coastguard Worker log::warn!( 172*e1997b9aSAndroid Build Coastguard Worker "{:?} {} Pending: {:?} Overdue {:?} for {:?}", 173*e1997b9aSAndroid Build Coastguard Worker i.tid, 174*e1997b9aSAndroid Build Coastguard Worker i.id, 175*e1997b9aSAndroid Build Coastguard Worker r.started.elapsed(), 176*e1997b9aSAndroid Build Coastguard Worker r.deadline.elapsed(), 177*e1997b9aSAndroid Build Coastguard Worker ctx 178*e1997b9aSAndroid Build Coastguard Worker ); 179*e1997b9aSAndroid Build Coastguard Worker } 180*e1997b9aSAndroid Build Coastguard Worker None => { 181*e1997b9aSAndroid Build Coastguard Worker log::warn!( 182*e1997b9aSAndroid Build Coastguard Worker "{:?} {} Pending: {:?} Overdue {:?}", 183*e1997b9aSAndroid Build Coastguard Worker i.tid, 184*e1997b9aSAndroid Build Coastguard Worker i.id, 185*e1997b9aSAndroid Build Coastguard Worker r.started.elapsed(), 186*e1997b9aSAndroid Build Coastguard Worker r.deadline.elapsed() 187*e1997b9aSAndroid Build Coastguard Worker ); 188*e1997b9aSAndroid Build Coastguard Worker } 189*e1997b9aSAndroid Build Coastguard Worker } 190*e1997b9aSAndroid Build Coastguard Worker } 191*e1997b9aSAndroid Build Coastguard Worker } 192*e1997b9aSAndroid Build Coastguard Worker log::warn!("### Keystore Watchdog report - END ###"); 193*e1997b9aSAndroid Build Coastguard Worker } 194*e1997b9aSAndroid Build Coastguard Worker disarm(&mut self, index: Index)195*e1997b9aSAndroid Build Coastguard Worker fn disarm(&mut self, index: Index) { 196*e1997b9aSAndroid Build Coastguard Worker let result = self.records.remove(&index); 197*e1997b9aSAndroid Build Coastguard Worker if let Some(record) = result { 198*e1997b9aSAndroid Build Coastguard Worker let now = Instant::now(); 199*e1997b9aSAndroid Build Coastguard Worker let timeout_left = record.deadline.saturating_duration_since(now); 200*e1997b9aSAndroid Build Coastguard Worker if timeout_left == Duration::new(0, 0) { 201*e1997b9aSAndroid Build Coastguard Worker match &record.context { 202*e1997b9aSAndroid Build Coastguard Worker Some(ctx) => log::info!( 203*e1997b9aSAndroid Build Coastguard Worker "Watchdog complete for: {:?} {} Pending: {:?} Overdue {:?} for {:?}", 204*e1997b9aSAndroid Build Coastguard Worker index.tid, 205*e1997b9aSAndroid Build Coastguard Worker index.id, 206*e1997b9aSAndroid Build Coastguard Worker record.started.elapsed(), 207*e1997b9aSAndroid Build Coastguard Worker record.deadline.elapsed(), 208*e1997b9aSAndroid Build Coastguard Worker ctx 209*e1997b9aSAndroid Build Coastguard Worker ), 210*e1997b9aSAndroid Build Coastguard Worker None => log::info!( 211*e1997b9aSAndroid Build Coastguard Worker "Watchdog complete for: {:?} {} Pending: {:?} Overdue {:?}", 212*e1997b9aSAndroid Build Coastguard Worker index.tid, 213*e1997b9aSAndroid Build Coastguard Worker index.id, 214*e1997b9aSAndroid Build Coastguard Worker record.started.elapsed(), 215*e1997b9aSAndroid Build Coastguard Worker record.deadline.elapsed() 216*e1997b9aSAndroid Build Coastguard Worker ), 217*e1997b9aSAndroid Build Coastguard Worker } 218*e1997b9aSAndroid Build Coastguard Worker } 219*e1997b9aSAndroid Build Coastguard Worker } 220*e1997b9aSAndroid Build Coastguard Worker } 221*e1997b9aSAndroid Build Coastguard Worker arm(&mut self, index: Index, record: Record)222*e1997b9aSAndroid Build Coastguard Worker fn arm(&mut self, index: Index, record: Record) { 223*e1997b9aSAndroid Build Coastguard Worker if self.records.insert(index.clone(), record).is_some() { 224*e1997b9aSAndroid Build Coastguard Worker log::warn!("Recursive watchdog record at \"{:?}\" replaces previous record.", index); 225*e1997b9aSAndroid Build Coastguard Worker } 226*e1997b9aSAndroid Build Coastguard Worker } 227*e1997b9aSAndroid Build Coastguard Worker } 228*e1997b9aSAndroid Build Coastguard Worker 229*e1997b9aSAndroid Build Coastguard Worker /// Watchdog spawns a thread that logs records of all overdue watch points when a deadline 230*e1997b9aSAndroid Build Coastguard Worker /// is missed and at least every second as long as overdue watch points exist. 231*e1997b9aSAndroid Build Coastguard Worker /// The thread terminates when idle for a given period of time. 232*e1997b9aSAndroid Build Coastguard Worker pub struct Watchdog { 233*e1997b9aSAndroid Build Coastguard Worker state: Arc<(Condvar, Mutex<WatchdogState>)>, 234*e1997b9aSAndroid Build Coastguard Worker } 235*e1997b9aSAndroid Build Coastguard Worker 236*e1997b9aSAndroid Build Coastguard Worker impl Watchdog { 237*e1997b9aSAndroid Build Coastguard Worker /// Construct a [`Watchdog`]. When `idle_timeout` has elapsed since the watchdog thread became 238*e1997b9aSAndroid Build Coastguard Worker /// idle, i.e., there are no more active or overdue watch points, the watchdog thread 239*e1997b9aSAndroid Build Coastguard Worker /// terminates. new(idle_timeout: Duration) -> Arc<Self>240*e1997b9aSAndroid Build Coastguard Worker pub fn new(idle_timeout: Duration) -> Arc<Self> { 241*e1997b9aSAndroid Build Coastguard Worker Arc::new(Self { 242*e1997b9aSAndroid Build Coastguard Worker state: Arc::new(( 243*e1997b9aSAndroid Build Coastguard Worker Condvar::new(), 244*e1997b9aSAndroid Build Coastguard Worker Mutex::new(WatchdogState { 245*e1997b9aSAndroid Build Coastguard Worker state: State::NotRunning, 246*e1997b9aSAndroid Build Coastguard Worker thread: None, 247*e1997b9aSAndroid Build Coastguard Worker idle_timeout, 248*e1997b9aSAndroid Build Coastguard Worker records: HashMap::new(), 249*e1997b9aSAndroid Build Coastguard Worker last_report: None, 250*e1997b9aSAndroid Build Coastguard Worker noisy_timeout: WatchdogState::MIN_REPORT_TIMEOUT, 251*e1997b9aSAndroid Build Coastguard Worker }), 252*e1997b9aSAndroid Build Coastguard Worker )), 253*e1997b9aSAndroid Build Coastguard Worker }) 254*e1997b9aSAndroid Build Coastguard Worker } 255*e1997b9aSAndroid Build Coastguard Worker watch_with_optional( wd: Arc<Self>, context: Option<Box<dyn std::fmt::Debug + Send + 'static>>, id: &'static str, timeout: Duration, ) -> Option<WatchPoint>256*e1997b9aSAndroid Build Coastguard Worker fn watch_with_optional( 257*e1997b9aSAndroid Build Coastguard Worker wd: Arc<Self>, 258*e1997b9aSAndroid Build Coastguard Worker context: Option<Box<dyn std::fmt::Debug + Send + 'static>>, 259*e1997b9aSAndroid Build Coastguard Worker id: &'static str, 260*e1997b9aSAndroid Build Coastguard Worker timeout: Duration, 261*e1997b9aSAndroid Build Coastguard Worker ) -> Option<WatchPoint> { 262*e1997b9aSAndroid Build Coastguard Worker let Some(deadline) = Instant::now().checked_add(timeout) else { 263*e1997b9aSAndroid Build Coastguard Worker log::warn!("Deadline computation failed for WatchPoint \"{}\"", id); 264*e1997b9aSAndroid Build Coastguard Worker log::warn!("WatchPoint not armed."); 265*e1997b9aSAndroid Build Coastguard Worker return None; 266*e1997b9aSAndroid Build Coastguard Worker }; 267*e1997b9aSAndroid Build Coastguard Worker wd.arm(context, id, deadline); 268*e1997b9aSAndroid Build Coastguard Worker Some(WatchPoint { id, wd, not_send: Default::default() }) 269*e1997b9aSAndroid Build Coastguard Worker } 270*e1997b9aSAndroid Build Coastguard Worker 271*e1997b9aSAndroid Build Coastguard Worker /// Create a new watch point. If the WatchPoint is not dropped before the timeout 272*e1997b9aSAndroid Build Coastguard Worker /// expires, a report is logged at least every second, which includes the id string 273*e1997b9aSAndroid Build Coastguard Worker /// and any provided context. watch_with( wd: &Arc<Self>, id: &'static str, timeout: Duration, context: impl std::fmt::Debug + Send + 'static, ) -> Option<WatchPoint>274*e1997b9aSAndroid Build Coastguard Worker pub fn watch_with( 275*e1997b9aSAndroid Build Coastguard Worker wd: &Arc<Self>, 276*e1997b9aSAndroid Build Coastguard Worker id: &'static str, 277*e1997b9aSAndroid Build Coastguard Worker timeout: Duration, 278*e1997b9aSAndroid Build Coastguard Worker context: impl std::fmt::Debug + Send + 'static, 279*e1997b9aSAndroid Build Coastguard Worker ) -> Option<WatchPoint> { 280*e1997b9aSAndroid Build Coastguard Worker Self::watch_with_optional(wd.clone(), Some(Box::new(context)), id, timeout) 281*e1997b9aSAndroid Build Coastguard Worker } 282*e1997b9aSAndroid Build Coastguard Worker 283*e1997b9aSAndroid Build Coastguard Worker /// Like `watch_with`, but without context. watch(wd: &Arc<Self>, id: &'static str, timeout: Duration) -> Option<WatchPoint>284*e1997b9aSAndroid Build Coastguard Worker pub fn watch(wd: &Arc<Self>, id: &'static str, timeout: Duration) -> Option<WatchPoint> { 285*e1997b9aSAndroid Build Coastguard Worker Self::watch_with_optional(wd.clone(), None, id, timeout) 286*e1997b9aSAndroid Build Coastguard Worker } 287*e1997b9aSAndroid Build Coastguard Worker arm( &self, context: Option<Box<dyn std::fmt::Debug + Send + 'static>>, id: &'static str, deadline: Instant, )288*e1997b9aSAndroid Build Coastguard Worker fn arm( 289*e1997b9aSAndroid Build Coastguard Worker &self, 290*e1997b9aSAndroid Build Coastguard Worker context: Option<Box<dyn std::fmt::Debug + Send + 'static>>, 291*e1997b9aSAndroid Build Coastguard Worker id: &'static str, 292*e1997b9aSAndroid Build Coastguard Worker deadline: Instant, 293*e1997b9aSAndroid Build Coastguard Worker ) { 294*e1997b9aSAndroid Build Coastguard Worker let tid = thread::current().id(); 295*e1997b9aSAndroid Build Coastguard Worker let index = Index { tid, id }; 296*e1997b9aSAndroid Build Coastguard Worker let record = Record { started: Instant::now(), deadline, context }; 297*e1997b9aSAndroid Build Coastguard Worker 298*e1997b9aSAndroid Build Coastguard Worker let (ref condvar, ref state) = *self.state; 299*e1997b9aSAndroid Build Coastguard Worker 300*e1997b9aSAndroid Build Coastguard Worker let mut state = state.lock().unwrap(); 301*e1997b9aSAndroid Build Coastguard Worker state.arm(index, record); 302*e1997b9aSAndroid Build Coastguard Worker 303*e1997b9aSAndroid Build Coastguard Worker if state.state != State::Running { 304*e1997b9aSAndroid Build Coastguard Worker self.spawn_thread(&mut state); 305*e1997b9aSAndroid Build Coastguard Worker } 306*e1997b9aSAndroid Build Coastguard Worker drop(state); 307*e1997b9aSAndroid Build Coastguard Worker condvar.notify_all(); 308*e1997b9aSAndroid Build Coastguard Worker } 309*e1997b9aSAndroid Build Coastguard Worker disarm(&self, id: &'static str)310*e1997b9aSAndroid Build Coastguard Worker fn disarm(&self, id: &'static str) { 311*e1997b9aSAndroid Build Coastguard Worker let tid = thread::current().id(); 312*e1997b9aSAndroid Build Coastguard Worker let index = Index { tid, id }; 313*e1997b9aSAndroid Build Coastguard Worker let (_, ref state) = *self.state; 314*e1997b9aSAndroid Build Coastguard Worker 315*e1997b9aSAndroid Build Coastguard Worker let mut state = state.lock().unwrap(); 316*e1997b9aSAndroid Build Coastguard Worker state.disarm(index); 317*e1997b9aSAndroid Build Coastguard Worker // There is no need to notify condvar. There is no action required for the 318*e1997b9aSAndroid Build Coastguard Worker // watchdog thread before the next deadline. 319*e1997b9aSAndroid Build Coastguard Worker } 320*e1997b9aSAndroid Build Coastguard Worker spawn_thread(&self, state: &mut MutexGuard<WatchdogState>)321*e1997b9aSAndroid Build Coastguard Worker fn spawn_thread(&self, state: &mut MutexGuard<WatchdogState>) { 322*e1997b9aSAndroid Build Coastguard Worker if let Some(t) = state.thread.take() { 323*e1997b9aSAndroid Build Coastguard Worker t.join().expect("Watchdog thread panicked."); 324*e1997b9aSAndroid Build Coastguard Worker } 325*e1997b9aSAndroid Build Coastguard Worker 326*e1997b9aSAndroid Build Coastguard Worker let cloned_state = self.state.clone(); 327*e1997b9aSAndroid Build Coastguard Worker 328*e1997b9aSAndroid Build Coastguard Worker state.thread = Some(thread::spawn(move || { 329*e1997b9aSAndroid Build Coastguard Worker let (ref condvar, ref state) = *cloned_state; 330*e1997b9aSAndroid Build Coastguard Worker 331*e1997b9aSAndroid Build Coastguard Worker let mut state = state.lock().unwrap(); 332*e1997b9aSAndroid Build Coastguard Worker 333*e1997b9aSAndroid Build Coastguard Worker loop { 334*e1997b9aSAndroid Build Coastguard Worker let (has_overdue, next_timeout) = state.overdue_and_next_timeout(); 335*e1997b9aSAndroid Build Coastguard Worker state.log_report(has_overdue); 336*e1997b9aSAndroid Build Coastguard Worker 337*e1997b9aSAndroid Build Coastguard Worker let (next_timeout, idle) = match (has_overdue, next_timeout) { 338*e1997b9aSAndroid Build Coastguard Worker (true, Some(next_timeout)) => (min(next_timeout, state.noisy_timeout), false), 339*e1997b9aSAndroid Build Coastguard Worker (true, None) => (state.noisy_timeout, false), 340*e1997b9aSAndroid Build Coastguard Worker (false, Some(next_timeout)) => (next_timeout, false), 341*e1997b9aSAndroid Build Coastguard Worker (false, None) => (state.idle_timeout, true), 342*e1997b9aSAndroid Build Coastguard Worker }; 343*e1997b9aSAndroid Build Coastguard Worker 344*e1997b9aSAndroid Build Coastguard Worker // Wait until the closest timeout pops, but use a condition variable so that if a 345*e1997b9aSAndroid Build Coastguard Worker // new watchpoint is started in the meanwhile it will interrupt the wait so we can 346*e1997b9aSAndroid Build Coastguard Worker // recalculate. 347*e1997b9aSAndroid Build Coastguard Worker let (s, timeout) = condvar.wait_timeout(state, next_timeout).unwrap(); 348*e1997b9aSAndroid Build Coastguard Worker state = s; 349*e1997b9aSAndroid Build Coastguard Worker 350*e1997b9aSAndroid Build Coastguard Worker if idle && timeout.timed_out() && state.records.is_empty() { 351*e1997b9aSAndroid Build Coastguard Worker state.reset_noisy_timeout(); 352*e1997b9aSAndroid Build Coastguard Worker state.state = State::NotRunning; 353*e1997b9aSAndroid Build Coastguard Worker break; 354*e1997b9aSAndroid Build Coastguard Worker } 355*e1997b9aSAndroid Build Coastguard Worker } 356*e1997b9aSAndroid Build Coastguard Worker log::info!("Watchdog thread idle -> terminating. Have a great day."); 357*e1997b9aSAndroid Build Coastguard Worker })); 358*e1997b9aSAndroid Build Coastguard Worker state.state = State::Running; 359*e1997b9aSAndroid Build Coastguard Worker } 360*e1997b9aSAndroid Build Coastguard Worker } 361