// Copyright 2023 The ChromiumOS Authors1// Use of this source code is governed by a BSD-style license that can be2// found in the LICENSE file.34//! Worker thread abstraction56use std::panic;7use std::thread;8use std::thread::JoinHandle;9use std::thread::Thread;1011use crate::Error;12use crate::Event;1314/// Wrapper object for creating a worker thread that can be stopped by signaling an event.15pub struct WorkerThread<T: Send + 'static> {16worker: Option<(Event, JoinHandle<T>)>,17}1819impl<T: Send + 'static> WorkerThread<T> {20/// Starts a worker thread named `thread_name` running the `thread_func` function.21///22/// The `thread_func` implementation must monitor the provided `Event` and return from the23/// thread when it is signaled.24///25/// Call [`stop()`](Self::stop) to stop the thread.26pub fn start<F>(thread_name: impl Into<String>, thread_func: F) -> Self27where28F: FnOnce(Event) -> T + Send + 'static,29{30let stop_event = Event::new().expect("Event::new() failed");31let thread_stop_event = stop_event.try_clone().expect("Event::try_clone() failed");3233let thread_handle = thread::Builder::new()34.name(thread_name.into())35.spawn(move || thread_func(thread_stop_event))36.expect("thread spawn failed");3738WorkerThread {39worker: Some((stop_event, thread_handle)),40}41}4243/// Stops the worker thread.44///45/// Returns the value returned by the function running in the thread.46pub fn stop(mut self) -> T {47// The only time the internal `Option` should be `None` is in a `drop` after `stop`, so this48// `expect()` should never fail.49self.stop_internal().expect("invalid worker state")50}5152// `stop_internal` accepts a reference so it can be called from `drop`.53#[doc(hidden)]54fn stop_internal(&mut self) -> Option<T> {55self.worker.take().map(|(stop_event, thread_handle)| {56// There is nothing the caller can do to handle `stop_event.signal()` failure, and we57// don't want to leave the thread running, so panic in that case.58stop_event59.signal()60.expect("WorkerThread stop event signal failed");6162match thread_handle.join() {63Ok(v) => v,64Err(e) => panic::resume_unwind(e),65}66})67}6869/// Signal thread's stop event. Unlike stop, the function doesn't wait70/// on joining the thread.71/// The function can be called multiple times.72/// Calling `stop` or `drop` will internally signal the stop event again73/// and join the thread.74pub fn signal(&mut self) -> Result<(), Error> {75if let Some((event, _)) = &mut self.worker {76event.signal()77} else {78Ok(())79}80}8182/// Returns a handle to the running thread.83pub fn thread(&self) -> &Thread {84// The only time the internal `Option` should be `None` is in a `drop` after `stop`, so this85// `unwrap()` should never fail.86self.worker.as_ref().unwrap().1.thread()87}88}8990impl<T: Send + 'static> Drop for WorkerThread<T> {91/// Stops the thread if the `WorkerThread` is dropped without calling [`stop()`](Self::stop).92fn drop(&mut self) {93let _ = self.stop_internal();94}95}969798