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:
206
crates/libremetaverse-voice-vivox/src/callbacks.rs
Normal file
206
crates/libremetaverse-voice-vivox/src/callbacks.rs
Normal file
@@ -0,0 +1,206 @@
|
||||
//! Safe native replacements for the CLR delegate types in the Vivox API.
|
||||
|
||||
use crate::Error;
|
||||
use libremetaverse_types::compat::{Object, SocketException};
|
||||
use std::any::Any;
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
type AsyncCallback = Box<dyn Fn(&dyn Any) + Send + Sync>;
|
||||
type AsyncResult = Box<dyn Any + Send + Sync>;
|
||||
|
||||
macro_rules! callback_type {
|
||||
($name:ident ( $($argument:ident : $type:ty),* $(,)? )) => {
|
||||
#[derive(Clone)]
|
||||
pub struct $name {
|
||||
handler: Arc<dyn Fn($($type),*) -> Result<(), Error> + Send + Sync>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for $name {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter
|
||||
.debug_struct(stringify!($name))
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl $name {
|
||||
/// Creates a native callback without exposing CLR method pointers.
|
||||
pub fn from_handler<F>(handler: F) -> Self
|
||||
where
|
||||
F: Fn($($type),*) -> Result<(), Error> + Send + Sync + 'static,
|
||||
{
|
||||
Self { handler: Arc::new(handler) }
|
||||
}
|
||||
|
||||
/// Rehydrates a callback previously stored in a mapped `Object`.
|
||||
/// A non-zero CLR method pointer cannot be invoked safely in native
|
||||
/// Rust and is therefore rejected explicitly.
|
||||
pub fn new(object: Object, method: isize) -> Result<Self, Error> {
|
||||
if method != 0 {
|
||||
return Err(Error::Argument);
|
||||
}
|
||||
object.downcast_ref::<Self>().cloned().ok_or(Error::Argument)
|
||||
}
|
||||
|
||||
pub fn invoke(&self, $($argument: $type),*) -> Result<(), Error> {
|
||||
(self.handler)($($argument),*)
|
||||
}
|
||||
|
||||
pub fn begin_invoke(
|
||||
&self,
|
||||
$($argument: $type,)*
|
||||
callback: AsyncCallback,
|
||||
object: Object,
|
||||
) -> Result<AsyncResult, Error> {
|
||||
self.invoke($($argument),*)?;
|
||||
callback(&object);
|
||||
Ok(Box::new(()))
|
||||
}
|
||||
|
||||
pub fn end_invoke(&self, result: AsyncResult) -> Result<(), Error> {
|
||||
result.downcast::<()>().map(|_| ()).map_err(|_| Error::Argument)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
callback_type!(TCPPipeOnDisconnectedCallback(se: SocketException));
|
||||
callback_type!(TCPPipeOnReceiveLineCallback(line: String));
|
||||
callback_type!(VoiceGatewayDaemonConnectedCallback());
|
||||
callback_type!(VoiceGatewayDaemonCouldntConnectCallback());
|
||||
callback_type!(VoiceGatewayDaemonCouldntRunCallback());
|
||||
callback_type!(VoiceGatewayDaemonDisconnectedCallback());
|
||||
callback_type!(VoiceGatewayDaemonExitedCallback());
|
||||
callback_type!(VoiceGatewayDaemonRunningCallback());
|
||||
callback_type!(VoiceGatewayVoiceConnectionChangeCallback(
|
||||
state: crate::VoiceGatewayConnectionState,
|
||||
));
|
||||
callback_type!(VoiceGatewayVoiceMicTestCallback(level: f32));
|
||||
callback_type!(VoiceManagerAuxAudioPropertiesCallback(cookie: i32, energy: f32));
|
||||
callback_type!(VoiceManagerBasicActionCallback(
|
||||
cookie: i32,
|
||||
status_code: i32,
|
||||
status_string: String,
|
||||
));
|
||||
callback_type!(VoiceManagerConnectorCreatedCallback(
|
||||
cookie: i32,
|
||||
status_code: i32,
|
||||
status_string: String,
|
||||
connector_handle: String,
|
||||
));
|
||||
callback_type!(VoiceManagerDevicesCallback(
|
||||
cookie: i32,
|
||||
status_code: i32,
|
||||
status_string: String,
|
||||
current_device: String,
|
||||
));
|
||||
callback_type!(VoiceManagerLoginCallback(
|
||||
cookie: i32,
|
||||
status_code: i32,
|
||||
status_string: String,
|
||||
account_handle: String,
|
||||
));
|
||||
callback_type!(VoiceManagerLoginStateChangeCallback(
|
||||
cookie: i32,
|
||||
account_handle: String,
|
||||
status_code: i32,
|
||||
status_string: String,
|
||||
state: i32,
|
||||
));
|
||||
callback_type!(VoiceManagerNewSessionCallback(
|
||||
cookie: i32,
|
||||
account_handle: String,
|
||||
event_session_handle: String,
|
||||
state: i32,
|
||||
name_string: String,
|
||||
uri_string: String,
|
||||
));
|
||||
callback_type!(VoiceManagerParcelVoiceInfoCallback(
|
||||
region_name: String,
|
||||
local_id: i32,
|
||||
channel_uri: Option<String>,
|
||||
));
|
||||
callback_type!(VoiceManagerParticipantPropertiesCallback(
|
||||
cookie: i32,
|
||||
uri_string: String,
|
||||
status_code: i32,
|
||||
status_string: String,
|
||||
is_locally_muted: bool,
|
||||
is_moderator_muted: bool,
|
||||
is_speaking: bool,
|
||||
volume: i32,
|
||||
energy: f32,
|
||||
));
|
||||
callback_type!(VoiceManagerParticipantStateChangeCallback(
|
||||
cookie: i32,
|
||||
uri_string: String,
|
||||
status_code: i32,
|
||||
status_string: String,
|
||||
state: i32,
|
||||
name_string: String,
|
||||
display_name_string: String,
|
||||
participant_type: i32,
|
||||
));
|
||||
callback_type!(VoiceManagerProvisionAccountCallback(
|
||||
username: String,
|
||||
password: String,
|
||||
));
|
||||
callback_type!(VoiceManagerSessionCreatedCallback(
|
||||
cookie: i32,
|
||||
status_code: i32,
|
||||
status_string: String,
|
||||
session_handle: String,
|
||||
));
|
||||
callback_type!(VoiceManagerSessionStateChangeCallback(
|
||||
cookie: i32,
|
||||
uri_string: String,
|
||||
status_code: i32,
|
||||
status_string: String,
|
||||
event_session_handle: String,
|
||||
state: i32,
|
||||
is_channel: bool,
|
||||
name_string: String,
|
||||
));
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::sync::atomic::{AtomicI32, Ordering};
|
||||
|
||||
#[test]
|
||||
fn callback_invocation_and_apm_completion_execute_once() {
|
||||
let total = Arc::new(AtomicI32::new(0));
|
||||
let observed = Arc::clone(&total);
|
||||
let callback =
|
||||
VoiceManagerAuxAudioPropertiesCallback::from_handler(move |cookie, energy| {
|
||||
observed.fetch_add(cookie + energy as i32, Ordering::SeqCst);
|
||||
Ok(())
|
||||
});
|
||||
callback.invoke(2, 3.0).unwrap();
|
||||
let completion_count = Arc::new(AtomicI32::new(0));
|
||||
let completed = Arc::clone(&completion_count);
|
||||
let result = callback
|
||||
.begin_invoke(
|
||||
4,
|
||||
5.0,
|
||||
Box::new(move |_| {
|
||||
completed.fetch_add(1, Ordering::SeqCst);
|
||||
}),
|
||||
Object::Undefined,
|
||||
)
|
||||
.unwrap();
|
||||
callback.end_invoke(result).unwrap();
|
||||
assert_eq!(total.load(Ordering::SeqCst), 14);
|
||||
assert_eq!(completion_count.load(Ordering::SeqCst), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn callback_round_trips_through_mapped_object_without_raw_pointer() {
|
||||
let callback = TCPPipeOnReceiveLineCallback::from_handler(|_| Ok(()));
|
||||
let restored = TCPPipeOnReceiveLineCallback::new(Object::opaque(callback), 0).unwrap();
|
||||
restored.invoke("line".into()).unwrap();
|
||||
assert!(TCPPipeOnReceiveLineCallback::new(Object::Undefined, 0).is_err());
|
||||
assert!(TCPPipeOnReceiveLineCallback::new(Object::Undefined, 1).is_err());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user