Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement blocking unnamed_socket #4072

Merged
merged 1 commit into from
Dec 18, 2024
Merged

Implement blocking unnamed_socket #4072

merged 1 commit into from
Dec 18, 2024

Conversation

tiif
Copy link
Contributor

@tiif tiif commented Dec 4, 2024

Support blocking for socketpair and pipe.

closes #3665

@rustbot rustbot added the S-waiting-on-author Status: Waiting for the PR author to address review comments label Dec 4, 2024
@tiif
Copy link
Contributor Author

tiif commented Dec 4, 2024

Planning to open a minor clean-up PR to remove some unused variable here before getting back to this.

@rustbot label S-blocked

@rustbot rustbot added the S-blocked Status: blocked on something happening somewhere else label Dec 4, 2024
@bors
Copy link
Contributor

bors commented Dec 5, 2024

☔ The latest upstream changes (presumably #4074) made this pull request unmergeable. Please resolve the merge conflicts.

@tiif tiif changed the title [WIP] blocking unnamed_socket Implement blocking unnamed_socket Dec 16, 2024
Comment on lines +172 to +173
let Some(self_ref) = weak_self_ref.upgrade() else {
// FIXME: We should raise a deadlock error if the self_ref upgrade failed.
throw_unsup_format!("This will be a deadlock error in future")
};
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this PR is big enough, so I decided to temporarily make this throws unsupported format.

I wanted to write a test for this, and potentially tweak the diagnostic a bit if it is too confusing (because of the reason stated here).

But I certainly won't mind putting it in this PR if anyone prefers.

@tiif
Copy link
Contributor Author

tiif commented Dec 16, 2024

@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: Waiting for a review to complete and removed S-waiting-on-author Status: Waiting for the PR author to address review comments S-blocked Status: blocked on something happening somewhere else labels Dec 16, 2024
@tiif tiif marked this pull request as ready for review December 16, 2024 14:22
Copy link
Contributor

@oli-obk oli-obk left a comment

Choose a reason for hiding this comment

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

just some nits, then this is ready, please apply and squash

src/shims/unix/unnamed_socket.rs Outdated Show resolved Hide resolved
src/shims/unix/unnamed_socket.rs Outdated Show resolved Hide resolved
src/shims/unix/unnamed_socket.rs Outdated Show resolved Hide resolved
src/shims/unix/unnamed_socket.rs Outdated Show resolved Hide resolved
src/shims/unix/unnamed_socket.rs Outdated Show resolved Hide resolved
src/shims/unix/unnamed_socket.rs Outdated Show resolved Hide resolved
@oli-obk oli-obk added this pull request to the merge queue Dec 18, 2024
Merged via the queue into rust-lang:master with commit c9c28f4 Dec 18, 2024
7 checks passed
@tiif tiif deleted the blockop branch December 19, 2024 09:06
// 0 bytes successfully read indicates end-of-file.
return ecx.return_read_success(ptr, &[], 0, &dest);
} else {
// Blocking socketpair with writer and empty buffer.
Copy link
Member

Choose a reason for hiding this comment

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

How do we know it is blocking?

If that's a precondition, please add an assert! further up this function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh yea, I should add a check here. The general idea is in #4072 (comment), since readbuf.borrow().buf.is_empty() && self.is_nonblock is already handled in read, this is always blocking. It is also impossible to change an fd from blocking type to non-blocking type for now. But it is likely that I will move the checks around, I will add it if it is still necessary.

return ecx.return_read_success(ptr, &[], 0, &dest);
} else {
// Blocking socketpair with writer and empty buffer.
let weak_self_ref = weak_self_ref.clone();
Copy link
Member

Choose a reason for hiding this comment

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

Why do you have to clone this reference? Seems like you should move it.

}
}

if readbuf.borrow().buf.is_empty() && self.is_nonblock {
Copy link
Member

Choose a reason for hiding this comment

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

The separation of concerns between read and anonsocket_read is a bit unclear. Why is this one case handled here and everything else handled below?

I think the code would be easier to follow if you moved this logic into anonsocket_read as well.

Copy link
Contributor Author

@tiif tiif Dec 20, 2024

Choose a reason for hiding this comment

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

My idea is keeping the non-blocking && error logic in read and write. In anonsocket_read and anonsocket_write, we either go through the block_thread path or the actual read/write path.

For now, if readbuf.borrow().buf.is_empty() && self.is_nonblock check will never be hit by blocking fd (the one without SOCK_NONBLOCK flag) after unblocked, so I just kept them in read/write.

It is fine to move it in anonsocket_read too if it is clearer.

Copy link
Member

@RalfJung RalfJung Dec 21, 2024

Choose a reason for hiding this comment

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

Splitting the logic across two functions leads to a very odd (and not documented!) precondition for anonsocket_read. It also means I have to scroll up and down to look at the entire logic. The logic really isn't that complicated so IMO it'd be better to have it in a single function where one can easily see the entire thing at a single glance.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This makes sense, I can combine them together.

Copy link
Member

Choose a reason for hiding this comment

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

Actually the new logic here is just wrong. When the buffer is empty, we have to first check self_anonsocket.peer_fd().upgrade() and then check is_nonblock.

In the future please just keep the logic the way it is rather than trying to break it apart into two functions. :)

Comment on lines +175 to +178
let Some(peer_fd) = self_anonsocket.peer_fd().upgrade() else {
// If the upgrade from Weak to Rc fails, it indicates that all read ends have been
// closed.
return ecx.set_last_error_and_return(ErrorKind::BrokenPipe, &dest);
Copy link
Member

Choose a reason for hiding this comment

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

Is it really necessary to duplicate this check (it is both here and in write above)? I think that should be avoidable, so please reduce the code duplication.

Copy link
Contributor Author

@tiif tiif Dec 20, 2024

Choose a reason for hiding this comment

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

Yea we can always do the check in anonsocket_write, and remove the one in write. This was still in write because I wanted to throw error as early as possible.

// Blocking socketpair with a full buffer.
throw_unsup_format!("socketpair/pipe/pipe2 write: blocking isn't supported yet");
}
if available_space == 0 && self.is_nonblock {
Copy link
Member

Choose a reason for hiding this comment

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

This should also be moved into anonsocket_write I think, again to keep all the logic in the same place.

Comment on lines 148 to 149
let available_space =
MAX_SOCKETPAIR_BUFFER_CAPACITY.strict_sub(writebuf.borrow().buf.len());
Copy link
Member

@RalfJung RalfJung Dec 20, 2024

Choose a reason for hiding this comment

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

Please do not duplicate this logic (that same line now exists twice in this file, always a bad sign).

Comment on lines +224 to +225
let peer_anonsocket = peer_fd.downcast::<AnonSocket>().unwrap();
// Unblock all threads that are currently blocked on peer_fd's read.
Copy link
Member

Choose a reason for hiding this comment

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

The let peer_anonsocket is already part of the "unblock all threads", right? So the comment should be moved up by 1 line.

Comment on lines +306 to +307
let peer_anonsocket = peer_fd.downcast::<AnonSocket>().unwrap();
// Unblock all threads that are currently blocked on peer_fd's write.
Copy link
Member

Choose a reason for hiding this comment

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

The let peer_anonsocket is already part of the "unblock all threads", right? So the comment should be moved up by 1 line.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Waiting for a review to complete
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement blocking support for eventfd and socketpair
5 participants