From c700315b305368f2db935ec9619d2ccdcb6ae39c Mon Sep 17 00:00:00 2001 From: Paul Lietar Date: Mon, 22 Feb 2016 05:41:37 +0000 Subject: [PATCH] Fix a deadlock between SpircManager and Player. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The player was invoking its observers with the state locked, and the SpircManager’s registered observer would try to lock it again. Instead, the observers are invoked with a cloned state, and the SpircManager avoids locking the state again. --- src/player.rs | 11 ++++++++--- src/spirc.rs | 14 +++++++++----- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/player.rs b/src/player.rs index 433c7a03..39f6d560 100644 --- a/src/player.rs +++ b/src/player.rs @@ -20,6 +20,7 @@ pub struct Player { commands: mpsc::Sender, } +#[derive(Clone)] pub struct PlayerState { status: PlayStatus, position_ms: u32, @@ -38,6 +39,7 @@ struct PlayerInternal { commands: mpsc::Receiver, } +#[derive(Debug)] enum PlayerCommand { Load(SpotifyId, bool, u32), Play, @@ -102,8 +104,8 @@ impl Player { self.command(PlayerCommand::Seek(position_ms)); } - pub fn state(&self) -> MutexGuard { - self.state.lock().unwrap() + pub fn state(&self) -> PlayerState { + self.state.lock().unwrap().clone() } pub fn volume(&self, vol: u16) { @@ -315,8 +317,11 @@ impl PlayerInternal { let observers = self.observers.lock().unwrap(); if update { guard.update_time = util::now_ms(); + let state = guard.clone(); + drop(guard); + for observer in observers.iter() { - observer(&guard); + observer(&state); } } } diff --git a/src/spirc.rs b/src/spirc.rs index 41ffc890..e22a70b2 100644 --- a/src/spirc.rs +++ b/src/spirc.rs @@ -8,6 +8,7 @@ use util::version::version_string; use mercury::{MercuryRequest, MercuryMethod}; use player::{Player, PlayerState}; +use std::borrow::Cow; use std::sync::{Mutex, Arc}; use std::collections::HashMap; @@ -420,8 +421,11 @@ impl<'a> CommandSender<'a> { } fn send(self) { - let internal_player_state = self.spirc_internal.player.state(); - let s = self.player_state.unwrap_or(&*internal_player_state); + let state = self.player_state.map_or_else(|| { + Cow::Owned(self.spirc_internal.player.state()) + }, |s| { + Cow::Borrowed(s) + }); let mut pkt = protobuf_init!(protocol::spirc::Frame::new(), { version: 1, @@ -432,12 +436,12 @@ impl<'a> CommandSender<'a> { recipient: RepeatedField::from_vec( self.recipient.map(|r| vec![r.to_owned()] ).unwrap_or(vec![]) ), - device_state: self.spirc_internal.device_state(s), - state_update_id: s.update_time() as i64, + device_state: self.spirc_internal.device_state(&state), + state_update_id: state.update_time() }); if self.spirc_internal.is_active { - pkt.set_state(self.spirc_internal.spirc_state(s)); + pkt.set_state(self.spirc_internal.spirc_state(&state)); } self.spirc_internal