mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
MessagePort: implement disentanglement (#36654)
Implement [disentangle](https://html.spec.whatwg.org/multipage/#disentangle) Remove bespoke gc logic which now becomes unnecessary. Adds a wpt test that hits the "disentangle while in transfer" logic. Updates streams code, fixing an error where disentanglement is conditional on an error. Test coverage: there are existing tests in `/webmessaging/message-channels/close-event/explicitly-closed.tentative.window.js` for the no transfer case, and the simple completed transfer case, and this PR adds a test for the more complicated transfer in progress case. Fix https://github.com/servo/servo/issues/36465 --------- Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
This commit is contained in:
parent
c46402e222
commit
af5d665efa
19 changed files with 356 additions and 191 deletions
|
@ -115,9 +115,10 @@ use constellation_traits::{
|
|||
AuxiliaryWebViewCreationRequest, AuxiliaryWebViewCreationResponse, BroadcastMsg, DocumentState,
|
||||
EmbedderToConstellationMessage, IFrameLoadInfo, IFrameLoadInfoWithData, IFrameSandboxState,
|
||||
IFrameSizeMsg, Job, LoadData, LoadOrigin, LogEntry, MessagePortMsg, NavigationHistoryBehavior,
|
||||
PaintMetricEvent, PortMessageTask, SWManagerMsg, SWManagerSenders, ScriptToConstellationChan,
|
||||
ScriptToConstellationMessage, ScrollState, ServiceWorkerManagerFactory, ServiceWorkerMsg,
|
||||
StructuredSerializedData, TraversalDirection, WindowSizeType,
|
||||
PaintMetricEvent, PortMessageTask, PortTransferInfo, SWManagerMsg, SWManagerSenders,
|
||||
ScriptToConstellationChan, ScriptToConstellationMessage, ScrollState,
|
||||
ServiceWorkerManagerFactory, ServiceWorkerMsg, StructuredSerializedData, TraversalDirection,
|
||||
WindowSizeType,
|
||||
};
|
||||
use crossbeam_channel::{Receiver, Select, Sender, unbounded};
|
||||
use devtools_traits::{
|
||||
|
@ -202,9 +203,6 @@ enum TransferState {
|
|||
/// While a completion failed, another global requested to complete the transfer.
|
||||
/// We are still buffering messages, and awaiting the return of the buffer from the global who failed.
|
||||
CompletionRequested(MessagePortRouterId, VecDeque<PortMessageTask>),
|
||||
/// The entangled port has been removed while the port was in-transfer,
|
||||
/// the current port should be removed as well once it is managed again.
|
||||
EntangledRemoved,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -1524,12 +1522,12 @@ where
|
|||
ScriptToConstellationMessage::NewMessagePort(router_id, port_id) => {
|
||||
self.handle_new_messageport(router_id, port_id);
|
||||
},
|
||||
ScriptToConstellationMessage::RemoveMessagePort(port_id) => {
|
||||
self.handle_remove_messageport(port_id);
|
||||
},
|
||||
ScriptToConstellationMessage::EntanglePorts(port1, port2) => {
|
||||
self.handle_entangle_messageports(port1, port2);
|
||||
},
|
||||
ScriptToConstellationMessage::DisentanglePorts(port1, port2) => {
|
||||
self.handle_disentangle_messageports(port1, port2);
|
||||
},
|
||||
ScriptToConstellationMessage::NewBroadcastChannelRouter(
|
||||
router_id,
|
||||
response_sender,
|
||||
|
@ -2117,17 +2115,6 @@ where
|
|||
Entry::Occupied(entry) => entry,
|
||||
};
|
||||
match entry.get().state {
|
||||
TransferState::EntangledRemoved => {
|
||||
// If the entangled port has been removed while this one was in-transfer,
|
||||
// remove it now.
|
||||
if let Some(ipc_sender) = self.message_port_routers.get(&router_id) {
|
||||
let _ = ipc_sender.send(MessagePortMsg::RemoveMessagePort(port_id));
|
||||
} else {
|
||||
warn!("No message-port sender for {:?}", router_id);
|
||||
}
|
||||
entry.remove_entry();
|
||||
continue;
|
||||
},
|
||||
TransferState::CompletionInProgress(expected_router_id) => {
|
||||
// Here, the transfer was normally completed.
|
||||
|
||||
|
@ -2151,9 +2138,9 @@ where
|
|||
|
||||
fn handle_message_port_transfer_failed(
|
||||
&mut self,
|
||||
ports: HashMap<MessagePortId, VecDeque<PortMessageTask>>,
|
||||
ports: HashMap<MessagePortId, PortTransferInfo>,
|
||||
) {
|
||||
for (port_id, mut previous_buffer) in ports.into_iter() {
|
||||
for (port_id, mut transfer_info) in ports.into_iter() {
|
||||
let entry = match self.message_ports.remove(&port_id) {
|
||||
None => {
|
||||
warn!(
|
||||
|
@ -2165,11 +2152,6 @@ where
|
|||
Some(entry) => entry,
|
||||
};
|
||||
let new_info = match entry.state {
|
||||
TransferState::EntangledRemoved => {
|
||||
// If the entangled port has been removed while this one was in-transfer,
|
||||
// just drop it.
|
||||
continue;
|
||||
},
|
||||
TransferState::CompletionFailed(mut current_buffer) => {
|
||||
// The transfer failed,
|
||||
// and now the global has returned us the buffer we previously sent.
|
||||
|
@ -2177,7 +2159,7 @@ where
|
|||
|
||||
// Tasks in the previous buffer are older,
|
||||
// hence need to be added to the front of the current one.
|
||||
while let Some(task) = previous_buffer.pop_back() {
|
||||
while let Some(task) = transfer_info.port_message_queue.pop_back() {
|
||||
current_buffer.push_front(task);
|
||||
}
|
||||
// Update the state to transfer-in-progress.
|
||||
|
@ -2196,7 +2178,7 @@ where
|
|||
|
||||
// Tasks in the previous buffer are older,
|
||||
// hence need to be added to the front of the current one.
|
||||
while let Some(task) = previous_buffer.pop_back() {
|
||||
while let Some(task) = transfer_info.port_message_queue.pop_back() {
|
||||
current_buffer.push_front(task);
|
||||
}
|
||||
// Forward the buffered message-queue to complete the current transfer.
|
||||
|
@ -2204,7 +2186,10 @@ where
|
|||
if ipc_sender
|
||||
.send(MessagePortMsg::CompletePendingTransfer(
|
||||
port_id,
|
||||
current_buffer,
|
||||
PortTransferInfo {
|
||||
port_message_queue: current_buffer,
|
||||
disentangled: entry.entangled_with.is_none(),
|
||||
},
|
||||
))
|
||||
.is_err()
|
||||
{
|
||||
|
@ -2251,18 +2236,14 @@ where
|
|||
Some(entry) => entry,
|
||||
};
|
||||
let new_info = match entry.state {
|
||||
TransferState::EntangledRemoved => {
|
||||
// If the entangled port has been removed while this one was in-transfer,
|
||||
// remove it now.
|
||||
if let Some(ipc_sender) = self.message_port_routers.get(&router_id) {
|
||||
let _ = ipc_sender.send(MessagePortMsg::RemoveMessagePort(port_id));
|
||||
} else {
|
||||
warn!("No message-port sender for {:?}", router_id);
|
||||
}
|
||||
continue;
|
||||
},
|
||||
TransferState::TransferInProgress(buffer) => {
|
||||
response.insert(port_id, buffer);
|
||||
response.insert(
|
||||
port_id,
|
||||
PortTransferInfo {
|
||||
port_message_queue: buffer,
|
||||
disentangled: entry.entangled_with.is_none(),
|
||||
},
|
||||
);
|
||||
|
||||
// If the port was in transfer, and a global is requesting completion,
|
||||
// we note the start of the completion.
|
||||
|
@ -2341,10 +2322,6 @@ where
|
|||
TransferState::TransferInProgress(queue) => queue.push_back(task),
|
||||
TransferState::CompletionFailed(queue) => queue.push_back(task),
|
||||
TransferState::CompletionRequested(_, queue) => queue.push_back(task),
|
||||
TransferState::EntangledRemoved => warn!(
|
||||
"Messageport received a message, but entangled has alread been removed {:?}",
|
||||
port_id
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2406,59 +2383,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace")
|
||||
)]
|
||||
fn handle_remove_messageport(&mut self, port_id: MessagePortId) {
|
||||
let entangled = match self.message_ports.remove(&port_id) {
|
||||
Some(info) => info.entangled_with,
|
||||
None => {
|
||||
return warn!(
|
||||
"Constellation asked to remove unknown messageport {:?}",
|
||||
port_id
|
||||
);
|
||||
},
|
||||
};
|
||||
let entangled_id = match entangled {
|
||||
Some(id) => id,
|
||||
None => return,
|
||||
};
|
||||
let info = match self.message_ports.get_mut(&entangled_id) {
|
||||
Some(info) => info,
|
||||
None => {
|
||||
return warn!(
|
||||
"Constellation asked to remove unknown entangled messageport {:?}",
|
||||
entangled_id
|
||||
);
|
||||
},
|
||||
};
|
||||
let router_id = match info.state {
|
||||
TransferState::EntangledRemoved => {
|
||||
return warn!(
|
||||
"Constellation asked to remove entangled messageport by a port that was already removed {:?}",
|
||||
port_id
|
||||
);
|
||||
},
|
||||
TransferState::TransferInProgress(_) |
|
||||
TransferState::CompletionInProgress(_) |
|
||||
TransferState::CompletionFailed(_) |
|
||||
TransferState::CompletionRequested(_, _) => {
|
||||
// Note: since the port is in-transer, we don't have a router to send it a message
|
||||
// to let it know that its entangled port has been removed.
|
||||
// Hence we mark it so that it will be messaged and removed once the transfer completes.
|
||||
info.state = TransferState::EntangledRemoved;
|
||||
return;
|
||||
},
|
||||
TransferState::Managed(router_id) => router_id,
|
||||
};
|
||||
if let Some(sender) = self.message_port_routers.get(&router_id) {
|
||||
let _ = sender.send(MessagePortMsg::RemoveMessagePort(entangled_id));
|
||||
} else {
|
||||
warn!("No message-port sender for {:?}", router_id);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace")
|
||||
|
@ -2482,6 +2406,57 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "tracing",
|
||||
tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace")
|
||||
)]
|
||||
/// <https://html.spec.whatwg.org/multipage/#disentangle>
|
||||
fn handle_disentangle_messageports(
|
||||
&mut self,
|
||||
port1: MessagePortId,
|
||||
port2: Option<MessagePortId>,
|
||||
) {
|
||||
// Disentangle initiatorPort and otherPort,
|
||||
// so that they are no longer entangled or associated with each other.
|
||||
// Note: If `port2` is some, then this is the first message
|
||||
// and `port1` is the initiatorPort, `port2` is the otherPort.
|
||||
// We can immediately remove the initiator.
|
||||
let _ = self.message_ports.remove(&port1);
|
||||
|
||||
// Note: the none case is when otherPort sent this message
|
||||
// in response to completing its own local disentanglement.
|
||||
let Some(port2) = port2 else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Start disentanglement of the other port.
|
||||
if let Some(info) = self.message_ports.get_mut(&port2) {
|
||||
info.entangled_with = None;
|
||||
match &mut info.state {
|
||||
TransferState::Managed(router_id) |
|
||||
TransferState::CompletionInProgress(router_id) => {
|
||||
// We try to disentangle the other port now,
|
||||
// and if it has been transfered out by the time the message is received,
|
||||
// it will be ignored,
|
||||
// and disentanglement will be completed as part of the transfer.
|
||||
if let Some(ipc_sender) = self.message_port_routers.get(router_id) {
|
||||
let _ = ipc_sender.send(MessagePortMsg::CompleteDisentanglement(port2));
|
||||
} else {
|
||||
warn!("No message-port sender for {:?}", router_id);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// Note: the port is in transfer, disentanglement will complete along with it.
|
||||
},
|
||||
}
|
||||
} else {
|
||||
warn!(
|
||||
"Constellation asked to disentangle unknown messageport: {:?}",
|
||||
port2
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://w3c.github.io/ServiceWorker/#schedule-job-algorithm>
|
||||
/// and
|
||||
/// <https://w3c.github.io/ServiceWorker/#dfn-job-queue>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue