Skip to content

devtools: wrap TcpStream to synchronize network operations#43472

Merged
eerii merged 6 commits into
servo:mainfrom
brentschroeter:devtools-connection-type
Mar 26, 2026
Merged

devtools: wrap TcpStream to synchronize network operations#43472
eerii merged 6 commits into
servo:mainfrom
brentschroeter:devtools-connection-type

Conversation

@brentschroeter

Copy link
Copy Markdown
Contributor

Introduces a new type DevtoolsConnection, which implements the JsonPacketStream trait with proper coordination of I/O across threads. This replaces the implementation of JsonPacketStream for raw TcpStreams, which was susceptible to interleaving writes when cloned across threads.

DevtoolsConnection also defensively synchronizes read operations. These are less likely to cause issues: in practice actors should never independently pull incoming messages without centralized coordination.

Testing: No new tests, as interleaving writes are difficult to evoke deterministically. Removing the JsonPacketStream implementation for TcpStream should discourage regressions due to improper use of raw streams.

@servo-highfive servo-highfive added the S-awaiting-review There is new code that needs to be reviewed. label Mar 20, 2026
@brentschroeter

Copy link
Copy Markdown
Contributor Author

Related to this discussion: #general > TcpStream::try_clone() thread safety in devtools. If it's helpful, I can copy the details into a formal bug report for posterity.

@brentschroeter

Copy link
Copy Markdown
Contributor Author

A happy side effect is that this removes a big source of flakiness in the existing devtools tests. On my machine with the main branch, ./mach test-devtools fails at a rate of 5 times in 50, compared to 0 times in 50 with this branch. Potentially progress for #39273?

Comment thread components/devtools/actors/worker.rs
Comment thread components/devtools/lib.rs

@eerii eerii left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome change, thank you so much! I like how it looks, I left a few nits / style comments but great code.

I am testing it locally and it seems to have also fixed the flakiness I had on my machine with some devtools tests c:

Comment thread components/devtools/lib.rs Outdated
Comment thread components/devtools/lib.rs
Comment thread components/devtools/lib.rs Outdated
Comment thread components/devtools/protocol.rs Outdated
Comment thread components/devtools/protocol.rs Outdated
Comment thread components/devtools/protocol.rs
Comment thread components/devtools/protocol.rs
Comment thread components/devtools/protocol.rs Outdated
Comment thread components/devtools/protocol.rs Outdated
@servo-highfive servo-highfive removed the S-awaiting-review There is new code that needs to be reviewed. label Mar 24, 2026
Signed-off-by: Brent Schroeter <contact@brentsch.com>
@servo-highfive servo-highfive added the S-needs-rebase There are merge conflict errors. label Mar 25, 2026
brentschroeter and others added 4 commits March 25, 2026 16:11
Signed-off-by: Brent Schroeter <contact@brentsch.com>
Signed-off-by: Brent Schroeter <contact@brentsch.com>
Co-authored-by: eri <eri@igalia.com>
Signed-off-by: Brent Schroeter <contact@brentsch.com>
Signed-off-by: Brent Schroeter <contact@brentsch.com>
@brentschroeter brentschroeter force-pushed the devtools-connection-type branch from abb7679 to 44cf3e9 Compare March 25, 2026 23:18
@servo-highfive servo-highfive added S-awaiting-review There is new code that needs to be reviewed. and removed S-needs-rebase There are merge conflict errors. labels Mar 25, 2026
@brentschroeter

brentschroeter commented Mar 25, 2026

Copy link
Copy Markdown
Contributor Author

@eerii thanks for the suggestions! Updated, and rebased on top of #43623. Only comment I have not addressed in code is the one re: the DevtoolsConnection docstring—#43472 (comment). (edit: now resolved)

Signed-off-by: Brent Schroeter <contact@brentsch.com>
@brentschroeter brentschroeter force-pushed the devtools-connection-type branch from a18a595 to 25cb5c4 Compare March 26, 2026 05:58

@eerii eerii left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you so much, everything looks great! c:

@servo-highfive servo-highfive removed the S-awaiting-review There is new code that needs to be reviewed. label Mar 26, 2026
@eerii eerii added this pull request to the merge queue Mar 26, 2026
@servo-highfive servo-highfive added the S-awaiting-merge The PR is in the process of compiling and running tests on the automated CI. label Mar 26, 2026
Merged via the queue into servo:main with commit c94722d Mar 26, 2026
30 checks passed
@servo-highfive servo-highfive removed the S-awaiting-merge The PR is in the process of compiling and running tests on the automated CI. label Mar 26, 2026
@Skgland

Skgland commented Apr 7, 2026

Copy link
Copy Markdown
Contributor

Testing: [..] Removing the JsonPacketStream implementation for TcpStream should discourage regressions due to improper use of raw streams.

This reads to me like impl JsonPacketStream for TcpStream should have been removed, but it wasn't?

impl JsonPacketStream for TcpStream {
fn write_json_packet<T: Serialize>(&mut self, message: &T) -> Result<(), ActorError> {
let s = serde_json::to_string(message).map_err(|_| ActorError::Internal)?;
debug!("<- {}", s);
write!(self, "{}:{}", s.len(), s).map_err(|_| ActorError::Internal)?;
Ok(())
}
fn read_json_packet(&mut self) -> Result<Option<Value>, String> {
// https://firefox-source-docs.mozilla.org/devtools/backend/protocol.html#stream-transport
// In short, each JSON packet is [ascii length]:[JSON data of given length]
let mut buffer = vec![];
loop {
let mut buf = [0];
let byte = match self.read(&mut buf) {
Ok(0) => return Ok(None), // EOF
Err(e) if e.kind() == ErrorKind::ConnectionReset => return Ok(None), // EOF
Ok(1) => buf[0],
Ok(_) => unreachable!(),
Err(e) => return Err(e.to_string()),
};
match byte {
b':' => {
let packet_len_str = match String::from_utf8(buffer) {
Ok(packet_len) => packet_len,
Err(_) => return Err("nonvalid UTF8 in packet length".to_owned()),
};
let packet_len = match packet_len_str.parse::<u64>() {
Ok(packet_len) => packet_len,
Err(_) => return Err("packet length missing / not parsable".to_owned()),
};
let mut packet = String::new();
self.take(packet_len)
.read_to_string(&mut packet)
.map_err(|e| e.to_string())?;
debug!("{}", packet);
return match serde_json::from_str(&packet) {
Ok(json) => Ok(Some(json)),
Err(err) => Err(err.to_string()),
};
},
c => buffer.push(c),
}
}
}
}

@brentschroeter

Copy link
Copy Markdown
Contributor Author

You're absolutely right. I must have mixed up a merge or something. Thank you for catching it!

Do you want to make a PR to fix? Otherwise I can submit one later today.

Skgland added a commit to Skgland/servo that referenced this pull request Apr 7, 2026
This was supposed to be removed as part of servo#43472 see servo#43472 (comment)

Signed-off-by: Bennet Bleßmann <bennet.blessmann+github@googlemail.com>
Skgland added a commit to Skgland/servo that referenced this pull request Apr 7, 2026
This was supposed to be removed as part of servo#43472 see servo#43472 (comment) and servo#43472 (comment)

Signed-off-by: Bennet Bleßmann <bennet.blessmann+github@googlemail.com>
@Skgland

Skgland commented Apr 7, 2026

Copy link
Copy Markdown
Contributor

Opened PR #44006

github-merge-queue Bot pushed a commit that referenced this pull request Apr 8, 2026
Removes the `impl JsonPacketStream for TcpStream` that was supposed to
be removed as part of #43472

> Testing: [..] Removing the JsonPacketStream implementation for
TcpStream should discourage regressions due to improper use of raw
streams.

Confirmed in
#43472 (comment)

Testing: no new test needed as this only removes code

Signed-off-by: Bennet Bleßmann <bennet.blessmann+github@googlemail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants