-
Notifications
You must be signed in to change notification settings - Fork 353
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
Conversation
Planning to open a minor clean-up PR to remove some unused variable here before getting back to this. @rustbot label S-blocked |
☔ The latest upstream changes (presumably #4074) made this pull request unmergeable. Please resolve the merge conflicts. |
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") | ||
}; |
There was a problem hiding this comment.
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.
@rustbot ready |
There was a problem hiding this 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
// 0 bytes successfully read indicates end-of-file. | ||
return ecx.return_read_success(ptr, &[], 0, &dest); | ||
} else { | ||
// Blocking socketpair with writer and empty buffer. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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. :)
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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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.
let available_space = | ||
MAX_SOCKETPAIR_BUFFER_CAPACITY.strict_sub(writebuf.borrow().buf.len()); |
There was a problem hiding this comment.
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).
let peer_anonsocket = peer_fd.downcast::<AnonSocket>().unwrap(); | ||
// Unblock all threads that are currently blocked on peer_fd's read. |
There was a problem hiding this comment.
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.
let peer_anonsocket = peer_fd.downcast::<AnonSocket>().unwrap(); | ||
// Unblock all threads that are currently blocked on peer_fd's write. |
There was a problem hiding this comment.
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.
Support blocking for socketpair and pipe.
closes #3665