1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2022 The ChromiumOS Authors
2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file.
4*bb4ee6a4SAndroid Build Coastguard Worker
5*bb4ee6a4SAndroid Build Coastguard Worker use std::any::Any;
6*bb4ee6a4SAndroid Build Coastguard Worker use std::panic;
7*bb4ee6a4SAndroid Build Coastguard Worker use std::sync::mpsc::channel;
8*bb4ee6a4SAndroid Build Coastguard Worker use std::sync::mpsc::Receiver;
9*bb4ee6a4SAndroid Build Coastguard Worker use std::thread;
10*bb4ee6a4SAndroid Build Coastguard Worker use std::thread::JoinHandle;
11*bb4ee6a4SAndroid Build Coastguard Worker use std::time::Duration;
12*bb4ee6a4SAndroid Build Coastguard Worker
13*bb4ee6a4SAndroid Build Coastguard Worker /// Spawns a thread that can be joined with a timeout.
spawn_with_timeout<F, T>(f: F) -> JoinHandleWithTimeout<T> where F: FnOnce() -> T, F: Send + 'static, T: Send + 'static,14*bb4ee6a4SAndroid Build Coastguard Worker pub fn spawn_with_timeout<F, T>(f: F) -> JoinHandleWithTimeout<T>
15*bb4ee6a4SAndroid Build Coastguard Worker where
16*bb4ee6a4SAndroid Build Coastguard Worker F: FnOnce() -> T,
17*bb4ee6a4SAndroid Build Coastguard Worker F: Send + 'static,
18*bb4ee6a4SAndroid Build Coastguard Worker T: Send + 'static,
19*bb4ee6a4SAndroid Build Coastguard Worker {
20*bb4ee6a4SAndroid Build Coastguard Worker // Use a channel to signal completion to the join handle
21*bb4ee6a4SAndroid Build Coastguard Worker let (tx, rx) = channel();
22*bb4ee6a4SAndroid Build Coastguard Worker let handle = thread::spawn(move || {
23*bb4ee6a4SAndroid Build Coastguard Worker let val = panic::catch_unwind(panic::AssertUnwindSafe(f));
24*bb4ee6a4SAndroid Build Coastguard Worker tx.send(()).unwrap();
25*bb4ee6a4SAndroid Build Coastguard Worker val
26*bb4ee6a4SAndroid Build Coastguard Worker });
27*bb4ee6a4SAndroid Build Coastguard Worker JoinHandleWithTimeout { handle, rx }
28*bb4ee6a4SAndroid Build Coastguard Worker }
29*bb4ee6a4SAndroid Build Coastguard Worker
30*bb4ee6a4SAndroid Build Coastguard Worker pub struct JoinHandleWithTimeout<T> {
31*bb4ee6a4SAndroid Build Coastguard Worker handle: JoinHandle<thread::Result<T>>,
32*bb4ee6a4SAndroid Build Coastguard Worker rx: Receiver<()>,
33*bb4ee6a4SAndroid Build Coastguard Worker }
34*bb4ee6a4SAndroid Build Coastguard Worker
35*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Debug)]
36*bb4ee6a4SAndroid Build Coastguard Worker pub enum JoinError {
37*bb4ee6a4SAndroid Build Coastguard Worker Panic(Box<dyn Any>),
38*bb4ee6a4SAndroid Build Coastguard Worker Timeout,
39*bb4ee6a4SAndroid Build Coastguard Worker }
40*bb4ee6a4SAndroid Build Coastguard Worker
41*bb4ee6a4SAndroid Build Coastguard Worker impl<T> JoinHandleWithTimeout<T> {
42*bb4ee6a4SAndroid Build Coastguard Worker /// Tries to join the thread. Returns an error if the join takes more than `timeout_ms`.
try_join(self, timeout: Duration) -> Result<T, JoinError>43*bb4ee6a4SAndroid Build Coastguard Worker pub fn try_join(self, timeout: Duration) -> Result<T, JoinError> {
44*bb4ee6a4SAndroid Build Coastguard Worker if self.rx.recv_timeout(timeout).is_ok() {
45*bb4ee6a4SAndroid Build Coastguard Worker self.handle.join().unwrap().map_err(|e| JoinError::Panic(e))
46*bb4ee6a4SAndroid Build Coastguard Worker } else {
47*bb4ee6a4SAndroid Build Coastguard Worker Err(JoinError::Timeout)
48*bb4ee6a4SAndroid Build Coastguard Worker }
49*bb4ee6a4SAndroid Build Coastguard Worker }
50*bb4ee6a4SAndroid Build Coastguard Worker }
51