Skip to content

Commit

Permalink
Using multi-threading to avoid possible deadlock (v0.0.7)
Browse files Browse the repository at this point in the history
  • Loading branch information
Shiritai committed Jul 14, 2024
1 parent e3cba40 commit b8741a4
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 65 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "super-rodio"
version = "0.0.6"
version = "0.0.7"
edition = "2021"
authors = ["Tzu-Ching Yang"]
license = "MIT"
Expand Down
16 changes: 16 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,4 +160,20 @@ mod tests {
}
}
}

#[test]
fn test_add_play_pause_set_auto() {
let player = SharedPlayer::make();
for _ in 0..10 {
player.add(Song::from("Music".into(), "audio/music".into()));
}
player.play();
sleep(Duration::from_secs(1));
player.toggle();
sleep(Duration::from_secs(1));
player.use_auto_play();
sleep(Duration::from_secs(2));
player.stop();
// should not be dead if is dead, that is a bug
}
}
24 changes: 12 additions & 12 deletions src/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,31 @@ use crate::song::{ActiveSong, Song};

pub trait Player {
/// Add a song to the player
fn add(&self, song: Song);
fn add(&self, song: Song) -> JoinHandle<()>;
/// Get current waiting list
fn waiting_list(&self) -> Vec<Song>;
fn waiting_list(&self) -> JoinHandle<Vec<Song>>;
/// Get current played history
fn played_list(&self) -> Vec<Song>;
fn played_list(&self) -> JoinHandle<Vec<Song>>;
/// Get current active song
fn current_song(&self) -> ActiveSong;
fn current_song(&self) -> JoinHandle<ActiveSong>;
/// Play the song in waiting list
fn play(&self) -> JoinHandle<()>;
/// Use normal play mode: playing a single song and stop
fn use_normal_play(&self);
fn use_normal_play(&self) -> JoinHandle<()>;
/// Use auto play mode: playing all the songs one-by-one in the playlist
fn use_auto_play(&self);
fn use_auto_play(&self) -> JoinHandle<()>;
/// Toggle play/pause
fn toggle(&self);
/// Stop current music and clear all songs in waiting/played list
fn stop(&self);
fn toggle(&self) -> JoinHandle<()>;
/// Stop current music
fn stop(&self) -> JoinHandle<()>;
/// Clear all songs in waiting/played list
fn clear(&self);
fn clear(&self) -> JoinHandle<()>;
/// Check whether the current song is playing
fn is_playing(&self) -> bool;
fn is_playing(&self) -> JoinHandle<bool>;
/// Set output device generator, the default
/// generator is based on `OutputStream::try_default`
fn set_device_maker(
&self,
with_generator: Box<dyn Fn() -> (OutputStream, OutputStreamHandle) + Send + Sync>,
);
) -> JoinHandle<()>;
}
130 changes: 79 additions & 51 deletions src/shared_player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,34 +23,50 @@ impl Make<Self> for SharedPlayer {
}

impl Player for SharedPlayer {
fn add(&self, song: Song) {
self.write().unwrap().waiting_q.push(song);
fn add(&self, song: Song) -> JoinHandle<()> {
// acquire an arc for this thread
let state = Arc::clone(&self);
spawn(move || {
state.write().unwrap().waiting_q.push(song);
})
}

fn waiting_list(&self) -> Vec<Song> {
self.read()
.unwrap()
.waiting_q
.iter()
.map(Clone::clone)
.collect()
fn waiting_list(&self) -> JoinHandle<Vec<Song>> {
// acquire an arc for this thread
let state = Arc::clone(&self);
spawn(move || {
state
.read()
.unwrap()
.waiting_q
.iter()
.map(Clone::clone)
.collect()
})
}

fn played_list(&self) -> Vec<Song> {
self.read()
.unwrap()
.played_q
.iter()
.map(Clone::clone)
.collect()
fn played_list(&self) -> JoinHandle<Vec<Song>> {
// acquire an arc for this thread
let state = Arc::clone(&self);
spawn(move || {
state
.read()
.unwrap()
.played_q
.iter()
.map(Clone::clone)
.collect()
})
}

fn current_song(&self) -> ActiveSong {
self.read().unwrap().current.clone()
fn current_song(&self) -> JoinHandle<ActiveSong> {
// acquire an arc for this thread
let state = Arc::clone(&self);
spawn(move || state.read().unwrap().current.clone())
}

fn play(&self) -> JoinHandle<()> {
if self.is_playing() {
if self.is_playing().join().unwrap() {
return spawn(|| {});
};
// acquire an arc for child thread
Expand Down Expand Up @@ -110,56 +126,66 @@ impl Player for SharedPlayer {
})
}

fn toggle(&self) {
fn toggle(&self) -> JoinHandle<()> {
// acquire an arc for this thread
let state = Arc::clone(&self);
// check if old sink exists and
// play/pause it by acquiring read lock
if let Some(sink) = &state.read().unwrap().sink {
if sink.is_paused() {
sink.play();
} else {
sink.pause();
}
};
spawn(move || {
// check if old sink exists and
// play/pause it by acquiring read lock
if let Some(sink) = &state.read().unwrap().sink {
if sink.is_paused() {
sink.play();
} else {
sink.pause();
}
};
})
}

fn stop(&self) {
fn stop(&self) -> JoinHandle<()> {
// acquire an arc for this thread
let state = Arc::clone(&self);
// check if old sink exists and
// stop it by acquiring read lock
if let Some(sink) = &state.read().unwrap().sink {
sink.stop();
};

self.clear(); // clear the whole list
spawn(move || {
// check if old sink exists and
// stop it by acquiring read lock
if let Some(sink) = &state.read().unwrap().sink {
sink.stop();
};
})
}

/// Clear the playlist
fn clear(&self) {
fn clear(&self) -> JoinHandle<()> {
// acquire an arc for this thread
let state = Arc::clone(&self);
let mut state = state.write().unwrap();
state.waiting_q.clear();
state.played_q.clear();
spawn(move || {
let mut state = state.write().unwrap();
state.waiting_q.clear();
state.played_q.clear();
})
}

fn is_playing(&self) -> bool {
// acquire an arc for this thread
fn is_playing(&self) -> JoinHandle<bool> {
let state = Arc::clone(&self);
let res = state.read().unwrap().current.state == SongState::PLAY;
res
spawn(move || {
// acquire an arc for this thread
let res = state.read().unwrap().current.state == SongState::PLAY;
res
})
}

fn use_normal_play(&self) {
fn use_normal_play(&self) -> JoinHandle<()> {
let state = Arc::clone(&self);
state.write().unwrap().mode = PlaybackMode::NORMAL;
spawn(move || {
state.write().unwrap().mode = PlaybackMode::NORMAL;
})
}

fn use_auto_play(&self) {
fn use_auto_play(&self) -> JoinHandle<()> {
let state = Arc::clone(&self);
state.write().unwrap().mode = PlaybackMode::AUTO;
spawn(move || {
state.write().unwrap().mode = PlaybackMode::AUTO;
})
}

/// Set output device generator, the default
Expand All @@ -179,8 +205,10 @@ impl Player for SharedPlayer {
with_generator: Box<
dyn Fn() -> (rodio::OutputStream, rodio::OutputStreamHandle) + Send + Sync,
>,
) {
) -> JoinHandle<()> {
let state = Arc::clone(&self);
state.write().unwrap().gen_out = with_generator;
spawn(move || {
state.write().unwrap().gen_out = with_generator;
})
}
}

0 comments on commit b8741a4

Please sign in to comment.