Driver: Split receive into its own feature (#141)
Adds the "receive" feature, which is disabled by default. When this is disabled, the UDP receive task is not compiled and not run, and as an optimisation the UDP receive buffer size is set to 0. All related events are also removed. This also removes the UDP Tx task, and moves packet and keepalive sends back into the mixer thread. This allows us to entirely remove channels and various allocations between the mixer and an async task created only for sending data (i.e., fewer memcopies). If "receive" is enabled, UDP sends are now non-blocking due to technical constraints -- failure to send is non-fatal, but *will* drop affected packets. Given that blocking on a UDP send indicates that the OS cannot clear send buffers fast enough, this should alleviate OS load. Closes #131.
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
pub mod error;
|
||||
|
||||
#[cfg(feature = "receive")]
|
||||
use super::tasks::udp_rx;
|
||||
use super::{
|
||||
tasks::{message::*, udp_rx, udp_tx, ws as ws_task},
|
||||
tasks::{message::*, ws as ws_task},
|
||||
Config,
|
||||
CryptoMode,
|
||||
};
|
||||
@@ -18,7 +20,8 @@ use crate::{
|
||||
use discortp::discord::{IpDiscoveryPacket, IpDiscoveryType, MutableIpDiscoveryPacket};
|
||||
use error::{Error, Result};
|
||||
use flume::Sender;
|
||||
use std::{net::IpAddr, str::FromStr, sync::Arc};
|
||||
use socket2::Socket;
|
||||
use std::{net::IpAddr, str::FromStr};
|
||||
use tokio::{net::UdpSocket, spawn, time::timeout};
|
||||
use tracing::{debug, info, instrument};
|
||||
use url::Url;
|
||||
@@ -103,6 +106,16 @@ impl Connection {
|
||||
}
|
||||
|
||||
let udp = UdpSocket::bind("0.0.0.0:0").await?;
|
||||
|
||||
// Optimisation for non-receive case: set rx buffer size to zero.
|
||||
let udp = if cfg!(feature = "receive") {
|
||||
udp
|
||||
} else {
|
||||
let socket = Socket::from(udp.into_std()?);
|
||||
socket.set_recv_buffer_size(0)?;
|
||||
UdpSocket::from_std(socket.into())?
|
||||
};
|
||||
|
||||
udp.connect((ready.ip, ready.port)).await?;
|
||||
|
||||
// Follow Discord's IP Discovery procedures, in case NAT tunnelling is needed.
|
||||
@@ -164,22 +177,36 @@ impl Connection {
|
||||
info!("WS heartbeat duration {}ms.", hello.heartbeat_interval,);
|
||||
|
||||
let (ws_msg_tx, ws_msg_rx) = flume::unbounded();
|
||||
let (udp_sender_msg_tx, udp_sender_msg_rx) = flume::unbounded();
|
||||
#[cfg(feature = "receive")]
|
||||
let (udp_receiver_msg_tx, udp_receiver_msg_rx) = flume::unbounded();
|
||||
|
||||
// NOTE: This causes the UDP Socket on "receive" to be non-blocking,
|
||||
// and the standard to be blocking. A UDP send should only WouldBlock if
|
||||
// you're sending more data than the OS can handle (not likely, and
|
||||
// at that point you should scale horizontally).
|
||||
//
|
||||
// If this is a problem for anyone, we can make non-blocking sends
|
||||
// queue up a delayed send up to a limit.
|
||||
#[cfg(feature = "receive")]
|
||||
let (udp_rx, udp_tx) = {
|
||||
let udp_rx = Arc::new(udp);
|
||||
let udp_tx = Arc::clone(&udp_rx);
|
||||
let udp_tx = udp.into_std()?;
|
||||
let udp_rx = UdpSocket::from_std(udp_tx.try_clone()?)?;
|
||||
(udp_rx, udp_tx)
|
||||
};
|
||||
#[cfg(not(feature = "receive"))]
|
||||
let udp_tx = udp.into_std()?;
|
||||
|
||||
let ssrc = ready.ssrc;
|
||||
|
||||
let mix_conn = MixerConnection {
|
||||
#[cfg(feature = "receive")]
|
||||
cipher: cipher.clone(),
|
||||
#[cfg(not(feature = "receive"))]
|
||||
cipher,
|
||||
crypto_state: config.crypto_mode.into(),
|
||||
#[cfg(feature = "receive")]
|
||||
udp_rx: udp_receiver_msg_tx,
|
||||
udp_tx: udp_sender_msg_tx,
|
||||
udp_tx,
|
||||
};
|
||||
|
||||
interconnect
|
||||
@@ -200,6 +227,7 @@ impl Connection {
|
||||
info.clone(),
|
||||
));
|
||||
|
||||
#[cfg(feature = "receive")]
|
||||
spawn(udp_rx::runner(
|
||||
interconnect.clone(),
|
||||
udp_receiver_msg_rx,
|
||||
@@ -207,7 +235,6 @@ impl Connection {
|
||||
config.clone(),
|
||||
udp_rx,
|
||||
));
|
||||
spawn(udp_tx::runner(udp_sender_msg_rx, ssrc, udp_tx));
|
||||
|
||||
Ok(Connection {
|
||||
info,
|
||||
|
||||
Reference in New Issue
Block a user