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