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
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:
179
crates/libremetaverse/src/callback_runtime.rs
Normal file
179
crates/libremetaverse/src/callback_runtime.rs
Normal file
@@ -0,0 +1,179 @@
|
||||
//! Native callback and legacy asynchronous-delegate adapters.
|
||||
|
||||
use std::any::Any;
|
||||
use std::fmt;
|
||||
use std::sync::{Arc, Condvar, Mutex};
|
||||
|
||||
use libremetaverse_types::compat::{DictionaryEntry, Object};
|
||||
|
||||
use crate::{DictionaryEventAction, Error};
|
||||
|
||||
/// Completion object returned by mapped delegate `BeginInvoke` methods.
|
||||
#[derive(Clone)]
|
||||
pub struct AsyncInvocation {
|
||||
state: Arc<AsyncInvocationState>,
|
||||
async_state: Object,
|
||||
}
|
||||
|
||||
struct AsyncInvocationState {
|
||||
result: Mutex<Option<Result<(), Error>>>,
|
||||
completed: Condvar,
|
||||
}
|
||||
|
||||
impl fmt::Debug for AsyncInvocation {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter
|
||||
.debug_struct("AsyncInvocation")
|
||||
.field("async_state", &self.async_state)
|
||||
.field("completed", &self.is_completed())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncInvocation {
|
||||
pub(crate) fn pending(async_state: Object) -> Self {
|
||||
Self {
|
||||
state: Arc::new(AsyncInvocationState {
|
||||
result: Mutex::new(None),
|
||||
completed: Condvar::new(),
|
||||
}),
|
||||
async_state,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn complete(&self, result: Result<(), Error>) {
|
||||
*self
|
||||
.state
|
||||
.result
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner) = Some(result);
|
||||
self.state.completed.notify_all();
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn is_completed(&self) -> bool {
|
||||
self.state
|
||||
.result
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.is_some()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn async_state(&self) -> &Object {
|
||||
&self.async_state
|
||||
}
|
||||
|
||||
pub(crate) fn wait(&self) -> Result<(), Error> {
|
||||
let result = self
|
||||
.state
|
||||
.result
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||
let result = self
|
||||
.state
|
||||
.completed
|
||||
.wait_while(result, |result| result.is_none())
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||
result.expect("completion predicate guarantees a result")
|
||||
}
|
||||
}
|
||||
|
||||
type DictionaryHandler = Arc<dyn Fn(DictionaryEventAction, DictionaryEntry) + Send + Sync>;
|
||||
|
||||
/// Opaque target accepted by the metadata-compatible delegate constructor.
|
||||
pub struct DictionaryCallbackTarget(pub DictionaryHandler);
|
||||
|
||||
impl fmt::Debug for DictionaryCallbackTarget {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter.write_str("DictionaryCallbackTarget(..)")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DictionaryChangeCallback(DictionaryHandler);
|
||||
|
||||
impl DictionaryChangeCallback {
|
||||
pub fn from_callback(
|
||||
callback: impl Fn(DictionaryEventAction, DictionaryEntry) + Send + Sync + 'static,
|
||||
) -> Self {
|
||||
Self(Arc::new(callback))
|
||||
}
|
||||
|
||||
pub fn new(object: Object, method: isize) -> Result<Self, Error> {
|
||||
if method != 0 {
|
||||
return Err(Error::Argument);
|
||||
}
|
||||
object
|
||||
.downcast_arc::<DictionaryCallbackTarget>()
|
||||
.map(|target| Self(Arc::clone(&target.0)))
|
||||
.ok_or(Error::Argument)
|
||||
}
|
||||
|
||||
pub fn begin_invoke(
|
||||
&self,
|
||||
action: DictionaryEventAction,
|
||||
entry: DictionaryEntry,
|
||||
callback: Box<dyn Fn(&dyn Any) + Send + Sync>,
|
||||
object: Object,
|
||||
) -> Result<Box<dyn Any + Send + Sync>, Error> {
|
||||
let invocation = AsyncInvocation::pending(object);
|
||||
let worker_invocation = invocation.clone();
|
||||
let handler = Arc::clone(&self.0);
|
||||
std::thread::Builder::new()
|
||||
.name("dictionary-callback".to_owned())
|
||||
.spawn(move || {
|
||||
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
||||
handler(action, entry);
|
||||
}))
|
||||
.map_err(|_| Error::InvalidOperation);
|
||||
worker_invocation.complete(result);
|
||||
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
||||
callback(&worker_invocation);
|
||||
}));
|
||||
})
|
||||
.map_err(|_| Error::InvalidOperation)?;
|
||||
Ok(Box::new(invocation))
|
||||
}
|
||||
|
||||
pub fn end_invoke(&self, result: Box<dyn Any + Send + Sync>) -> Result<(), Error> {
|
||||
result
|
||||
.downcast::<AsyncInvocation>()
|
||||
.map_err(|_| Error::Argument)?
|
||||
.wait()
|
||||
}
|
||||
|
||||
pub fn invoke(
|
||||
&self,
|
||||
action: DictionaryEventAction,
|
||||
entry: DictionaryEntry,
|
||||
) -> Result<(), Error> {
|
||||
(self.0)(action, entry);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
#[test]
|
||||
fn asynchronous_delegate_can_be_observed_and_ended() {
|
||||
let calls = Arc::new(AtomicUsize::new(0));
|
||||
let observed = Arc::clone(&calls);
|
||||
let callback = DictionaryChangeCallback::from_callback(move |_, _| {
|
||||
observed.fetch_add(1, Ordering::Relaxed);
|
||||
});
|
||||
let result = callback
|
||||
.begin_invoke(
|
||||
DictionaryEventAction::Add,
|
||||
DictionaryEntry(Object::Integer(1), Object::Integer(2)),
|
||||
Box::new(|_| {}),
|
||||
Object::Undefined,
|
||||
)
|
||||
.unwrap();
|
||||
callback.end_invoke(result).unwrap();
|
||||
assert_eq!(calls.load(Ordering::Relaxed), 1);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user