finish reconnection logic + fix mac compilation

This commit is contained in:
Théo Monnom
2022-12-23 00:00:50 +01:00
parent 9c489d51f4
commit 167418ca24
3 changed files with 78 additions and 44 deletions
+54 -40
View File
@@ -12,6 +12,7 @@ use std::time::Duration;
use thiserror::Error; use thiserror::Error;
use tokio::sync::RwLock as AsyncRwLock; use tokio::sync::RwLock as AsyncRwLock;
use tokio::task::JoinHandle; use tokio::task::JoinHandle;
use tokio::time::{interval, Interval};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use tokio::sync::{mpsc, oneshot}; use tokio::sync::{mpsc, oneshot};
@@ -79,9 +80,8 @@ pub enum EngineEvent {
Disconnected, Disconnected,
} }
// TODO(theomonnom): Smarter retry intervals
pub const RECONNECT_ATTEMPTS: u32 = 10; pub const RECONNECT_ATTEMPTS: u32 = 10;
pub const RECONNECT_INTERVAL: Duration = Duration::from_millis(300); pub const RECONNECT_INTERVAL: Duration = Duration::from_secs(5);
lazy_static! { lazy_static! {
// Share one LKRuntime across all RTCEngine instances // Share one LKRuntime across all RTCEngine instances
@@ -102,9 +102,13 @@ struct EngineInner {
lk_runtime: Arc<LKRuntime>, lk_runtime: Arc<LKRuntime>,
session_info: Mutex<Option<SessionInfo>>, // Last/Current Sessioninfo session_info: Mutex<Option<SessionInfo>>, // Last/Current Sessioninfo
running_handle: AsyncRwLock<Option<EngineHandle>>, running_handle: AsyncRwLock<Option<EngineHandle>>,
reconnecting: AtomicBool,
opened: AtomicBool, opened: AtomicBool,
engine_emitter: EngineEmitter, engine_emitter: EngineEmitter,
// Reconnecting fields
reconnecting: AtomicBool,
full_reconnect: AtomicBool,
reconnect_interval: Mutex<Interval>,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -131,9 +135,11 @@ impl RTCEngine {
lk_runtime: lk_runtime.unwrap(), lk_runtime: lk_runtime.unwrap(),
session_info: Default::default(), session_info: Default::default(),
running_handle: Default::default(), running_handle: Default::default(),
reconnecting: Default::default(),
opened: Default::default(), opened: Default::default(),
engine_emitter, engine_emitter,
reconnecting: Default::default(),
full_reconnect: Default::default(),
reconnect_interval: Mutex::new(interval(RECONNECT_INTERVAL)),
}); });
(Self { inner }, engine_events) (Self { inner }, engine_events)
@@ -214,7 +220,7 @@ impl EngineInner {
}, },
_ = &mut close_receiver => { _ = &mut close_receiver => {
break; break;
}
} }
} }
} }
@@ -225,10 +231,12 @@ impl EngineInner {
source, source,
reason, reason,
can_reconnect, can_reconnect,
retry_now,
full_reconnect,
} => { } => {
info!("received session close: {}, {:?}", source, reason); info!("received session close: {}, {:?}", source, reason);
if can_reconnect { if can_reconnect {
self.handle_disconnected().await; self.clone().try_reconnect(retry_now, full_reconnect);
} else { } else {
self.close().await; self.close().await;
} }
@@ -301,6 +309,8 @@ impl EngineInner {
let _ = self.engine_emitter.send(EngineEvent::Disconnected).await; let _ = self.engine_emitter.send(EngineEvent::Disconnected).await;
} }
// Wait for the reconnection task to finish
// Return directly if no open RTCSession
async fn wait_reconnection(&self) -> EngineResult<()> { async fn wait_reconnection(&self) -> EngineResult<()> {
if !self.opened.load(Ordering::SeqCst) { if !self.opened.load(Ordering::SeqCst) {
Err(EngineError::Connection("not opened".to_owned()))? Err(EngineError::Connection("not opened".to_owned()))?
@@ -317,37 +327,51 @@ impl EngineInner {
Ok(()) Ok(())
} }
fn try_reconnect(self: Arc<Self>) { /// Start the reconnect task if not already started
warn!("reconnecting RTCEngine..."); fn try_reconnect(self: Arc<Self>, retry_now: bool, full_reconnect: bool) {
if !self.opened.load(Ordering::SeqCst) {
if !self.opened.load(Ordering::SeqCst) || self.reconnecting.load(Ordering::SeqCst) {
return; return;
} }
let mut reconnect_task = self.reconnect_task.lock(); if self.reconnecting.load(Ordering::SeqCst) {
*reconnect_task = Some(tokio::spawn({ if retry_now {
self.reconnect_interval.lock().reset();
self.full_reconnect.store(full_reconnect, Ordering::SeqCst);
}
return;
}
warn!("reconnecting RTCEngine...");
self.reconnecting.store(true, Ordering::SeqCst);
self.full_reconnect.store(full_reconnect, Ordering::SeqCst);
self.reconnect_interval.lock().reset();
tokio::spawn({
let inner = self.clone(); let inner = self.clone();
async move { async move {
inner.handle_disconnected().await; let res = inner.reconnect_task().await;
inner.reconnect_task.lock().take(); inner.reconnecting.store(false, Ordering::SeqCst);
if res.is_err() {
error!("failed to reconnect after {} attemps", RECONNECT_ATTEMPTS);
inner.close().await;
}
} }
})); });
} }
/// Called every time the PeerConnection or the SignalClient is closed /// Called every time the PeerConnection or the SignalClient is closed
/// We first try to resume the connection, if it fails, we start a full reconnect. /// We first try to resume the connection, if it fails, we start a full reconnect.
async fn handle_disconnected(self: &Arc<Self>) { async fn reconnect_task(self: &Arc<Self>) -> EngineResult<()> {
if !self.opened.load(Ordering::SeqCst) || self.reconnecting.load(Ordering::SeqCst) {
return;
}
self.reconnecting.store(true, Ordering::SeqCst);
warn!("RTCEngine disconnected unexpectedly, reconnecting...");
let mut connected = false;
let mut full_reconnect = false;
for i in 0..RECONNECT_ATTEMPTS { for i in 0..RECONNECT_ATTEMPTS {
if full_reconnect { if !self.opened.load(Ordering::Acquire) {
// The user closed the RTCEngine, cancel the reconnection task
return Ok(());
}
self.reconnect_interval.lock().tick().await;
if self.full_reconnect.load(Ordering::SeqCst) {
if i == 0 { if i == 0 {
let _ = self.engine_emitter.send(EngineEvent::Restarting).await; let _ = self.engine_emitter.send(EngineEvent::Restarting).await;
} }
@@ -357,8 +381,7 @@ impl EngineInner {
error!("restarting connection failed: {}", err); error!("restarting connection failed: {}", err);
} else { } else {
let _ = self.engine_emitter.send(EngineEvent::Restarted).await; let _ = self.engine_emitter.send(EngineEvent::Restarted).await;
connected = true; return Ok(());
break;
} }
} else { } else {
if i == 0 { if i == 0 {
@@ -369,25 +392,16 @@ impl EngineInner {
if let Err(err) = self.try_resume_connection().await { if let Err(err) = self.try_resume_connection().await {
error!("resuming connection failed: {}", err); error!("resuming connection failed: {}", err);
if let EngineError::Signal(_) = err { if let EngineError::Signal(_) = err {
full_reconnect = true; self.full_reconnect.store(true, Ordering::SeqCst);
} }
} else { } else {
info!("Connected but failed?");
let _ = self.engine_emitter.send(EngineEvent::Resumed).await; let _ = self.engine_emitter.send(EngineEvent::Resumed).await;
connected = true; return Ok(());
break;
} }
} }
tokio::time::sleep(RECONNECT_INTERVAL).await;
} }
self.reconnecting.store(false, Ordering::SeqCst); Err(EngineError::Connection("failed to reconnect".to_owned()))
if !connected {
error!("failed to reconnect after {} attemps", RECONNECT_ATTEMPTS);
self.close().await;
}
} }
/// Try to recover the connection by doing a full reconnect. /// Try to recover the connection by doing a full reconnect.
@@ -55,6 +55,8 @@ pub enum SessionEvent {
source: String, source: String,
reason: DisconnectReason, reason: DisconnectReason,
can_reconnect: bool, can_reconnect: bool,
full_reconnect: bool,
retry_now: bool,
}, },
Connected, Connected,
} }
@@ -338,7 +340,7 @@ impl SessionInner {
} }
} }
SignalEvent::Close => { SignalEvent::Close => {
self.on_session_disconnected("SignalClient closed", DisconnectReason::UnknownReason, true); self.on_session_disconnected("SignalClient closed", DisconnectReason::UnknownReason, true, false, false);
} }
} }
} else { } else {
@@ -405,7 +407,13 @@ impl SessionInner {
} }
} }
signal_response::Message::Leave(leave) => { signal_response::Message::Leave(leave) => {
self.on_session_disconnected("received leave", leave.reason(), leave.can_reconnect); self.on_session_disconnected(
"received leave",
leave.reason(),
leave.can_reconnect,
true,
true,
);
} }
_ => {} _ => {}
} }
@@ -450,6 +458,8 @@ impl SessionInner {
"pc_state failed", "pc_state failed",
DisconnectReason::UnknownReason, DisconnectReason::UnknownReason,
true, true,
false,
false,
); );
} }
} }
@@ -507,11 +517,20 @@ impl SessionInner {
/// Called when the SignalClient or one of the PeerConnection has lost the connection /// Called when the SignalClient or one of the PeerConnection has lost the connection
/// The RTCEngine may try a reconnect. /// The RTCEngine may try a reconnect.
fn on_session_disconnected(&self, source: &str, reason: DisconnectReason, can_reconnect: bool) { fn on_session_disconnected(
&self,
source: &str,
reason: DisconnectReason,
can_reconnect: bool,
retry_now: bool,
full_reconnect: bool,
) {
let _ = self.emitter.send(SessionEvent::Close { let _ = self.emitter.send(SessionEvent::Close {
source: source.to_owned(), source: source.to_owned(),
reason, reason,
can_reconnect, can_reconnect,
retry_now,
full_reconnect,
}); });
} }
+2 -1
View File
@@ -52,7 +52,7 @@ fn macos_link_search_path() -> Option<String> {
fn main() { fn main() {
// TODO Download precompiled binaries of WebRTC for the target_os // TODO Download precompiled binaries of WebRTC for the target_os
let target_os = "windows"; let target_os = "macos";
//let target_arch = "arm64"; //let target_arch = "arm64";
let libwebrtc_dir = path::PathBuf::from("libwebrtc/src"); let libwebrtc_dir = path::PathBuf::from("libwebrtc/src");
@@ -62,6 +62,7 @@ fn main() {
path::PathBuf::from("./include"), path::PathBuf::from("./include"),
libwebrtc_dir.clone(), libwebrtc_dir.clone(),
libwebrtc_dir.join("third_party/abseil-cpp/"), libwebrtc_dir.join("third_party/abseil-cpp/"),
libwebrtc_dir.join("third_party/libyuv/include/"),
libwebrtc_dir.join("third_party/libc++/"), libwebrtc_dir.join("third_party/libc++/"),
// For mac & ios // For mac & ios
libwebrtc_dir.join("sdk/objc"), libwebrtc_dir.join("sdk/objc"),