Merge pull request #89 from Xaeroxe23/master

Fix QueueIterator leak
This commit is contained in:
tomaka 2017-02-01 10:12:21 +01:00 committed by GitHub
commit 32c75caed1

View file

@ -115,7 +115,18 @@ impl Engine {
if sounds.len() == 0 {
return Ok(());
}
sounds.retain(|s| !s.1.local_dead);
// Drop if it's not playing a real source, and it's sink is detached
// or the sink was dropped before being detached.
sounds.retain(|s| {
if s.1.local_dead {
return false;
}
if !s.1.is_playing_real_source {
return !s.1.local_handle_dead;
} else {
true
}
});
let samples_iter = (0..).map(|_| {
let v = sounds.iter_mut().map(|s| s.1.next().unwrap_or(0.0))
.fold(0.0, |a, b| a + b);
@ -154,6 +165,9 @@ impl Engine {
// If dead is set to true then this Handle should be removed.
let dead = Arc::new(AtomicBool::new(false));
// Used to detect detached handles.
let handle_dead = Arc::new(AtomicBool::new(false));
// `next_sounds` contains a Vec that can later be used to append new iterators to the sink
let next_sounds = Arc::new(Mutex::new(Vec::new()));
@ -166,8 +180,11 @@ impl Engine {
next: next_sounds.clone(),
local_dead: false,
remote_dead: dead.clone(),
local_handle_dead: false,
remote_handle_dead: handle_dead.clone(),
samples_until_update: update_frequency,
update_frequency: update_frequency,
is_playing_real_source: true,
};
// Adding the new sound to the list of parallel sounds.
@ -186,6 +203,7 @@ impl Engine {
channels: end_point.format.channels.len() as u16,
next_sounds: next_sounds,
dead: dead,
handle_dead: handle_dead,
paused: paused,
volume: volume,
end: Mutex::new(None),
@ -211,6 +229,10 @@ pub struct Handle {
// Pointer to dead value in QueueIterator
dead: Arc<AtomicBool>,
// Set this to true when we are dropped, regardless of if we were detached or not.
// This is read by the engine thread.
handle_dead: Arc<AtomicBool>,
// Holds a pointer to the list of iterators to be played after the current one has
// finished playing.
next_sounds: Arc<Mutex<Vec<(Box<Iterator<Item = f32> + Send>, Option<Sender<()>>)>>>,
@ -290,6 +312,13 @@ impl Handle {
}
}
impl Drop for Handle {
#[inline]
fn drop(&mut self) {
self.handle_dead.store(true, Ordering::Relaxed);
}
}
// Main source of samples for a voice.
struct QueueIterator {
// The current iterator that produces samples.
@ -308,11 +337,20 @@ struct QueueIterator {
//The dead value which may be manipulated by another thread.
remote_dead: Arc<AtomicBool>,
// Local storage of the handle_dead value. Allows us to only check the remote occasionally.
local_handle_dead: bool,
// Is our handle dead? Used to identify situations with a dropped handle that's been detached.
remote_handle_dead: Arc<AtomicBool>,
//The frequency with which local_dead should be updated by remote_dead
update_frequency: u32,
//How many samples remain until it is time to update local_dead with remote_dead.
samples_until_update: u32,
// Whether we're playing a source from a sink, or a dummy iter
is_playing_real_source: bool,
}
impl Iterator for QueueIterator {
@ -323,6 +361,7 @@ impl Iterator for QueueIterator {
self.samples_until_update -= 1;
if self.samples_until_update == 0 {
self.local_dead = self.remote_dead.load(Ordering::Relaxed);
self.local_handle_dead = self.remote_handle_dead.load(Ordering::Relaxed);
self.samples_until_update = self.update_frequency;
}
if self.local_dead {
@ -341,10 +380,12 @@ impl Iterator for QueueIterator {
let (next, signal_after_end) = {
let mut next = self.next.lock().unwrap();
if next.len() == 0 {
self.is_playing_real_source = false;
// if there's no iter waiting, we create a dummy iter with 1000 null samples
// this avoids a spinlock
(Box::new((0 .. 1000).map(|_| 0.0f32)) as Box<Iterator<Item = f32> + Send>, None)
} else {
self.is_playing_real_source = true;
next.remove(0)
}
};