1 // Copyright © 2017 Mozilla Foundation
2 //
3 // This program is made available under an ISC-style license.  See the
4 // accompanying file LICENSE for details
5 #![warn(unused_extern_crates)]
6 
7 #[macro_use]
8 extern crate error_chain;
9 #[macro_use]
10 extern crate log;
11 
12 use audio_thread_priority::promote_current_thread_to_real_time;
13 use audioipc::core;
14 use audioipc::platformhandle_passing::framed_with_platformhandles;
15 use audioipc::rpc;
16 use audioipc::{MessageStream, PlatformHandle, PlatformHandleType};
17 use futures::sync::oneshot;
18 use futures::Future;
19 use once_cell::sync::Lazy;
20 use std::ffi::{CStr, CString};
21 use std::os::raw::c_void;
22 use std::ptr;
23 use std::sync::Mutex;
24 use tokio::reactor;
25 
26 mod server;
27 
28 struct CubebContextParams {
29     context_name: CString,
30     backend_name: Option<CString>,
31 }
32 
33 static G_CUBEB_CONTEXT_PARAMS: Lazy<Mutex<CubebContextParams>> = Lazy::new(|| {
34     Mutex::new(CubebContextParams {
35         context_name: CString::new("AudioIPC Server").unwrap(),
36         backend_name: None,
37     })
38 });
39 
40 #[allow(deprecated)]
41 pub mod errors {
42     #![allow(clippy::upper_case_acronyms)]
43     error_chain! {
44         links {
45             AudioIPC(::audioipc::errors::Error, ::audioipc::errors::ErrorKind);
46         }
47         foreign_links {
48             Cubeb(cubeb_core::Error);
49             Io(::std::io::Error);
50             Canceled(::futures::sync::oneshot::Canceled);
51         }
52     }
53 }
54 
55 use crate::errors::*;
56 
57 struct ServerWrapper {
58     core_thread: core::CoreThread,
59     callback_thread: core::CoreThread,
60 }
61 
run() -> Result<ServerWrapper>62 fn run() -> Result<ServerWrapper> {
63     trace!("Starting up cubeb audio server event loop thread...");
64 
65     let callback_thread = core::spawn_thread(
66         "AudioIPC Callback RPC",
67         || {
68             match promote_current_thread_to_real_time(0, 48000) {
69                 Ok(_) => {}
70                 Err(_) => {
71                     debug!("Failed to promote audio callback thread to real-time.");
72                 }
73             }
74             trace!("Starting up cubeb audio callback event loop thread...");
75             Ok(())
76         },
77         || {},
78     )
79     .map_err(|e| {
80         debug!(
81             "Failed to start cubeb audio callback event loop thread: {:?}",
82             e
83         );
84         e
85     })?;
86 
87     let core_thread = core::spawn_thread(
88         "AudioIPC Server RPC",
89         move || {
90             audioipc::server_platform_init();
91             Ok(())
92         },
93         || {},
94     )
95     .map_err(|e| {
96         debug!("Failed to cubeb audio core event loop thread: {:?}", e);
97         e
98     })?;
99 
100     Ok(ServerWrapper {
101         core_thread,
102         callback_thread,
103     })
104 }
105 
106 #[allow(clippy::missing_safety_doc)]
107 #[no_mangle]
audioipc_server_start( context_name: *const std::os::raw::c_char, backend_name: *const std::os::raw::c_char, ) -> *mut c_void108 pub unsafe extern "C" fn audioipc_server_start(
109     context_name: *const std::os::raw::c_char,
110     backend_name: *const std::os::raw::c_char,
111 ) -> *mut c_void {
112     let mut params = G_CUBEB_CONTEXT_PARAMS.lock().unwrap();
113     if !context_name.is_null() {
114         params.context_name = CStr::from_ptr(context_name).to_owned();
115     }
116     if !backend_name.is_null() {
117         let backend_string = CStr::from_ptr(backend_name).to_owned();
118         params.backend_name = Some(backend_string);
119     }
120     match run() {
121         Ok(server) => Box::into_raw(Box::new(server)) as *mut _,
122         Err(_) => ptr::null_mut() as *mut _,
123     }
124 }
125 
126 #[no_mangle]
audioipc_server_new_client(p: *mut c_void) -> PlatformHandleType127 pub extern "C" fn audioipc_server_new_client(p: *mut c_void) -> PlatformHandleType {
128     let (wait_tx, wait_rx) = oneshot::channel();
129     let wrapper: &ServerWrapper = unsafe { &*(p as *mut _) };
130 
131     let core_handle = wrapper.callback_thread.handle();
132 
133     // We create a connected pair of anonymous IPC endpoints. One side
134     // is registered with the reactor core, the other side is returned
135     // to the caller.
136     MessageStream::anonymous_ipc_pair()
137         .map(|(ipc_server, ipc_client)| {
138             // Spawn closure to run on same thread as reactor::Core
139             // via remote handle.
140             wrapper
141                 .core_thread
142                 .handle()
143                 .spawn(futures::future::lazy(|| {
144                     trace!("Incoming connection");
145                     let handle = reactor::Handle::default();
146                     ipc_server.into_tokio_ipc(&handle)
147                     .map(|sock| {
148                         let transport = framed_with_platformhandles(sock, Default::default());
149                         rpc::bind_server(transport, server::CubebServer::new(core_handle));
150                     }).map_err(|_| ())
151                     // Notify waiting thread that server has been registered.
152                     .and_then(|_| wait_tx.send(()))
153                 }))
154                 .expect("Failed to spawn CubebServer");
155             // Wait for notification that server has been registered
156             // with reactor::Core.
157             let _ = wait_rx.wait();
158             unsafe { PlatformHandle::from(ipc_client).into_raw() }
159         })
160         .unwrap_or(audioipc::INVALID_HANDLE_VALUE)
161 }
162 
163 #[no_mangle]
audioipc_server_stop(p: *mut c_void)164 pub extern "C" fn audioipc_server_stop(p: *mut c_void) {
165     let wrapper = unsafe { Box::<ServerWrapper>::from_raw(p as *mut _) };
166     drop(wrapper);
167 }
168