fix(peer): coalesce full UI view publications
Keep one queued and one replaceable pending snapshot for remote-library and Call-to-Play views while preserving lifecycle-event FIFO delivery. Generation barriers and fenced drains prevent stale nonempty views from crossing disable or acknowledgement boundaries. Test Plan: - just test - just clippy - focused burst, lifecycle ordering, fence, repeated-barrier, and stale-view tests - independent ordering review - git diff --check
This commit is contained in:
1 parent
470e119515
commit
37420e26ed
17 files changed
+828
-251
No files matched your search
@@ -35,6 +35,8 @@ use lanspread_peer::{
|
||||
NoopStreamInstallProvider,
|
||||
PeerCommand,
|
||||
PeerEvent,
|
||||
PeerEventReceiver,
|
||||
PeerEventSender,
|
||||
PeerGameDB,
|
||||
PeerIdentity,
|
||||
PeerIdentityDurability,
|
||||
@@ -47,6 +49,7 @@ use lanspread_peer::{
|
||||
UnpackFuture,
|
||||
Unpacker,
|
||||
migrate_legacy_state,
|
||||
peer_event_channel,
|
||||
scoped_blocking,
|
||||
start_peer_with_options,
|
||||
};
|
||||
@@ -687,7 +690,7 @@ struct InstallSettings {
|
||||
language: String,
|
||||
}
|
||||
|
||||
struct PeerEventTx(UnboundedSender<PeerEvent>);
|
||||
struct PeerEventTx(PeerEventSender);
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize)]
|
||||
enum UiOperationKind {
|
||||
@@ -4086,7 +4089,7 @@ fn emit_game_id_event(app_handle: &AppHandle, event: &str, id: &str, label: &str
|
||||
|
||||
fn spawn_peer_event_loop(
|
||||
app_handle: AppHandle,
|
||||
mut rx_peer_event: UnboundedReceiver<PeerEvent>,
|
||||
mut rx_peer_event: PeerEventReceiver,
|
||||
mut rx_ui_state: UnboundedReceiver<UiStateCommand>,
|
||||
) {
|
||||
let tasks = app_handle
|
||||
@@ -4228,6 +4231,7 @@ fn set_identity_diagnostic_in_loop(
|
||||
Ok(next)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn take_exactly_queued<T>(
|
||||
receiver: &mut UnboundedReceiver<T>,
|
||||
count: usize,
|
||||
@@ -4243,15 +4247,17 @@ fn take_exactly_queued<T>(
|
||||
|
||||
async fn drain_queued_peer_events(
|
||||
app_handle: &AppHandle,
|
||||
receiver: &mut UnboundedReceiver<PeerEvent>,
|
||||
receiver: &mut PeerEventReceiver,
|
||||
) -> Result<(), String> {
|
||||
// The core replies only after all events for the requested transition have
|
||||
// been enqueued. Once this UI command wins the fair select, snapshot the
|
||||
// peer queue and process that exact FIFO prefix. Later autonomous traffic
|
||||
// remains for the normal event-loop turn and cannot contaminate this
|
||||
// transition's acknowledgement boundary.
|
||||
let count = receiver.len();
|
||||
let queued = take_exactly_queued(receiver, count)?;
|
||||
// been enqueued. Once this UI command wins the fair select, hold the shared
|
||||
// producer fence while draining every event already admitted, including a
|
||||
// coalesced pending view. Later autonomous traffic remains for the normal
|
||||
// event-loop turn and cannot contaminate this transition's acknowledgement
|
||||
// boundary.
|
||||
let queued = receiver
|
||||
.drain_ready()
|
||||
.map_err(|error| format!("peer-event queue changed while applying its fence: {error}"))?;
|
||||
for event in queued {
|
||||
handle_peer_event(app_handle, event).await;
|
||||
}
|
||||
@@ -4260,7 +4266,7 @@ async fn drain_queued_peer_events(
|
||||
|
||||
async fn fence_queued_peer_events(
|
||||
app_handle: &AppHandle,
|
||||
receiver: &mut UnboundedReceiver<PeerEvent>,
|
||||
receiver: &mut PeerEventReceiver,
|
||||
) -> Result<LocalNetworkSharingSnapshot, String> {
|
||||
drain_queued_peer_events(app_handle, receiver).await?;
|
||||
current_local_network_sharing(app_handle.state::<LanSpreadState>().inner())
|
||||
@@ -4268,7 +4274,7 @@ async fn fence_queued_peer_events(
|
||||
|
||||
async fn reset_game_transfer_status_in_loop(
|
||||
app_handle: &AppHandle,
|
||||
receiver: &mut UnboundedReceiver<PeerEvent>,
|
||||
receiver: &mut PeerEventReceiver,
|
||||
) -> Result<GameTransferStatusSnapshot, String> {
|
||||
// A successful game-root command has already caused core to enqueue its
|
||||
// preceding events. Drain that bounded prefix before terminalizing the old
|
||||
@@ -4290,7 +4296,7 @@ async fn reset_game_transfer_status_in_loop(
|
||||
async fn handle_ui_state_command(
|
||||
app_handle: &AppHandle,
|
||||
command: UiStateCommand,
|
||||
peer_events: &mut UnboundedReceiver<PeerEvent>,
|
||||
peer_events: &mut PeerEventReceiver,
|
||||
) {
|
||||
match command {
|
||||
UiStateCommand::MutateSharing { mutation, reply } => {
|
||||
@@ -4629,7 +4635,7 @@ struct ProtocolMismatchSnapshot {
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
// channel to receive events from the peer
|
||||
let (tx_peer_event, rx_peer_event) = tokio::sync::mpsc::unbounded_channel::<PeerEvent>();
|
||||
let (tx_peer_event, rx_peer_event) = peer_event_channel();
|
||||
let (tx_ui_state, rx_ui_state) = tokio::sync::mpsc::unbounded_channel::<UiStateCommand>();
|
||||
|
||||
tauri::Builder::default()
|
||||
|
||||
Reference in new issue
Block a user