use std::sync::{
Arc, Mutex,
atomic::{AtomicBool, Ordering},
};
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
mod linux;
#[cfg(target_os = "macos")]
mod macos;
#[cfg(windows)]
mod windows;
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
use linux::PlatformPump;
#[cfg(target_os = "macos")]
use macos::PlatformPump;
#[cfg(windows)]
use windows::PlatformPump;
const K_TIMER_DELAY_PLACEHOLDER: i64 = i32::MAX as i64;
const K_MAX_TIMER_DELAY: i64 = 1000 / 30;
#[derive(Clone)]
pub(crate) struct CefExternalPump {
state: Arc<PumpState>,
}
impl CefExternalPump {
pub(crate) fn new() -> Self {
let state = Arc::new_cyclic(|weak| PumpState {
is_active: AtomicBool::new(false),
reentrancy_detected: AtomicBool::new(false),
platform: Mutex::new(PlatformPump::new(weak.clone())),
});
Self { state }
}
pub(crate) fn on_schedule_message_pump_work(&self, delay_ms: i64) {
self.state.on_schedule_message_pump_work(delay_ms);
}
pub(crate) fn do_work(&self) {
self.state.do_work();
}
}
struct PumpState {
is_active: AtomicBool,
reentrancy_detected: AtomicBool,
platform: Mutex<PlatformPump>,
}
impl PumpState {
fn on_schedule_message_pump_work(&self, delay_ms: i64) {
if let Ok(mut platform) = self.platform.lock() {
platform.on_schedule_message_pump_work(delay_ms);
}
}
fn on_schedule_work(&self, mut delay_ms: i64) {
{
let Ok(mut platform) = self.platform.lock() else {
return;
};
if delay_ms == K_TIMER_DELAY_PLACEHOLDER && platform.is_timer_pending() {
return;
}
platform.kill_timer();
}
if delay_ms <= 0 {
self.do_work();
} else if let Ok(mut platform) = self.platform.lock() {
if delay_ms > K_MAX_TIMER_DELAY {
delay_ms = K_MAX_TIMER_DELAY;
}
platform.set_timer(delay_ms);
}
}
fn on_timer_timeout(&self) {
if let Ok(mut platform) = self.platform.lock() {
platform.kill_timer();
}
self.do_work();
}
fn do_work(&self) {
let was_reentrant = self.perform_message_loop_work();
if was_reentrant {
self.on_schedule_message_pump_work(0);
} else if !self.is_timer_pending() {
self.on_schedule_message_pump_work(K_TIMER_DELAY_PLACEHOLDER);
}
}
fn is_timer_pending(&self) -> bool {
self
.platform
.lock()
.map(|platform| platform.is_timer_pending())
.unwrap_or(true)
}
fn perform_message_loop_work(&self) -> bool {
if self.is_active.load(Ordering::SeqCst) {
self.reentrancy_detected.store(true, Ordering::SeqCst);
return false;
}
self.reentrancy_detected.store(false, Ordering::SeqCst);
self.is_active.store(true, Ordering::SeqCst);
cef::do_message_loop_work();
self.is_active.store(false, Ordering::SeqCst);
self.reentrancy_detected.load(Ordering::SeqCst)
}
}