diff options
Diffstat (limited to 'third_party/rust/audioipc2-server/src/lib.rs')
-rw-r--r-- | third_party/rust/audioipc2-server/src/lib.rs | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/third_party/rust/audioipc2-server/src/lib.rs b/third_party/rust/audioipc2-server/src/lib.rs new file mode 100644 index 0000000000..c90de38c66 --- /dev/null +++ b/third_party/rust/audioipc2-server/src/lib.rs @@ -0,0 +1,221 @@ +// Copyright © 2017 Mozilla Foundation +// +// This program is made available under an ISC-style license. See the +// accompanying file LICENSE for details +#![warn(unused_extern_crates)] + +#[macro_use] +extern crate error_chain; +#[macro_use] +extern crate log; + +use audio_thread_priority::promote_current_thread_to_real_time; +use audioipc::ipccore; +use audioipc::sys; +use audioipc::PlatformHandleType; +use once_cell::sync::Lazy; +use std::ffi::{CStr, CString}; +use std::os::raw::c_void; +use std::ptr; +use std::sync::Mutex; +use std::thread; + +mod server; + +struct CubebContextParams { + context_name: CString, + backend_name: Option<CString>, +} + +static G_CUBEB_CONTEXT_PARAMS: Lazy<Mutex<CubebContextParams>> = Lazy::new(|| { + Mutex::new(CubebContextParams { + context_name: CString::new("AudioIPC Server").unwrap(), + backend_name: None, + }) +}); + +#[allow(deprecated)] +pub mod errors { + #![allow(clippy::upper_case_acronyms)] + error_chain! { + links { + AudioIPC(::audioipc::errors::Error, ::audioipc::errors::ErrorKind); + } + foreign_links { + Cubeb(cubeb_core::Error); + Io(::std::io::Error); + } + } +} + +use crate::errors::*; + +struct ServerWrapper { + rpc_thread: ipccore::EventLoopThread, + callback_thread: ipccore::EventLoopThread, + device_collection_thread: ipccore::EventLoopThread, +} + +fn register_thread(callback: Option<extern "C" fn(*const ::std::os::raw::c_char)>) { + if let Some(func) = callback { + let name = CString::new(thread::current().name().unwrap()).unwrap(); + func(name.as_ptr()); + } +} + +fn unregister_thread(callback: Option<extern "C" fn()>) { + if let Some(func) = callback { + func(); + } +} + +fn init_threads( + thread_create_callback: Option<extern "C" fn(*const ::std::os::raw::c_char)>, + thread_destroy_callback: Option<extern "C" fn()>, +) -> Result<ServerWrapper> { + let rpc_name = "AudioIPC Server RPC"; + let rpc_thread = ipccore::EventLoopThread::new( + rpc_name.to_string(), + None, + move || { + trace!("Starting {} thread", rpc_name); + register_thread(thread_create_callback); + audioipc::server_platform_init(); + }, + move || { + unregister_thread(thread_destroy_callback); + trace!("Stopping {} thread", rpc_name); + }, + ) + .map_err(|e| { + debug!("Failed to start {} thread: {:?}", rpc_name, e); + e + })?; + + let callback_name = "AudioIPC Server Callback"; + let callback_thread = ipccore::EventLoopThread::new( + callback_name.to_string(), + None, + move || { + trace!("Starting {} thread", callback_name); + if let Err(e) = promote_current_thread_to_real_time(0, 48000) { + debug!( + "Failed to promote {} thread to real-time: {:?}", + callback_name, e + ); + } + register_thread(thread_create_callback); + }, + move || { + unregister_thread(thread_destroy_callback); + trace!("Stopping {} thread", callback_name); + }, + ) + .map_err(|e| { + debug!("Failed to start {} thread: {:?}", callback_name, e); + e + })?; + + let device_collection_name = "AudioIPC DeviceCollection RPC"; + let device_collection_thread = ipccore::EventLoopThread::new( + device_collection_name.to_string(), + None, + move || { + trace!("Starting {} thread", device_collection_name); + register_thread(thread_create_callback); + }, + move || { + unregister_thread(thread_destroy_callback); + trace!("Stopping {} thread", device_collection_name); + }, + ) + .map_err(|e| { + debug!("Failed to start {} thread: {:?}", device_collection_name, e); + e + })?; + + Ok(ServerWrapper { + rpc_thread, + callback_thread, + device_collection_thread, + }) +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct AudioIpcServerInitParams { + // Fields only need to be public for ipctest. + pub thread_create_callback: Option<extern "C" fn(*const ::std::os::raw::c_char)>, + pub thread_destroy_callback: Option<extern "C" fn()>, +} + +#[allow(clippy::missing_safety_doc)] +#[no_mangle] +pub unsafe extern "C" fn audioipc2_server_start( + context_name: *const std::os::raw::c_char, + backend_name: *const std::os::raw::c_char, + init_params: *const AudioIpcServerInitParams, +) -> *mut c_void { + assert!(!init_params.is_null()); + let mut params = G_CUBEB_CONTEXT_PARAMS.lock().unwrap(); + if !context_name.is_null() { + params.context_name = CStr::from_ptr(context_name).to_owned(); + } + if !backend_name.is_null() { + let backend_string = CStr::from_ptr(backend_name).to_owned(); + params.backend_name = Some(backend_string); + } + match init_threads( + (*init_params).thread_create_callback, + (*init_params).thread_destroy_callback, + ) { + Ok(server) => Box::into_raw(Box::new(server)) as *mut _, + Err(_) => ptr::null_mut() as *mut _, + } +} + +// A `shm_area_size` of 0 allows the server to calculate an appropriate shm size for each stream. +// A non-zero `shm_area_size` forces all allocations to the specified size. +#[no_mangle] +pub extern "C" fn audioipc2_server_new_client( + p: *mut c_void, + shm_area_size: usize, +) -> PlatformHandleType { + let wrapper: &ServerWrapper = unsafe { &*(p as *mut _) }; + + // We create a connected pair of anonymous IPC endpoints. One side + // is registered with the event loop core, the other side is returned + // to the caller to be remoted to the client to complete setup. + let (server_pipe, client_pipe) = match sys::make_pipe_pair() { + Ok((server_pipe, client_pipe)) => (server_pipe, client_pipe), + Err(e) => { + error!( + "audioipc_server_new_client - make_pipe_pair failed: {:?}", + e + ); + return audioipc::INVALID_HANDLE_VALUE; + } + }; + + let rpc_thread = wrapper.rpc_thread.handle(); + let callback_thread = wrapper.callback_thread.handle(); + let device_collection_thread = wrapper.device_collection_thread.handle(); + + let server = server::CubebServer::new( + callback_thread.clone(), + device_collection_thread.clone(), + shm_area_size, + ); + if let Err(e) = rpc_thread.bind_server(server, server_pipe) { + error!("audioipc_server_new_client - bind_server failed: {:?}", e); + return audioipc::INVALID_HANDLE_VALUE; + } + + unsafe { client_pipe.into_raw() } +} + +#[no_mangle] +pub extern "C" fn audioipc2_server_stop(p: *mut c_void) { + let wrapper = unsafe { Box::<ServerWrapper>::from_raw(p as *mut _) }; + drop(wrapper); +} |