mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-24 09:53:24 +00:00
Avoid locking twice in UDP send
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
6a13787c49
commit
418f58ec89
@ -32,8 +32,8 @@ impl BoundDatagram {
|
|||||||
self.bound_socket.local_endpoint().unwrap()
|
self.bound_socket.local_endpoint().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remote_endpoint(&self) -> Option<IpEndpoint> {
|
pub fn remote_endpoint(&self) -> Option<&IpEndpoint> {
|
||||||
self.remote_endpoint
|
self.remote_endpoint.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_remote_endpoint(&mut self, endpoint: &IpEndpoint) {
|
pub fn set_remote_endpoint(&mut self, endpoint: &IpEndpoint) {
|
||||||
|
@ -107,15 +107,6 @@ impl DatagramSocket {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remote_endpoint(&self) -> Option<IpEndpoint> {
|
|
||||||
let inner = self.inner.read();
|
|
||||||
|
|
||||||
match inner.as_ref() {
|
|
||||||
Inner::Bound(bound_datagram) => bound_datagram.remote_endpoint(),
|
|
||||||
Inner::Unbound(_) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_bind_ephemeral(&self, remote_endpoint: &IpEndpoint) -> Result<()> {
|
fn try_bind_ephemeral(&self, remote_endpoint: &IpEndpoint) -> Result<()> {
|
||||||
// Fast path
|
// Fast path
|
||||||
if let Inner::Bound(_) = self.inner.read().as_ref() {
|
if let Inner::Bound(_) = self.inner.read().as_ref() {
|
||||||
@ -138,6 +129,65 @@ impl DatagramSocket {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Selects the remote endpoint and binds if the socket is not bound.
|
||||||
|
///
|
||||||
|
/// The remote endpoint specified in the system call (e.g., `sendto`) argument is preferred,
|
||||||
|
/// otherwise the connected endpoint of the socket is used. If there are no remote endpoints
|
||||||
|
/// available, this method will fail with [`EDESTADDRREQ`].
|
||||||
|
///
|
||||||
|
/// If the remote endpoint is specified but the socket is not bound, this method will try to
|
||||||
|
/// bind the socket to an ephemeral endpoint.
|
||||||
|
///
|
||||||
|
/// If the above steps succeed, `op` will be called with the bound socket and the selected
|
||||||
|
/// remote endpoint.
|
||||||
|
///
|
||||||
|
/// [`EDESTADDRREQ`]: crate::error::Errno::EDESTADDRREQ
|
||||||
|
fn select_remote_and_bind<F, R>(&self, remote: Option<&IpEndpoint>, op: F) -> Result<R>
|
||||||
|
where
|
||||||
|
F: FnOnce(&BoundDatagram, &IpEndpoint) -> Result<R>,
|
||||||
|
{
|
||||||
|
let mut inner = self.inner.read();
|
||||||
|
|
||||||
|
// Not really a loop, since we always break on the first iteration. But we need to use
|
||||||
|
// `loop` here because we want to use `break` later.
|
||||||
|
#[expect(clippy::never_loop)]
|
||||||
|
let bound_datagram = loop {
|
||||||
|
// Fast path: The socket is already bound.
|
||||||
|
if let Inner::Bound(bound_datagram) = inner.as_ref() {
|
||||||
|
break bound_datagram;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slow path: Try to bind the socket to an ephemeral endpoint.
|
||||||
|
drop(inner);
|
||||||
|
if let Some(remote_endpoint) = remote {
|
||||||
|
self.try_bind_ephemeral(remote_endpoint)?;
|
||||||
|
} else {
|
||||||
|
return_errno_with_message!(
|
||||||
|
Errno::EDESTADDRREQ,
|
||||||
|
"the destination address is not specified"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
inner = self.inner.read();
|
||||||
|
|
||||||
|
// Now the socket must be bound.
|
||||||
|
if let Inner::Bound(bound_datagram) = inner.as_ref() {
|
||||||
|
break bound_datagram;
|
||||||
|
}
|
||||||
|
unreachable!("`try_bind_ephemeral` succeeds so the socket cannot be unbound");
|
||||||
|
};
|
||||||
|
|
||||||
|
let remote_endpoint = remote
|
||||||
|
.or_else(|| bound_datagram.remote_endpoint())
|
||||||
|
.ok_or_else(|| {
|
||||||
|
Error::with_message(
|
||||||
|
Errno::EDESTADDRREQ,
|
||||||
|
"the destination address is not specified",
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
op(bound_datagram, remote_endpoint)
|
||||||
|
}
|
||||||
|
|
||||||
fn try_recv(
|
fn try_recv(
|
||||||
&self,
|
&self,
|
||||||
writer: &mut dyn MultiWrite,
|
writer: &mut dyn MultiWrite,
|
||||||
@ -160,19 +210,16 @@ impl DatagramSocket {
|
|||||||
fn try_send(
|
fn try_send(
|
||||||
&self,
|
&self,
|
||||||
reader: &mut dyn MultiRead,
|
reader: &mut dyn MultiRead,
|
||||||
remote: &IpEndpoint,
|
remote: Option<&IpEndpoint>,
|
||||||
flags: SendRecvFlags,
|
flags: SendRecvFlags,
|
||||||
) -> Result<usize> {
|
) -> Result<usize> {
|
||||||
let inner = self.inner.read();
|
let (sent_bytes, iface_to_poll) =
|
||||||
|
self.select_remote_and_bind(remote, |bound_datagram, remote_endpoint| {
|
||||||
|
let sent_bytes = bound_datagram.try_send(reader, remote_endpoint, flags)?;
|
||||||
|
let iface_to_poll = bound_datagram.iface().clone();
|
||||||
|
Ok((sent_bytes, iface_to_poll))
|
||||||
|
})?;
|
||||||
|
|
||||||
let Inner::Bound(bound_datagram) = inner.as_ref() else {
|
|
||||||
return_errno_with_message!(Errno::EAGAIN, "the socket is not bound")
|
|
||||||
};
|
|
||||||
|
|
||||||
let sent_bytes = bound_datagram.try_send(reader, remote, flags)?;
|
|
||||||
let iface_to_poll = bound_datagram.iface().clone();
|
|
||||||
|
|
||||||
drop(inner);
|
|
||||||
self.pollee.invalidate();
|
self.pollee.invalidate();
|
||||||
iface_to_poll.poll();
|
iface_to_poll.poll();
|
||||||
|
|
||||||
@ -250,8 +297,15 @@ impl Socket for DatagramSocket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn peer_addr(&self) -> Result<SocketAddr> {
|
fn peer_addr(&self) -> Result<SocketAddr> {
|
||||||
self.remote_endpoint()
|
let inner = self.inner.read();
|
||||||
.map(|endpoint| endpoint.into())
|
|
||||||
|
let remote_endpoint = match inner.as_ref() {
|
||||||
|
Inner::Unbound(_) => None,
|
||||||
|
Inner::Bound(bound_datagram) => bound_datagram.remote_endpoint(),
|
||||||
|
};
|
||||||
|
|
||||||
|
remote_endpoint
|
||||||
|
.map(|endpoint| (*endpoint).into())
|
||||||
.ok_or_else(|| Error::with_message(Errno::ENOTCONN, "the socket is not connected"))
|
.ok_or_else(|| Error::with_message(Errno::ENOTCONN, "the socket is not connected"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,18 +325,9 @@ impl Socket for DatagramSocket {
|
|||||||
control_message,
|
control_message,
|
||||||
} = message_header;
|
} = message_header;
|
||||||
|
|
||||||
let remote_endpoint = match addr {
|
let endpoint = match addr {
|
||||||
Some(remote_addr) => {
|
Some(addr) => Some(addr.try_into()?),
|
||||||
let endpoint = remote_addr.try_into()?;
|
None => None,
|
||||||
self.try_bind_ephemeral(&endpoint)?;
|
|
||||||
endpoint
|
|
||||||
}
|
|
||||||
None => self.remote_endpoint().ok_or_else(|| {
|
|
||||||
Error::with_message(
|
|
||||||
Errno::EDESTADDRREQ,
|
|
||||||
"the destination address is not specified",
|
|
||||||
)
|
|
||||||
})?,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if control_message.is_some() {
|
if control_message.is_some() {
|
||||||
@ -291,7 +336,7 @@ impl Socket for DatagramSocket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Block if the send buffer is full
|
// TODO: Block if the send buffer is full
|
||||||
self.try_send(reader, &remote_endpoint, flags)
|
self.try_send(reader, endpoint.as_ref(), flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn recvmsg(
|
fn recvmsg(
|
||||||
|
Reference in New Issue
Block a user