use servo_config::pref;
#[cfg(target_env = "ohos")]
mod platform {
use std::fs;
use std::sync::LazyLock;
use super::{BoostAffinity, ThreadPriority};
const QOS_USER_INITIATED: i32 = 3;
const QOS_USER_INTERACTIVE: i32 = 5;
#[link(name = "qos")]
#[expect(unsafe_code)]
unsafe extern "C" {
safe fn OH_QoS_SetThreadQoS(level: i32) -> i32;
}
type CoreId = u16;
static NON_LITTLE_CPU_CORES: LazyLock<Option<Box<[CoreId]>>> = LazyLock::new(|| {
non_little_cpus()
.inspect_err(|error| log::error!("Failed to determine non-little cpu cores: {error}"))
.ok()?
.map(|cores| cores.into_boxed_slice())
});
fn parse_cpu_list() -> Result<Vec<CoreId>, String> {
let cpu_possible_file = "/sys/devices/system/cpu/possible";
let list = fs::read_to_string(cpu_possible_file)
.map_err(|error| format!("failed to read {cpu_possible_file}: {error:?}"))?;
let mut cpus = Vec::new();
for part in list.trim().split(',') {
if let Some((a, b)) = part.split_once('-') {
if let (Ok(a), Ok(b)) = (a.trim().parse::<CoreId>(), b.trim().parse::<CoreId>()) {
cpus.extend(a..=b);
}
} else if let Ok(core_id) = part.trim().parse::<CoreId>() {
cpus.push(core_id);
} else {
log::warn!("Unexpected CPU line: {part:?} in {cpu_possible_file}.");
}
}
Ok(cpus)
}
fn capacity_of(cpu: CoreId) -> Result<u64, String> {
let cpu_capacity_file = format!("/sys/devices/system/cpu/cpu{cpu}/cpu_capacity");
fs::read_to_string(cpu_capacity_file)
.map_err(|error| error.to_string())?
.trim()
.parse::<u64>()
.map_err(|error| error.to_string())
}
fn non_little_cpus() -> Result<Option<Vec<CoreId>>, String> {
let cpus = parse_cpu_list()?;
let cpu_and_caps: Vec<(CoreId, u64)> = cpus
.iter()
.map(|&cpu| capacity_of(cpu).map(|cap| (cpu, cap)))
.collect::<Result<_, _>>()?;
let mut classes: Vec<u64> = cpu_and_caps.iter().map(|&(_, cap)| cap).collect();
classes.sort_unstable();
classes.dedup();
if classes.len() < 2 {
return Ok(None);
}
let little = classes[0];
let chosen: Vec<CoreId> = cpu_and_caps
.iter()
.filter(|&&(_, cap)| cap > little)
.map(|&(cpu, _)| cpu)
.collect();
if chosen.len() < 2 {
return Ok(None);
}
Ok(Some(chosen))
}
#[expect(unsafe_code)]
fn pin_thread_to_medium_or_large_cpus() -> Result<(), String> {
let Some(cpus) = NON_LITTLE_CPU_CORES.as_ref() else {
return Ok(());
};
let ret = unsafe {
let mut set: libc::cpu_set_t = std::mem::zeroed();
for &cpu in cpus {
libc::CPU_SET(cpu.into(), &mut set);
}
libc::sched_setaffinity(0, size_of::<libc::cpu_set_t>(), &set)
};
if ret != 0 {
return Err(format!(
"Failed to set thread affinity: {:?}",
std::io::Error::last_os_error()
));
}
Ok(())
}
pub fn boost_thread(priority: ThreadPriority, boost_affinity: BoostAffinity) {
let qos_rc = match priority {
ThreadPriority::Elevated => OH_QoS_SetThreadQoS(QOS_USER_INITIATED),
ThreadPriority::Critical => OH_QoS_SetThreadQoS(QOS_USER_INTERACTIVE),
ThreadPriority::Default => 0,
};
if qos_rc != 0 {
log::warn!("Failed to boost thread priority. `OH_QoS_SetThreadQoS` returned {qos_rc}");
}
if matches!(boost_affinity, BoostAffinity::Boost) &&
let Err(error) = pin_thread_to_medium_or_large_cpus()
{
log::warn!(
"Failed to pin {} to medium or large cpus: {error:?}",
std::thread::current().name().unwrap_or("<unnamed>"),
);
}
}
}
#[cfg(not(target_env = "ohos"))]
mod platform {
pub fn boost_thread(_: super::ThreadPriority, _: super::BoostAffinity) {}
}
pub enum ThreadPriority {
Default,
Elevated,
Critical,
}
pub enum BoostAffinity {
No,
Boost,
}
#[allow(unsafe_code)]
pub fn boost_thread(priority: ThreadPriority, boost_affinity: BoostAffinity) {
if pref!(perf_thread_boost_enabled) {
platform::boost_thread(priority, boost_affinity)
}
}