Make preemption knobs configurable; fix unused-variable warnings
Add `Config::alloc_interval()` and `Config::timeslice_cycles()` so callers can tune preemption sensitivity at runtime. The values flow through `RuntimeInner` and are written into per-scheduler-thread locals via a new `configure_preempt()` call at thread startup, keeping the hot path free of cross-thread coherency traffic. Fix unused-variable warnings in channel.rs by inlining `current_pid()` directly into `te!` macro arguments — since the no-op macro arm never evaluates its argument, no binding is needed at the call site. Clean up a handful of dead imports exposed by the refactor.
This commit is contained in:
+25
-6
@@ -28,23 +28,42 @@
|
||||
use std::alloc::{GlobalAlloc, Layout, System};
|
||||
use std::cell::Cell;
|
||||
|
||||
const ALLOC_INTERVAL: u32 = 128;
|
||||
const TIMESLICE_CYCLES: u64 = 300_000; // ≈ 100µs on a 3 GHz CPU
|
||||
pub const DEFAULT_ALLOC_INTERVAL: u32 = 128;
|
||||
pub const DEFAULT_TIMESLICE_CYCLES: u64 = 300_000; // ≈ 100µs on a 3 GHz CPU
|
||||
|
||||
thread_local! {
|
||||
/// While `false`, the allocator hook is a no-op.
|
||||
pub static PREEMPTION_ENABLED: Cell<bool> = const { Cell::new(false) };
|
||||
|
||||
/// Countdown to next RDTSC check. Reset to `ALLOC_INTERVAL` on resume.
|
||||
static ALLOC_COUNT: Cell<u32> = const { Cell::new(ALLOC_INTERVAL) };
|
||||
static ALLOC_COUNT: Cell<u32> = const { Cell::new(DEFAULT_ALLOC_INTERVAL) };
|
||||
|
||||
/// RDTSC value written by the scheduler on every actor resume.
|
||||
static TIMESLICE_START: Cell<u64> = const { Cell::new(0) };
|
||||
|
||||
/// Per-thread copy of the configured alloc interval, written once at
|
||||
/// scheduler-thread startup. Kept in a thread-local so the hot path
|
||||
/// (`maybe_preempt`) pays only a TLS load, with no cache-coherency traffic.
|
||||
static CONFIGURED_ALLOC_INTERVAL: Cell<u32> = const { Cell::new(DEFAULT_ALLOC_INTERVAL) };
|
||||
|
||||
/// Per-thread copy of the configured timeslice, written once at
|
||||
/// scheduler-thread startup.
|
||||
static CONFIGURED_TIMESLICE_CYCLES: Cell<u64> = const { Cell::new(DEFAULT_TIMESLICE_CYCLES) };
|
||||
}
|
||||
|
||||
/// Called once per scheduler thread at startup (before any actor runs).
|
||||
/// Writes the runtime-configured preemption knobs into thread-locals so the
|
||||
/// hot path reads them without any cross-thread coherency cost.
|
||||
pub fn configure_preempt(alloc_interval: u32, timeslice_cycles: u64) {
|
||||
CONFIGURED_ALLOC_INTERVAL.with(|c| c.set(alloc_interval));
|
||||
CONFIGURED_TIMESLICE_CYCLES.with(|c| c.set(timeslice_cycles));
|
||||
// Also prime the countdown so the first resume uses the right interval.
|
||||
ALLOC_COUNT.with(|c| c.set(alloc_interval));
|
||||
}
|
||||
|
||||
/// Arm the timeslice. Called by the scheduler on every resume.
|
||||
pub fn reset_timeslice() {
|
||||
ALLOC_COUNT.with(|c| c.set(ALLOC_INTERVAL));
|
||||
ALLOC_COUNT.with(|c| c.set(CONFIGURED_ALLOC_INTERVAL.with(|i| i.get())));
|
||||
TIMESLICE_START.with(|c| c.set(rdtsc()));
|
||||
}
|
||||
|
||||
@@ -102,10 +121,10 @@ pub fn maybe_preempt() {
|
||||
ALLOC_COUNT.with(|c| {
|
||||
let n = c.get();
|
||||
if n == 0 {
|
||||
c.set(ALLOC_INTERVAL);
|
||||
c.set(CONFIGURED_ALLOC_INTERVAL.with(|i| i.get()));
|
||||
if PREEMPTION_ENABLED.with(|e| e.get()) {
|
||||
let start = TIMESLICE_START.with(|s| s.get());
|
||||
if rdtsc().saturating_sub(start) > TIMESLICE_CYCLES {
|
||||
if rdtsc().saturating_sub(start) > CONFIGURED_TIMESLICE_CYCLES.with(|t| t.get()) {
|
||||
// SAFETY: reachable only inside an actor (the scheduler
|
||||
// sets PREEMPTION_ENABLED on resume and clears it on
|
||||
// return). The scheduler stack is therefore valid.
|
||||
|
||||
Reference in New Issue
Block a user