finish reconnection logic + fix mac compilation
This commit is contained in:
@@ -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,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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"),
|
||||||
|
|||||||
Reference in New Issue
Block a user