Complete first release candidate audit (#107)
Some checks failed
API and SemVer surface / api-surface (push) Failing after 1m34s
Native code generation / deterministic (push) Has been cancelled
Concurrency and resource soak audit / soak (push) Has been cancelled
Documentation / documentation (push) Has been cancelled
performance evidence / audit (push) Has been cancelled
First release candidate / non-fuzz-release-gate (push) Has been cancelled
Release platform and feature matrix / audit (push) Has been cancelled
Release platform and feature matrix / matrix (false, linux-stable-minimal, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (false, macos-stable-portable, x86_64-apple-darwin, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (false, windows-stable-portable, x86_64-pc-windows-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-msrv-portable, x86_64-unknown-linux-gnu, 1.96.0) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-default, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-features, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-release-surface, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Dependency and supply-chain audit / audit (push) Has been cancelled
Native release artifact audit / audit (push) Has been cancelled
Imaging and meshing gate / native (push) Failing after 53s
JPEG 2000 feature / linux (push) Successful in 2m49s
Native Rust workspace compile / compile (push) Failing after 59s
Skia feature / linux (push) Has been cancelled

This commit is contained in:
2026-08-12 14:44:28 +00:00
parent dceb394378
commit c9a1170a27
140 changed files with 82175 additions and 27179 deletions

View File

@@ -12,10 +12,12 @@ use std::hash::Hash;
use std::marker::PhantomData;
use std::pin::Pin;
use std::sync::{
Arc, Mutex, Weak,
Arc, Condvar, Mutex, Weak,
atomic::{AtomicBool, AtomicU64, Ordering},
};
use std::task::{Context, Poll, Waker};
use std::thread::JoinHandle;
use std::time::{Duration, Instant};
pub trait Collection<T> {}
@@ -71,8 +73,9 @@ impl Hash for OpaqueObject {
}
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Default)]
pub enum Object {
#[default]
Undefined,
Boolean(bool),
Integer(i32),
@@ -202,6 +205,54 @@ impl From<&str> for Object {
}
}
impl From<bool> for Object {
fn from(value: bool) -> Self {
Self::Boolean(value)
}
}
impl From<i32> for Object {
fn from(value: i32) -> Self {
Self::Integer(value)
}
}
impl From<u32> for Object {
fn from(value: u32) -> Self {
Self::UInteger(value)
}
}
impl From<i64> for Object {
fn from(value: i64) -> Self {
Self::Long(value)
}
}
impl From<u64> for Object {
fn from(value: u64) -> Self {
Self::ULong(value)
}
}
impl From<f32> for Object {
fn from(value: f32) -> Self {
Self::Real(f64::from(value))
}
}
impl From<f64> for Object {
fn from(value: f64) -> Self {
Self::Real(value)
}
}
impl From<Vec<u8>> for Object {
fn from(value: Vec<u8>) -> Self {
Self::Bytes(value)
}
}
impl From<crate::Color4> for Object {
fn from(value: crate::Color4) -> Self {
Self::Color4(value)
@@ -256,6 +307,14 @@ pub struct CultureInfo(pub String);
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct ExternalError(pub String);
impl fmt::Display for ExternalError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(&self.0)
}
}
impl std::error::Error for ExternalError {}
/// Type-erased enum name and optional [`crate::EnumInfoAttribute`] text.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct EnumValue {
@@ -695,14 +754,214 @@ pub trait Close {
///
/// Returns the mapped disposal error, or an unimplemented error until the
/// owning resource supplies its implementation.
fn close(&mut self) -> Result<(), ExternalError> {
Err(ExternalError("Close::close is not implemented".to_owned()))
fn close(&mut self) -> Result<(), ExternalError>;
}
struct ThreadState {
name: Option<String>,
alive: AtomicBool,
panicked: AtomicBool,
completed: Mutex<bool>,
completed_changed: Condvar,
handle: Mutex<Option<JoinHandle<()>>>,
}
/// An owned, cloneable handle to a native worker thread.
///
/// Unlike the old API marker, this value tracks liveness and supports bounded
/// joins without relying on platform-specific thread APIs.
#[derive(Clone)]
pub struct Thread(Arc<ThreadState>);
impl Thread {
/// Starts a named native thread.
///
/// # Errors
///
/// Returns an external error when the operating system refuses to create
/// the thread.
pub fn spawn(
name: Option<String>,
action: impl FnOnce() + Send + 'static,
) -> Result<Self, ExternalError> {
let state = Arc::new(ThreadState {
name: name.clone(),
alive: AtomicBool::new(true),
panicked: AtomicBool::new(false),
completed: Mutex::new(false),
completed_changed: Condvar::new(),
handle: Mutex::new(None),
});
let worker_state = Arc::clone(&state);
let mut builder = std::thread::Builder::new();
if let Some(name) = name {
builder = builder.name(name);
}
let handle = builder
.spawn(move || {
if std::panic::catch_unwind(std::panic::AssertUnwindSafe(action)).is_err() {
worker_state.panicked.store(true, Ordering::Release);
}
worker_state.alive.store(false, Ordering::Release);
*worker_state
.completed
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = true;
worker_state.completed_changed.notify_all();
})
.map_err(|error| ExternalError(error.to_string()))?;
*state
.handle
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = Some(handle);
Ok(Self(state))
}
#[must_use]
pub fn name(&self) -> Option<&str> {
self.0.name.as_deref()
}
#[must_use]
pub fn is_alive(&self) -> bool {
self.0.alive.load(Ordering::Acquire)
}
/// Waits at most `timeout` for completion and joins a completed worker.
///
/// # Errors
///
/// Returns an external error if the worker panicked while running.
pub fn join_timeout(&self, timeout: Duration) -> Result<bool, ExternalError> {
let completed = self
.0
.completed
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let (completed, wait) = self
.0
.completed_changed
.wait_timeout_while(completed, timeout, |completed| !*completed)
.unwrap_or_else(std::sync::PoisonError::into_inner);
if wait.timed_out() && !*completed {
return Ok(false);
}
drop(completed);
if let Some(handle) = self
.0
.handle
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.take()
{
handle
.join()
.map_err(|_| ExternalError("worker thread panicked".to_owned()))?;
}
if self.0.panicked.load(Ordering::Acquire) {
return Err(ExternalError("worker thread panicked".to_owned()));
}
Ok(true)
}
}
pub struct Thread;
impl fmt::Debug for Thread {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("Thread")
.field("name", &self.name())
.field("alive", &self.is_alive())
.finish_non_exhaustive()
}
}
pub struct Task<T>(pub PhantomData<fn() -> T>);
struct CurrentThreadWake(std::thread::Thread);
impl std::task::Wake for CurrentThreadWake {
fn wake(self: Arc<Self>) {
self.0.unpark();
}
fn wake_by_ref(self: &Arc<Self>) {
self.0.unpark();
}
}
/// A runtime-neutral owned task used by mapped .NET `Task<T>` signatures.
///
/// The task can be awaited normally or synchronously observed with a bounded
/// wait. It deliberately does not create or require a Tokio runtime.
pub struct Task<T> {
future: Pin<Box<dyn Future<Output = Result<T, crate::Error>> + Send + 'static>>,
}
impl<T: Send + 'static> Task<T> {
#[must_use]
pub fn ready(value: T) -> Self {
Self::from_future(async move { Ok(value) })
}
#[must_use]
pub fn failed(error: crate::Error) -> Self {
Self::from_future(async move { Err(error) })
}
#[must_use]
pub fn from_future(
future: impl Future<Output = Result<T, crate::Error>> + Send + 'static,
) -> Self {
Self {
future: Box::pin(future),
}
}
/// Drives the task on the current thread until it completes or times out.
///
/// `Ok(None)` denotes a timeout; task failures remain typed errors.
///
/// # Errors
///
/// Returns the typed error produced by the task.
pub fn wait_timeout(mut self, timeout: Duration) -> Result<Option<T>, crate::Error> {
let deadline = Instant::now().checked_add(timeout);
let wake = Arc::new(CurrentThreadWake(std::thread::current()));
let waker = Waker::from(wake);
let mut context = Context::from_waker(&waker);
loop {
if let Poll::Ready(result) = self.future.as_mut().poll(&mut context) {
return result.map(Some);
}
let Some(deadline) = deadline else {
return Ok(None);
};
let now = Instant::now();
if now >= deadline {
return Ok(None);
}
std::thread::park_timeout(deadline.saturating_duration_since(now));
}
}
}
impl<T> Future for Task<T> {
type Output = Result<T, crate::Error>;
fn poll(mut self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
self.future.as_mut().poll(context)
}
}
impl Close for CancellationTokenSource {
fn close(&mut self) -> Result<(), ExternalError> {
self.cancel();
self.0
.linked_registrations
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.clear();
Ok(())
}
}
struct TaskCompletionState<T> {
next_id: AtomicU64,
@@ -711,9 +970,14 @@ struct TaskCompletionState<T> {
}
/// Cloneable completion source used by mapped task-returning APIs.
#[derive(Clone)]
pub struct TaskCompletionSource<T>(Arc<TaskCompletionState<T>>);
impl<T> Clone for TaskCompletionSource<T> {
fn clone(&self) -> Self {
Self(Arc::clone(&self.0))
}
}
impl<T> TaskCompletionSource<T> {
#[must_use]
pub fn new() -> Self {
@@ -804,7 +1068,7 @@ pub struct TaskCompletionFuture<T> {
waiter_id: Option<u64>,
}
impl<T: Clone> Future for TaskCompletionFuture<T> {
impl<T> Future for TaskCompletionFuture<T> {
type Output = Result<T, crate::Error>;
fn poll(mut self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
@@ -813,7 +1077,7 @@ impl<T: Clone> Future for TaskCompletionFuture<T> {
.result
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.clone()
.take()
{
return Poll::Ready(result);
}
@@ -832,7 +1096,7 @@ impl<T: Clone> Future for TaskCompletionFuture<T> {
.result
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.clone();
.take();
if let Some(result) = ready {
self.state
.waiters
@@ -973,7 +1237,92 @@ where
pub struct IEnumerator;
pub struct WaitHandle;
struct WaitHandleState {
signalled: Mutex<bool>,
changed: Condvar,
}
/// Cloneable, cross-platform manual-reset wait handle.
#[derive(Clone)]
pub struct WaitHandle(Arc<WaitHandleState>);
impl WaitHandle {
#[must_use]
pub fn new(signalled: bool) -> Self {
Self(Arc::new(WaitHandleState {
signalled: Mutex::new(signalled),
changed: Condvar::new(),
}))
}
pub fn set(&self) {
*self
.0
.signalled
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = true;
self.0.changed.notify_all();
}
pub fn reset(&self) {
*self
.0
.signalled
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = false;
}
#[must_use]
pub fn is_signalled(&self) -> bool {
*self
.0
.signalled
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
#[must_use]
pub fn wait_timeout(&self, timeout: Option<Duration>) -> bool {
let signalled = self
.0
.signalled
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if *signalled {
return true;
}
match timeout {
Some(timeout) => {
let (signalled, _) = self
.0
.changed
.wait_timeout_while(signalled, timeout, |signalled| !*signalled)
.unwrap_or_else(std::sync::PoisonError::into_inner);
*signalled
}
None => *self
.0
.changed
.wait_while(signalled, |signalled| !*signalled)
.unwrap_or_else(std::sync::PoisonError::into_inner),
}
}
}
impl Default for WaitHandle {
fn default() -> Self {
Self::new(false)
}
}
impl fmt::Debug for WaitHandle {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("WaitHandle")
.field("signalled", &self.is_signalled())
.finish()
}
}
#[derive(Clone, Debug)]
pub struct FrozenDictionary<TKey, TValue>(pub std::collections::HashMap<TKey, TValue>);
@@ -1001,7 +1350,54 @@ pub struct TypeId(pub &'static str);
pub struct Span<T>(pub PhantomData<fn(T)>);
pub struct RandomSource;
/// Thread-safe pseudo-random source for mapped `System.Random` parameters.
#[derive(Clone, Debug)]
pub struct RandomSource(Arc<Mutex<u64>>);
impl RandomSource {
#[must_use]
pub fn new() -> Self {
let mut bytes = [0_u8; 8];
if getrandom::fill(&mut bytes).is_err() {
bytes = 0x9e37_79b9_7f4a_7c15_u64.to_le_bytes();
}
Self(Arc::new(Mutex::new(u64::from_le_bytes(bytes))))
}
#[must_use]
pub fn with_seed(seed: i32) -> Self {
let seed = u64::from(seed.unsigned_abs()).wrapping_add(0x9e37_79b9_7f4a_7c15);
Self(Arc::new(Mutex::new(seed)))
}
/// Returns a pseudo-random value below `exclusive_max`.
///
/// # Errors
///
/// Returns an argument error when the exclusive maximum is not positive.
pub fn next(&self, exclusive_max: i32) -> Result<i32, crate::Error> {
let maximum = u64::try_from(exclusive_max).map_err(|_| crate::Error::Argument)?;
if maximum == 0 {
return Err(crate::Error::Argument);
}
let mut state = self
.0
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let mut value = *state;
value ^= value << 13;
value ^= value >> 7;
value ^= value << 17;
*state = value;
i32::try_from(value % maximum).map_err(|_| crate::Error::IndexOutOfRange)
}
}
impl Default for RandomSource {
fn default() -> Self {
Self::new()
}
}
struct AsyncEnumerableState<T> {
values: std::collections::VecDeque<T>,