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