The JIT that runs makepad apps from source: lexer, front end, type checker, lazy borrow checker (Gandalf; `--check-first` opts in), codegen, session and warm/cold caches keyed by compiler stamp, provided crates bound late, startup images, live patching. User-facing name is `makepad run` (binary `makepad`, MAKEPAD_RUN_* env, ~/.makepad-run); libs/relax stays the internal library name. `makepad check` reports rustc-style diagnostics with rustc error codes and checks a whole workspace (`--workspace`). Runs on macOS, Linux, Windows and Android. makepad-remote: one tool schema for apps and the WM (app/1): grabs, captures, traces, frame profiles and tweaks. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
85 lines
3 KiB
Rust
85 lines
3 KiB
Rust
// A big value made by a call (returned in memory), passed by value into another constructor and
|
|
// wrapped in Some(..) into an existing place -- Score's audio callback:
|
|
// `engine = Some(PlaybackEngine::new(SamplerBackend::new(..)))` with 108 KB / 121 KB values and Arc
|
|
// fields moved in, on CoreAudio's 512 KB IO thread. Relax kept each step in its own slot plus a dead
|
|
// slot's size in a colour (351 KB frame): the call writes its result buffer only, so the result is
|
|
// built where it is copied to, and a slot nothing addresses takes no stack.
|
|
use std::sync::Arc;
|
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
static DROPS: AtomicUsize = AtomicUsize::new(0);
|
|
struct Tracked(u32);
|
|
impl Drop for Tracked {
|
|
fn drop(&mut self) {
|
|
DROPS.fetch_add(1, Ordering::Relaxed);
|
|
}
|
|
}
|
|
struct Backend {
|
|
sound: Arc<Tracked>,
|
|
peak: Arc<Tracked>,
|
|
left: [f32; 8192],
|
|
right: [f32; 8192],
|
|
events: [(u8, u32); 64],
|
|
n: u32,
|
|
}
|
|
impl Backend {
|
|
#[inline(never)]
|
|
fn new(sound: Arc<Tracked>, peak: Arc<Tracked>, n: u32) -> Self {
|
|
let mut events = [(5u8, 0u32); 64];
|
|
events[3].1 = check(n);
|
|
Self { sound, peak, left: [0.25; 8192], right: [0.5; 8192], events, n }
|
|
}
|
|
}
|
|
struct Engine {
|
|
backend: Backend,
|
|
pending: [u64; 2048],
|
|
len: usize,
|
|
}
|
|
impl Engine {
|
|
#[inline(never)]
|
|
fn new(backend: Backend) -> Self {
|
|
let mut pending = [0u64; 2048];
|
|
pending[7] = backend.n as u64 * 3;
|
|
Self { backend, pending, len: 1 }
|
|
}
|
|
}
|
|
#[inline(never)]
|
|
fn check(n: u32) -> u32 {
|
|
if n > 1000 {
|
|
panic!("too big");
|
|
}
|
|
n + 1
|
|
}
|
|
fn hash(e: &Engine) -> u64 {
|
|
let mut h = 0u64;
|
|
for (k, x) in e.backend.left.iter().chain(e.backend.right.iter()).enumerate() {
|
|
h = h.wrapping_mul(31).wrapping_add((x.to_bits() as u64) ^ k as u64);
|
|
}
|
|
for (a, b) in e.backend.events.iter() {
|
|
h = h.wrapping_mul(31).wrapping_add(*a as u64 + *b as u64);
|
|
}
|
|
for p in e.pending.iter() {
|
|
h = h.wrapping_mul(31).wrapping_add(*p);
|
|
}
|
|
h ^ (e.len as u64) ^ (e.backend.sound.0 as u64) << 8 ^ (e.backend.peak.0 as u64) << 16
|
|
}
|
|
#[inline(never)]
|
|
fn fill(slot: &mut Option<Engine>, n: u32) {
|
|
let sound = Arc::new(Tracked(3));
|
|
let peak = Arc::new(Tracked(4));
|
|
if slot.is_none() {
|
|
*slot = Some(Engine::new(Backend::new(sound.clone(), peak.clone(), n)));
|
|
}
|
|
}
|
|
#[test]
|
|
fn big_through_calls() {
|
|
let mut slot: Option<Engine> = None;
|
|
fill(&mut slot, 7);
|
|
let e = slot.as_ref().unwrap();
|
|
println!("hash {} n {} arcs {} {}", hash(e), e.backend.n, Arc::strong_count(&e.backend.sound), Arc::strong_count(&e.backend.peak));
|
|
drop(slot);
|
|
println!("drops {}", DROPS.load(Ordering::Relaxed));
|
|
// the constructor panics half way: the moved-in Arcs are dropped once, the slot stays None
|
|
let mut slot2: Option<Engine> = None;
|
|
let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| fill(&mut slot2, 5000)));
|
|
println!("unwound {} none {} drops {}", r.is_err(), slot2.is_none(), DROPS.load(Ordering::Relaxed));
|
|
}
|