use callbacks on internal events (#100)

This commit is contained in:
Théo Monnom
2023-06-29 22:52:43 +02:00
committed by GitHub
parent efc334af8e
commit c7c82cc693
33 changed files with 1522 additions and 1447 deletions
+1 -1
View File
@@ -19,7 +19,7 @@ livekit = { path = "../livekit", version = "0.1.1" }
livekit-protocol = { path = "../livekit-protocol", version = "0.1.0" }
tokio = { version = "1.0", features = ["full"] }
futures-util = { version = "0.3", default-features = false, features = ["sink"] }
parking_lot = { version = "0.12.1", features=["send_guard"] }
parking_lot = { version = "0.12.1", features=["deadlock_detection"] }
prost = "0.11.0"
prost-types = "0.11.1"
lazy_static = "1.4.0"
+2 -5
View File
@@ -62,7 +62,7 @@ pub extern "C" fn livekit_ffi_request(
let handle_id = server::FFI_SERVER.next_id();
server::FFI_SERVER
.ffi_handles()
.ffi_handles
.insert(handle_id, Box::new(res));
handle_id
@@ -71,8 +71,5 @@ pub extern "C" fn livekit_ffi_request(
#[no_mangle]
pub extern "C" fn livekit_ffi_drop_handle(handle_id: FfiHandleId) -> bool {
// Free the memory
server::FFI_SERVER
.ffi_handles()
.remove(&handle_id)
.is_some()
server::FFI_SERVER.ffi_handles.remove(&handle_id).is_some()
}
+5 -5
View File
@@ -39,7 +39,7 @@ impl FfiAudioSream {
.id as FfiHandleId;
let track = server
.ffi_handles()
.ffi_handles
.get(&handle_id)
.ok_or(FfiError::InvalidRequest("track not found"))?;
@@ -77,7 +77,7 @@ impl FfiAudioSream {
// Store the new audio stream and return the info
let info = proto::AudioStreamInfo::from(&audio_stream);
server
.ffi_handles()
.ffi_handles
.insert(audio_stream.handle_id, Box::new(audio_stream));
Ok(info)
@@ -110,7 +110,7 @@ impl FfiAudioSream {
let handle_id = server.next_id();
let buffer_info = proto::AudioFrameBufferInfo::from(handle_id, &frame);
server.ffi_handles().insert(handle_id, Box::new(frame));
server.ffi_handles.insert(handle_id, Box::new(frame));
if let Err(err) = server.send_event(proto::ffi_event::Message::AudioStreamEvent(
proto::AudioStreamEvent {
@@ -164,7 +164,7 @@ impl FfiAudioSource {
let source_info = proto::AudioSourceInfo::from(&audio_source);
server
.ffi_handles()
.ffi_handles
.insert(audio_source.handle_id, Box::new(audio_source));
Ok(source_info)
@@ -184,7 +184,7 @@ impl FfiAudioSource {
.id as FfiHandleId;
let frame = server
.ffi_handles()
.ffi_handles
.get(&buffer_handle)
.ok_or(FfiError::InvalidRequest("handle not found"))?;
+52 -32
View File
@@ -8,7 +8,6 @@ use livekit::webrtc::prelude::*;
use livekit::webrtc::video_frame::{native::I420BufferExt, BoxVideoFrameBuffer, I420Buffer};
use parking_lot::Mutex;
use prost::Message;
use std::collections::HashMap;
use std::slice;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
@@ -29,12 +28,12 @@ pub struct FfiConfig {
}
pub struct FfiServer {
rooms: Mutex<HashMap<RoomSid, FfiHandleId>>,
/// Store all Ffi handles inside an HashMap, if this isn't efficient enough
/// We can still use Box::into_raw & Box::from_raw in the future (but keep it safe for now)
ffi_handles: DashMap<FfiHandleId, FfiHandle>,
pub ffi_handles: DashMap<FfiHandleId, FfiHandle>,
pub async_runtime: tokio::runtime::Runtime,
next_id: AtomicUsize,
async_runtime: tokio::runtime::Runtime,
config: Mutex<Option<FfiConfig>>,
}
@@ -42,8 +41,30 @@ impl Default for FfiServer {
fn default() -> Self {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
// Create a background thread which checks for deadlocks every 10s
{
use parking_lot::deadlock;
use std::thread;
use std::time::Duration;
thread::spawn(move || loop {
thread::sleep(Duration::from_secs(10));
let deadlocks = deadlock::check_deadlock();
if deadlocks.is_empty() {
continue;
}
log::error!("{} deadlocks detected", deadlocks.len());
for (i, threads) in deadlocks.iter().enumerate() {
log::error!("Deadlock #{}", i);
for t in threads {
log::error!("Thread Id {:#?}: \n{:#?}", t.thread_id(), t.backtrace());
}
}
});
}
Self {
rooms: Default::default(),
ffi_handles: Default::default(),
next_id: AtomicUsize::new(1), // 0 is invalid
async_runtime: tokio::runtime::Builder::new_multi_thread()
@@ -60,13 +81,19 @@ impl Default for FfiServer {
impl FfiServer {
pub async fn dispose(&'static self) {
// Close all rooms
for (_, room_handle) in self.rooms.lock().drain() {
let room = self.ffi_handles.remove(&room_handle);
if let Some(room) = room {
let ffi_room = room.1.downcast::<room::FfiRoom>().unwrap();
ffi_room.close().await;
log::info!("disposing the FfiServer, closing all rooms...");
let mut rooms = Vec::new();
for handle in self.ffi_handles.iter_mut() {
if let Some(handle) = handle.value().downcast_ref::<room::HandleType>() {
rooms.push(handle.clone());
}
}
for room in rooms {
room.close().await;
}
// Drop all handles
self.ffi_handles.clear();
@@ -78,14 +105,6 @@ impl FfiServer {
self.next_id.fetch_add(1, Ordering::Relaxed)
}
pub fn ffi_handles(&'static self) -> &DashMap<FfiHandleId, FfiHandle> {
&self.ffi_handles
}
pub fn rooms(&'static self) -> &Mutex<HashMap<RoomSid, FfiHandleId>> {
&self.rooms
}
pub fn send_event(&'static self, message: proto::ffi_event::Message) -> FfiResult<()> {
let callback_fn = self
.config
@@ -128,14 +147,13 @@ impl FfiServer {
) -> FfiResult<proto::DisposeResponse> {
*self.config.lock() = None;
let close = self.dispose();
if !dispose.r#async {
self.async_runtime.block_on(close);
self.async_runtime.block_on(self.dispose());
Ok(proto::DisposeResponse::default())
} else {
let async_id = self.next_id();
self.async_runtime.spawn(async move {
close.await;
self.dispose().await;
});
Ok(proto::DisposeResponse {
async_id: Some(proto::FfiAsyncId {
@@ -197,17 +215,19 @@ impl FfiServer {
.ok_or(FfiError::InvalidRequest("room_handle is empty"))?
.id as FfiHandleId;
let ffi_room = self
.ffi_handles
.remove(&room_handle)
.ok_or(FfiError::InvalidRequest("room not found"))?
.1;
let ffi_room = ffi_room
.downcast::<room::FfiRoom>()
.map_err(|_| FfiError::InvalidRequest("room is not a FfiRoom"))?;
self.async_runtime.spawn(async move {
let mut ffi_room = self
.ffi_handles
.get_mut(&room_handle)
.ok_or(FfiError::InvalidRequest("room not found"))
.unwrap();
let ffi_room = ffi_room
.value_mut()
.downcast_mut::<room::HandleType>()
.ok_or(FfiError::InvalidRequest("room is not a FfiRoom"))
.unwrap();
ffi_room.close().await;
let _ = self.send_event(proto::ffi_event::Message::Disconnect(
proto::DisconnectCallback {
@@ -313,7 +333,7 @@ impl FfiServer {
.ok_or(FfiError::InvalidRequest("room not found"))?;
let ffi_room = ffi_room
.downcast_ref::<room::FfiRoom>()
.downcast_ref::<room::HandleType>()
.ok_or(FfiError::InvalidRequest("room is not a FfiRoom"))?;
// Push the data to an async queue (avoid blocking and keep the order)
+31 -34
View File
@@ -1,11 +1,14 @@
use crate::server::FfiServer;
use crate::{proto, FfiAsyncId, FfiError, FfiHandleId, FfiResult};
use livekit::prelude::*;
use parking_lot::Mutex;
use std::slice;
use std::sync::Arc;
use tokio::sync::{broadcast, mpsc};
use tokio::task::JoinHandle;
pub type HandleType = Arc<FfiRoom>;
struct DataPacket {
data: Vec<u8>,
kind: DataPacketKind,
@@ -13,11 +16,15 @@ struct DataPacket {
async_id: FfiAsyncId,
}
pub struct FfiRoom {
room: Arc<Room>,
struct Handle {
event_handle: JoinHandle<()>,
data_handle: JoinHandle<()>,
close_tx: broadcast::Sender<()>,
}
pub struct FfiRoom {
room: Arc<Room>,
handle: Mutex<Option<Handle>>,
data_tx: mpsc::UnboundedSender<DataPacket>,
}
@@ -49,16 +56,17 @@ impl FfiRoom {
.async_runtime
.spawn(data_task(server, room.clone(), data_rx, close_rx));
let ffi_room = Self {
let ffi_room = Arc::new(Self {
room: room.clone(),
event_handle,
data_handle,
close_tx,
handle: Mutex::new(Some(Handle {
event_handle,
data_handle,
close_tx,
})),
data_tx,
};
});
server.ffi_handles().insert(next_id, Box::new(ffi_room));
server.rooms().lock().insert(room.sid(), next_id);
server.ffi_handles.insert(next_id, Box::new(ffi_room));
let room_info = proto::RoomInfo::from_room(next_id, &room);
Ok(room_info)
@@ -92,11 +100,15 @@ impl FfiRoom {
})
}
pub async fn close(self) {
pub async fn close(&self) {
let _ = self.room.close().await;
let _ = self.close_tx.send(());
let _ = self.event_handle.await;
let _ = self.data_handle.await;
let handle = self.handle.lock().take();
if let Some(handle) = handle {
let _ = handle.close_tx.send(());
let _ = handle.event_handle.await;
let _ = handle.data_handle.await;
}
}
pub fn room(&self) -> &Arc<Room> {
@@ -135,23 +147,16 @@ async fn data_task(
async fn room_task(
server: &'static FfiServer,
room: Arc<Room>,
_room: Arc<Room>,
room_handle: FfiHandleId,
mut events: mpsc::UnboundedReceiver<livekit::RoomEvent>,
mut close_rx: broadcast::Receiver<()>,
) {
server
.async_runtime
.spawn(participant_task(Participant::Local(
room.local_participant(),
)));
loop {
tokio::select! {
Some(event) = events.recv() => {
let message = match event {
if let Some(message)= match event {
RoomEvent::ParticipantConnected(participant) => {
server.async_runtime.spawn(participant_task(Participant::Remote(participant.clone())));
Some(proto::room_event::Message::ParticipantConnected(
proto::ParticipantConnected {
info: Some(proto::ParticipantInfo::from(&participant)),
@@ -190,7 +195,7 @@ async fn room_task(
} => {
let handle_id = server.next_id() as FfiHandleId;
let track_info = proto::TrackInfo::from_remote_track(handle_id, &track);
server.ffi_handles().insert(handle_id, Box::new(Track::from(track)));
server.ffi_handles.insert(handle_id, Box::new(Track::from(track)));
Some(proto::room_event::Message::TrackSubscribed(
proto::TrackSubscribed {
@@ -210,12 +215,11 @@ async fn room_task(
},
)),
_ => None
};
if message.is_some() {
} {
// Send the event to the FfiClient
let _ = server.send_event(proto::ffi_event::Message::RoomEvent(proto::RoomEvent{
room_handle: Some(room_handle.into()),
message
message: Some(message)
}));
}
@@ -226,10 +230,3 @@ async fn room_task(
};
}
}
async fn participant_task(participant: Participant) {
let mut participant_events = participant.register_observer();
while let Some(_event) = participant_events.recv().await {
// TODO(theomonnom): convert event to proto
}
}
+1 -1
View File
@@ -109,7 +109,7 @@ impl TestScope {
impl Drop for TestScope {
fn drop(&mut self) {
// At the end of a test, no more handle should exist
assert!(server::FFI_SERVER.ffi_handles().is_empty());
assert!(server::FFI_SERVER.ffi_handles.is_empty());
}
}
+5 -5
View File
@@ -39,7 +39,7 @@ impl FfiVideoStream {
.id as FfiHandleId;
let track = server
.ffi_handles()
.ffi_handles
.get(&handle_id)
.ok_or(FfiError::InvalidRequest("track not found"))?;
@@ -75,7 +75,7 @@ impl FfiVideoStream {
// Store the new video stream and return the info
let info = proto::VideoStreamInfo::from(&stream);
server
.ffi_handles()
.ffi_handles
.insert(stream.handle_id, Box::new(stream));
Ok(info)
@@ -110,7 +110,7 @@ impl FfiVideoStream {
let buffer_info = proto::VideoFrameBufferInfo::from(handle_id, &frame.buffer);
server
.ffi_handles()
.ffi_handles
.insert(handle_id, Box::new(frame.buffer));
if let Err(err) = server.send_event(proto::ffi_event::Message::VideoStreamEvent(
@@ -167,7 +167,7 @@ impl FfiVideoSource {
let source_info = proto::VideoSourceInfo::from(&video_source);
server
.ffi_handles()
.ffi_handles
.insert(video_source.handle_id, Box::new(video_source));
Ok(source_info)
@@ -191,7 +191,7 @@ impl FfiVideoSource {
.id as FfiHandleId;
let buffer = server
.ffi_handles()
.ffi_handles
.get(&buffer_handle)
.ok_or(FfiError::InvalidRequest("handle not found"))?;