Skip to content

Commit fb30d29

Browse files
committed
Avoid calling alsa::PCM::new() multiple times.
Ensures that alsa::PCM::new() is called only once for a given Device so that we are not rapidly opening and closing ALSA devices.
1 parent 296cf5c commit fb30d29

File tree

3 files changed

+68
-22
lines changed

3 files changed

+68
-22
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ lazy_static = "1.3"
3030
alsa = "0.4.3"
3131
nix = "0.15.0"
3232
libc = "0.2.65"
33+
parking_lot = "0.11"
3334
jack = { version = "0.6.5", optional = true }
3435

3536
[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies]

src/host/alsa/enumerate.rs

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use super::alsa;
2+
use super::parking_lot::Mutex;
23
use super::Device;
34
use {BackendSpecificError, DevicesError};
45

@@ -48,21 +49,16 @@ impl Iterator for Devices {
4849
};
4950

5051
// See if the device has an available output stream.
51-
let has_available_output = {
52-
let playback_handle =
53-
alsa::pcm::PCM::new(&name, alsa::Direction::Playback, true);
54-
playback_handle.is_ok()
55-
};
52+
let playback = alsa::pcm::PCM::new(&name, alsa::Direction::Playback, true).ok();
5653

5754
// See if the device has an available input stream.
58-
let has_available_input = {
59-
let capture_handle =
60-
alsa::pcm::PCM::new(&name, alsa::Direction::Capture, true);
61-
capture_handle.is_ok()
62-
};
55+
let capture = alsa::pcm::PCM::new(&name, alsa::Direction::Capture, true).ok();
6356

64-
if has_available_output || has_available_input {
65-
return Some(Device(name));
57+
if playback.is_some() || capture.is_some() {
58+
return Some(Device {
59+
name,
60+
handles: Mutex::new(super::DeviceHandles { playback, capture }),
61+
});
6662
}
6763
}
6864
}
@@ -72,12 +68,24 @@ impl Iterator for Devices {
7268

7369
#[inline]
7470
pub fn default_input_device() -> Option<Device> {
75-
Some(Device("default".to_owned()))
71+
Some(Device {
72+
name: "default".to_owned(),
73+
handles: Mutex::new(super::DeviceHandles {
74+
playback: None,
75+
capture: None,
76+
}),
77+
})
7678
}
7779

7880
#[inline]
7981
pub fn default_output_device() -> Option<Device> {
80-
Some(Device("default".to_owned()))
82+
Some(Device {
83+
name: "default".to_owned(),
84+
handles: Mutex::new(super::DeviceHandles {
85+
playback: None,
86+
capture: None,
87+
}),
88+
})
8189
}
8290

8391
impl From<alsa::Error> for DevicesError {

src/host/alsa/mod.rs

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
extern crate alsa;
22
extern crate libc;
3+
extern crate parking_lot;
34

45
use self::alsa::poll::Descriptors;
6+
use self::parking_lot::Mutex;
57
use crate::{
68
BackendSpecificError, BufferSize, BuildStreamError, ChannelCount, Data,
79
DefaultStreamConfigError, DeviceNameError, DevicesError, InputCallbackInfo, OutputCallbackInfo,
@@ -163,8 +165,15 @@ impl Drop for TriggerReceiver {
163165
}
164166
}
165167

166-
#[derive(Clone, Debug, PartialEq, Eq)]
167-
pub struct Device(String);
168+
struct DeviceHandles {
169+
playback: Option<alsa::PCM>,
170+
capture: Option<alsa::PCM>,
171+
}
172+
173+
pub struct Device {
174+
name: String,
175+
handles: Mutex<DeviceHandles>,
176+
}
168177

169178
impl Device {
170179
fn build_stream_inner(
@@ -173,10 +182,21 @@ impl Device {
173182
sample_format: SampleFormat,
174183
stream_type: alsa::Direction,
175184
) -> Result<StreamInner, BuildStreamError> {
176-
let name = &self.0;
185+
let name = &self.name;
177186

178-
let handle = match alsa::pcm::PCM::new(name, stream_type, true).map_err(|e| (e, e.errno()))
179-
{
187+
let device_handle = {
188+
let mut guard = self.handles.lock();
189+
match stream_type {
190+
alsa::Direction::Playback => guard.playback.take(),
191+
alsa::Direction::Capture => guard.capture.take(),
192+
}
193+
};
194+
195+
let handle_result = Ok(device_handle).transpose().unwrap_or_else(|| {
196+
alsa::pcm::PCM::new(name, stream_type, true).map_err(|e| (e, e.errno()))
197+
});
198+
199+
let handle = match handle_result {
180200
Err((_, Some(nix::errno::Errno::EBUSY))) => {
181201
return Err(BuildStreamError::DeviceNotAvailable)
182202
}
@@ -229,16 +249,33 @@ impl Device {
229249

230250
#[inline]
231251
fn name(&self) -> Result<String, DeviceNameError> {
232-
Ok(self.0.clone())
252+
Ok(self.name.clone())
233253
}
234254

235255
fn supported_configs(
236256
&self,
237257
stream_t: alsa::Direction,
238258
) -> Result<VecIntoIter<SupportedStreamConfigRange>, SupportedStreamConfigsError> {
239-
let name = &self.0;
259+
let name = &self.name;
260+
261+
let mut guard = self.handles.lock();
262+
263+
let device_handle = match stream_t {
264+
alsa::Direction::Playback => &mut guard.playback,
265+
alsa::Direction::Capture => &mut guard.capture,
266+
};
267+
268+
let handle_result = match device_handle {
269+
Some(handle) => Ok(handle),
270+
None => alsa::pcm::PCM::new(name, stream_t, true)
271+
.map(|handle| {
272+
*device_handle = Some(handle);
273+
device_handle.as_mut().unwrap()
274+
})
275+
.map_err(|e| (e, e.errno())),
276+
};
240277

241-
let handle = match alsa::pcm::PCM::new(name, stream_t, true).map_err(|e| (e, e.errno())) {
278+
let handle = match handle_result {
242279
Err((_, Some(nix::errno::Errno::ENOENT)))
243280
| Err((_, Some(nix::errno::Errno::EBUSY))) => {
244281
return Err(SupportedStreamConfigsError::DeviceNotAvailable)

0 commit comments

Comments
 (0)