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     error_chain! {
43         links {
44             AudioIPC(::audioipc::errors::Error, ::audioipc::errors::ErrorKind);
45         }
46         foreign_links {
47             Cubeb(cubeb_core::Error);
48             Io(::std::io::Error);
49             Canceled(::futures::sync::oneshot::Canceled);
50         }
51     }
52 }
53 
54 use crate::errors::*;
55 
56 struct ServerWrapper {
57     core_thread: core::CoreThread,
58     callback_thread: core::CoreThread,
59 }
60 
run() -> Result<ServerWrapper>61 fn run() -> Result<ServerWrapper> {
62     trace!("Starting up cubeb audio server event loop thread...");
63 
64     let callback_thread = core::spawn_thread(
65         "AudioIPC Callback RPC",
66         || {
67             match promote_current_thread_to_real_time(0, 48000) {
68                 Ok(_) => {}
69                 Err(_) => {
70                     debug!("Failed to promote audio callback thread to real-time.");
71                 }
72             }
73             trace!("Starting up cubeb audio callback event loop thread...");
74             Ok(())
75         },
76         || {},
77     )
78     .or_else(|e| {
79         debug!(
80             "Failed to start cubeb audio callback event loop thread: {:?}",
81             e
82         );
83         Err(e)
84     })?;
85 
86     let core_thread = core::spawn_thread(
87         "AudioIPC Server RPC",
88         move || {
89             audioipc::server_platform_init();
90             Ok(())
91         },
92         || {},
93     )
94     .or_else(|e| {
95         debug!("Failed to cubeb audio core event loop thread: {:?}", e);
96         Err(e)
97     })?;
98 
99     Ok(ServerWrapper {
100         core_thread,
101         callback_thread,
102     })
103 }
104 
105 #[no_mangle]
audioipc_server_start( context_name: *const std::os::raw::c_char, backend_name: *const std::os::raw::c_char, ) -> *mut c_void106 pub unsafe extern "C" fn audioipc_server_start(
107     context_name: *const std::os::raw::c_char,
108     backend_name: *const std::os::raw::c_char,
109 ) -> *mut c_void {
110     let mut params = G_CUBEB_CONTEXT_PARAMS.lock().unwrap();
111     if !context_name.is_null() {
112         params.context_name = CStr::from_ptr(context_name).to_owned();
113     }
114     if !backend_name.is_null() {
115         let backend_string = CStr::from_ptr(backend_name).to_owned();
116         params.backend_name = Some(backend_string);
117     }
118     match run() {
119         Ok(server) => Box::into_raw(Box::new(server)) as *mut _,
120         Err(_) => ptr::null_mut() as *mut _,
121     }
122 }
123 
124 #[no_mangle]
audioipc_server_new_client(p: *mut c_void) -> PlatformHandleType125 pub extern "C" fn audioipc_server_new_client(p: *mut c_void) -> PlatformHandleType {
126     let (wait_tx, wait_rx) = oneshot::channel();
127     let wrapper: &ServerWrapper = unsafe { &*(p as *mut _) };
128 
129     let core_handle = wrapper.callback_thread.handle();
130 
131     // We create a connected pair of anonymous IPC endpoints. One side
132     // is registered with the reactor core, the other side is returned
133     // to the caller.
134     MessageStream::anonymous_ipc_pair()
135         .and_then(|(ipc_server, ipc_client)| {
136             // Spawn closure to run on same thread as reactor::Core
137             // via remote handle.
138             wrapper
139                 .core_thread
140                 .handle()
141                 .spawn(futures::future::lazy(|| {
142                     trace!("Incoming connection");
143                     let handle = reactor::Handle::default();
144                     ipc_server.into_tokio_ipc(&handle)
145                     .and_then(|sock| {
146                         let transport = framed_with_platformhandles(sock, Default::default());
147                         rpc::bind_server(transport, server::CubebServer::new(core_handle));
148                         Ok(())
149                     }).map_err(|_| ())
150                     // Notify waiting thread that server has been registered.
151                     .and_then(|_| wait_tx.send(()))
152                 }))
153                 .expect("Failed to spawn CubebServer");
154             // Wait for notification that server has been registered
155             // with reactor::Core.
156             let _ = wait_rx.wait();
157             Ok(unsafe { PlatformHandle::from(ipc_client).into_raw() })
158         })
159         .unwrap_or(audioipc::INVALID_HANDLE_VALUE)
160 }
161 
162 #[no_mangle]
audioipc_server_stop(p: *mut c_void)163 pub extern "C" fn audioipc_server_stop(p: *mut c_void) {
164     let wrapper = unsafe { Box::<ServerWrapper>::from_raw(p as *mut _) };
165     drop(wrapper);
166 }
167