mirror of
https://github.com/topjohnwu/Magisk.git
synced 2026-01-16 23:02:23 -08:00
100 lines
3.1 KiB
Rust
100 lines
3.1 KiB
Rust
use base::{ResultExt, new_daemon_thread};
|
|
use nix::sys::signal::SigSet;
|
|
use nix::unistd::{getpid, gettid};
|
|
use std::sync::{Condvar, LazyLock, Mutex, WaitTimeoutResult};
|
|
use std::time::Duration;
|
|
|
|
static THREAD_POOL: LazyLock<ThreadPool> = LazyLock::new(ThreadPool::default);
|
|
|
|
const THREAD_IDLE_MAX_SEC: u64 = 60;
|
|
const CORE_POOL_SIZE: i32 = 3;
|
|
|
|
#[derive(Default)]
|
|
pub struct ThreadPool {
|
|
task_is_some: Condvar,
|
|
task_is_none: Condvar,
|
|
info: Mutex<PoolInfo>,
|
|
}
|
|
|
|
#[derive(Default)]
|
|
struct PoolInfo {
|
|
idle_threads: i32,
|
|
total_threads: i32,
|
|
task: Option<Box<dyn FnOnce() + Send>>,
|
|
}
|
|
|
|
impl ThreadPool {
|
|
fn pool_loop(&self, is_core_pool: bool) {
|
|
let mask = SigSet::all();
|
|
|
|
loop {
|
|
// Always restore the sigmask to block all signals
|
|
mask.thread_set_mask().log_ok();
|
|
|
|
let task: Option<Box<dyn FnOnce() + Send>>;
|
|
{
|
|
let mut info = self.info.lock().unwrap();
|
|
info.idle_threads += 1;
|
|
if info.task.is_none() {
|
|
if is_core_pool {
|
|
// Core pool never closes, wait forever.
|
|
info = self.task_is_some.wait(info).unwrap();
|
|
} else {
|
|
let dur = Duration::from_secs(THREAD_IDLE_MAX_SEC);
|
|
let result: WaitTimeoutResult;
|
|
(info, result) = self.task_is_some.wait_timeout(info, dur).unwrap();
|
|
if result.timed_out() {
|
|
// Terminate thread after timeout
|
|
info.idle_threads -= 1;
|
|
info.total_threads -= 1;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
task = info.task.take();
|
|
self.task_is_none.notify_one();
|
|
info.idle_threads -= 1;
|
|
}
|
|
if let Some(task) = task {
|
|
task();
|
|
}
|
|
if getpid() == gettid() {
|
|
// This meant the current thread forked and became the main thread, exit
|
|
std::process::exit(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn exec_task_impl(&self, f: impl FnOnce() + Send + 'static) {
|
|
extern "C" fn pool_loop_raw(arg: usize) -> usize {
|
|
let is_core_pool = arg != 0;
|
|
THREAD_POOL.pool_loop(is_core_pool);
|
|
0
|
|
}
|
|
|
|
let mut info = self.info.lock().unwrap();
|
|
while info.task.is_some() {
|
|
// Wait until task is none
|
|
info = self.task_is_none.wait(info).unwrap();
|
|
}
|
|
info.task = Some(Box::new(f));
|
|
if info.idle_threads == 0 {
|
|
info.total_threads += 1;
|
|
let is_core_thread = if info.total_threads <= CORE_POOL_SIZE {
|
|
1_usize
|
|
} else {
|
|
0_usize
|
|
};
|
|
unsafe {
|
|
new_daemon_thread(pool_loop_raw, is_core_thread);
|
|
}
|
|
} else {
|
|
self.task_is_some.notify_one();
|
|
}
|
|
}
|
|
|
|
pub fn exec_task(f: impl FnOnce() + Send + 'static) {
|
|
THREAD_POOL.exec_task_impl(f);
|
|
}
|
|
}
|