#[derive(Clone)]
pub(crate) struct EncryptWorkerPool {
senders: Arc<[WorkerSender]>,
#[cfg(target_os = "linux")]
linux_containers: Arc<LinuxBulkSendFlows>,
#[cfg(target_os = "macos")]
macos_senders: Arc<MacSequencedSendFlows>,
#[cfg(any(target_os = "linux", target_os = "macos"))]
next_worker: Arc<std::sync::atomic::AtomicUsize>,
}
impl EncryptWorkerPool {
pub fn spawn(n: usize) -> Self {
let n = n.max(1);
let worker_channel_cap = worker_channel_cap();
let mut senders = Vec::with_capacity(n);
for i in 0..n {
#[cfg(target_os = "macos")]
{
let (tx, rx) = mac_worker_channel(worker_channel_cap);
std::thread::Builder::new()
.name(format!("fips-encrypt-{i}"))
.spawn(move || run_worker_macos(i, rx))
.expect("failed to spawn fips-encrypt OS thread");
senders.push(tx);
}
#[cfg(not(target_os = "macos"))]
{
let (tx, rx) = fair_worker_channel(
worker_channel_cap.saturating_mul(4).max(1),
worker_channel_cap,
WORKER_FAIR_QUANTUM_BYTES,
);
std::thread::Builder::new()
.name(format!("fips-encrypt-{i}"))
.spawn(move || run_worker(i, rx))
.expect("failed to spawn fips-encrypt OS thread");
senders.push(tx);
}
}
Self {
senders: senders.into(),
#[cfg(target_os = "linux")]
linux_containers: Arc::new(LinuxBulkSendFlows::default()),
#[cfg(target_os = "macos")]
macos_senders: Arc::new(MacSequencedSendFlows::default()),
#[cfg(any(target_os = "linux", target_os = "macos"))]
next_worker: Arc::new(std::sync::atomic::AtomicUsize::new(0)),
}
}
pub fn dispatch(&self, job: FmpSendJob) {
if self.senders.is_empty() {
debug!("EncryptWorkerPool has no workers; dropping job");
return;
}
let (idx, job) = self.prepare_dispatch(job);
self.dispatch_to_worker(idx, job);
}
pub(crate) fn dispatch_bulk_batch(&self, jobs: Vec<FmpSendJob>) {
#[cfg(target_os = "linux")]
if linux_bulk_container_sender_enabled() {
self.dispatch_linux_bulk_containers(jobs);
return;
}
for job in jobs {
self.dispatch(job);
}
}
#[cfg(target_os = "linux")]
fn dispatch_linux_bulk_containers(&self, jobs: Vec<FmpSendJob>) {
let mut run = Vec::new();
let mut run_key: Option<SendTargetKey> = None;
for job in jobs {
if !job.bulk_endpoint_data || job.drop_on_backpressure {
self.dispatch_linux_bulk_container_run(&mut run);
run_key = None;
self.dispatch(job);
continue;
}
let key = job.send_target_key();
if matches!(run_key, Some(current) if current != key) {
self.dispatch_linux_bulk_container_run(&mut run);
run_key = None;
}
if run_key.is_none() {
run_key = Some(key);
}
run.push(job);
}
self.dispatch_linux_bulk_container_run(&mut run);
}
#[cfg(target_os = "linux")]
fn dispatch_linux_bulk_container_run(&self, run: &mut Vec<FmpSendJob>) {
if run.is_empty() {
return;
}
if self.senders.len() <= 1 || run.len() < linux_bulk_container_min_packets() {
for job in run.drain(..) {
self.dispatch(job);
}
return;
}
let target_key = run[0].send_target_key();
debug_assert!(
run.iter().all(|job| job.bulk_endpoint_data
&& !job.drop_on_backpressure
&& job.send_target_key() == target_key),
"Linux bulk container runs must be same-target reliable bulk packets"
);
let flow = self.linux_containers.flow_for(&run[0]);
let container = Arc::new(LinuxBulkSendContainer::new(run.len()));
if !flow.try_enqueue(Arc::clone(&container)) {
crate::perf_profile::record_fmp_linux_bulk_container_queue_full(run.len());
self.dispatch_linux_bulk_container_queue_full_run(run);
return;
}
crate::perf_profile::record_fmp_linux_bulk_container_enqueued(run.len());
let idx = self.select_linux_bulk_container_worker();
for (slot, job) in run.drain(..).enumerate() {
self.dispatch_to_worker(
idx,
QueuedFmpSendJob::linux_container(job, Arc::clone(&container), slot),
);
}
}
#[cfg(target_os = "linux")]
fn dispatch_linux_bulk_container_queue_full_run(&self, run: &mut Vec<FmpSendJob>) {
for job in run.drain(..) {
let (idx, queued) = self.prepare_dispatch(job);
record_encrypt_worker_queue_full(queued.queue_lane());
record_encrypt_worker_backpressure_drop(idx, queued.drop_on_backpressure());
queued.complete_worker_drop();
}
}
#[cfg(target_os = "linux")]
fn select_linux_bulk_container_worker(&self) -> usize {
debug_assert!(!self.senders.is_empty());
let start = self
.next_worker
.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
% self.senders.len();
let mut best = start;
let mut best_len = self.senders[start].queued_len();
for offset in 1..self.senders.len() {
let idx = (start + offset) % self.senders.len();
let queued_len = self.senders[idx].queued_len();
if queued_len < best_len {
best = idx;
best_len = queued_len;
if best_len == 0 {
break;
}
}
}
best
}
#[cfg(target_os = "macos")]
fn prepare_dispatch(&self, job: FmpSendJob) -> (usize, QueuedFmpSendJob) {
if !macos_ordered_sender_enabled() {
use std::hash::{Hash, Hasher};
let queued = QueuedFmpSendJob::direct(job);
let key = queued.target_key();
let mut h = std::collections::hash_map::DefaultHasher::new();
key.hash(&mut h);
let idx = (h.finish() as usize) % self.senders.len();
return (idx, queued);
}
let flow = self.macos_senders.flow_for(&job);
let ticket = self
.next_worker
.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
/ macos_worker_stride();
let idx = ticket % self.senders.len();
(idx, QueuedFmpSendJob::macos_sequenced(job, flow))
}
#[cfg(not(target_os = "macos"))]
fn prepare_dispatch(&self, job: FmpSendJob) -> (usize, QueuedFmpSendJob) {
let queued = QueuedFmpSendJob::direct(job);
let idx = (send_dispatch_fast_hash(&queued.dispatch_key()) as usize) % self.senders.len();
(idx, queued)
}
#[cfg(target_os = "macos")]
fn dispatch_to_worker(&self, idx: usize, job: QueuedFmpSendJob) {
match self.senders[idx].try_push(job) {
Ok(()) => {}
Err(MacWorkerTryPushError::Full(job)) => {
record_encrypt_worker_queue_full(job.queue_lane());
if job.queue_lane() == EncryptWorkerLane::Bulk
&& (job.drop_on_backpressure()
|| (job.bulk_endpoint_data()
&& macos_reliable_bulk_drop_on_worker_full_enabled()))
{
record_encrypt_worker_backpressure_drop(idx, job.drop_on_backpressure());
job.complete_worker_drop();
return;
}
static FULL_COUNT: std::sync::atomic::AtomicU64 =
std::sync::atomic::AtomicU64::new(0);
let n = FULL_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
if n < 8 || n.is_multiple_of(10000) {
warn!(
worker = idx,
full_events = n + 1,
"EncryptWorker channel full; applying outbound backpressure"
);
}
if let Err(MacWorkerPushError(job)) = self.senders[idx].push_blocking(*job) {
job.complete_worker_drop();
debug!(worker = idx, "EncryptWorker thread gone; dropping job");
}
}
Err(MacWorkerTryPushError::Closed(job)) => {
job.complete_worker_drop();
debug!(worker = idx, "EncryptWorker thread gone; dropping job");
}
}
}
#[cfg(not(target_os = "macos"))]
fn dispatch_to_worker(&self, idx: usize, job: QueuedFmpSendJob) {
let sender = &self.senders[idx];
match sender.try_push(job) {
Ok(()) => {}
Err(FairWorkerTryPushError::Full(job)) => {
record_encrypt_worker_queue_full(job.queue_lane());
if job.queue_lane() == EncryptWorkerLane::Bulk {
record_encrypt_worker_backpressure_drop(idx, job.drop_on_backpressure());
job.complete_worker_drop();
return;
}
static FULL_COUNT: std::sync::atomic::AtomicU64 =
std::sync::atomic::AtomicU64::new(0);
let n = FULL_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
if n < 8 || n.is_multiple_of(10000) {
warn!(
worker = idx,
full_events = n + 1,
"EncryptWorker channel full; applying outbound backpressure"
);
}
if let Err(FairWorkerPushError(job)) = sender.push_blocking(*job) {
job.complete_worker_drop();
debug!(worker = idx, "EncryptWorker thread gone; dropping job");
}
}
Err(FairWorkerTryPushError::Closed(job)) => {
job.complete_worker_drop();
debug!(worker = idx, "EncryptWorker thread gone; dropping job");
}
}
}
}
fn record_encrypt_worker_queue_full(lane: EncryptWorkerLane) {
crate::perf_profile::record_encrypt_worker_queue_full(lane == EncryptWorkerLane::Priority);
}
fn record_encrypt_worker_backpressure_drop(worker: usize, drop_on_backpressure: bool) {
crate::perf_profile::record_event(crate::perf_profile::Event::EncryptWorkerBulkDropped);
crate::perf_profile::record_event(if drop_on_backpressure {
crate::perf_profile::Event::EncryptWorkerDiscardableBulkDropped
} else {
crate::perf_profile::Event::EncryptWorkerReliableBulkDropped
});
static DROP_COUNT: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
let n = DROP_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
if n < 8 || n.is_multiple_of(100_000) {
warn!(
worker,
drops = n + 1,
"EncryptWorker channel full; dropping bulk data packet"
);
}
}
#[cfg(target_os = "macos")]
type MacSendFlowKey = SendTargetKey;
#[cfg(target_os = "macos")]
#[derive(Default)]
struct MacSequencedSendFlows {
flows: Mutex<HashMap<MacSendFlowKey, Arc<MacSequencedSendFlow>>>,
last_prune_ms: std::sync::atomic::AtomicU64,
}
#[cfg(target_os = "macos")]
impl MacSequencedSendFlows {
fn flow_for(&self, job: &FmpSendJob) -> Arc<MacSequencedSendFlow> {
let now_ms = mac_now_ms();
let key = job.send_target_key();
let mut flows = self.flows.lock().expect("mac send flow map poisoned");
self.prune_idle_locked(&mut flows, now_ms);
if let Some(flow) = flows.get(&key) {
flow.mark_used(now_ms);
return Arc::clone(flow);
}
let flow = MacSequencedSendFlow::spawn(key, job.send_target.clone(), now_ms);
flows.insert(key, Arc::clone(&flow));
flow
}
fn prune_idle_locked(
&self,
flows: &mut HashMap<MacSendFlowKey, Arc<MacSequencedSendFlow>>,
now_ms: u64,
) {
let last = self
.last_prune_ms
.load(std::sync::atomic::Ordering::Relaxed);
if now_ms.saturating_sub(last) < 10_000 {
return;
}
if self
.last_prune_ms
.compare_exchange(
last,
now_ms,
std::sync::atomic::Ordering::Relaxed,
std::sync::atomic::Ordering::Relaxed,
)
.is_err()
{
return;
}
let idle_ms = mac_send_flow_idle_ms();
flows.retain(|_, flow| {
if flow.is_idle(now_ms, idle_ms) {
flow.close();
false
} else {
true
}
});
}
}
#[cfg(target_os = "macos")]
fn macos_ordered_sender_enabled() -> bool {
static VALUE: OnceLock<bool> = OnceLock::new();
*VALUE.get_or_init(|| {
std::env::var("FIPS_MACOS_ORDERED_SENDER")
.ok()
.map(|raw| {
!matches!(
raw.trim().to_ascii_lowercase().as_str(),
"0" | "false" | "no" | "off"
)
})
.unwrap_or(false)
})
}
fn macos_worker_stride() -> usize {
static VALUE: OnceLock<usize> = OnceLock::new();
*VALUE.get_or_init(|| {
std::env::var("FIPS_MACOS_WORKER_STRIDE")
.ok()
.and_then(|raw| raw.trim().parse::<usize>().ok())
.unwrap_or(1)
.clamp(1, 64)
})
}
#[cfg(target_os = "macos")]
fn macos_worker_batch_size() -> usize {
static VALUE: OnceLock<usize> = OnceLock::new();
*VALUE.get_or_init(|| {
std::env::var("FIPS_MACOS_WORKER_BATCH")
.ok()
.and_then(|raw| raw.trim().parse::<usize>().ok())
.unwrap_or(1)
.clamp(1, 64)
})
}
#[cfg(target_os = "macos")]
fn mac_send_flow_idle_ms() -> u64 {
static VALUE: OnceLock<u64> = OnceLock::new();
*VALUE.get_or_init(|| {
std::env::var("FIPS_MACOS_SEND_FLOW_IDLE_MS")
.ok()
.and_then(|raw| raw.trim().parse::<u64>().ok())
.unwrap_or(120_000)
.max(10_000)
})
}
#[cfg(target_os = "macos")]
fn mac_now_ms() -> u64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|duration| duration.as_millis() as u64)
.unwrap_or(0)
}
#[cfg(target_os = "macos")]
struct MacSequencedSendFlow {
key: MacSendFlowKey,
send_target: SelectedSendTarget,
next_seq: std::sync::atomic::AtomicU64,
last_used_ms: std::sync::atomic::AtomicU64,
state: Mutex<MacSendFlowState>,
ready_cv: Condvar,
space_cv: Condvar,
}
#[cfg(target_os = "macos")]
#[derive(Default)]
struct MacSendFlowState {
next_send_seq: u64,
pending: BTreeMap<u64, MacSendItem>,
closed: bool,
}
#[cfg(target_os = "macos")]
struct MacCompletionGroup {
flow_key: MacSendFlowKey,
flow: Arc<MacSequencedSendFlow>,
items: Vec<(u64, MacSendItem)>,
}
#[cfg(target_os = "macos")]
enum MacSendItem {
Packet {
packet: Vec<u8>,
bulk_endpoint_data: bool,
drop_on_backpressure: bool,
},
Skip,
}
#[cfg(target_os = "macos")]
impl MacCompletionGroup {
fn new(flow: Arc<MacSequencedSendFlow>, seq: u64, item: MacSendItem) -> Self {
let flow_key = flow.key;
Self {
flow_key,
flow,
items: vec![(seq, item)],
}
}
#[cfg(test)]
fn target_key(&self) -> MacSendFlowKey {
self.flow_key
}
fn push(&mut self, flow: &Arc<MacSequencedSendFlow>, seq: u64, item: MacSendItem) {
debug_assert_eq!(
self.flow_key, flow.key,
"macOS completion group must keep the queued flow key"
);
debug_assert!(
Arc::ptr_eq(&self.flow, flow),
"macOS completion group must not merge a different flow owner"
);
self.items.push((seq, item));
}
fn complete(self) {
self.flow.complete_many(self.items);
}
}
#[cfg(target_os = "macos")]
impl MacSequencedSendFlow {
fn spawn(key: MacSendFlowKey, send_target: SelectedSendTarget, now_ms: u64) -> Arc<Self> {
let flow = Arc::new(Self {
key,
send_target,
next_seq: std::sync::atomic::AtomicU64::new(0),
last_used_ms: std::sync::atomic::AtomicU64::new(now_ms),
state: Mutex::new(MacSendFlowState::default()),
ready_cv: Condvar::new(),
space_cv: Condvar::new(),
});
let thread_flow = Arc::clone(&flow);
std::thread::Builder::new()
.name(format!("fips-mac-send-{}", key.socket_fd))
.spawn(move || thread_flow.run())
.expect("failed to spawn fips macOS send thread");
flow
}
fn reserve_seq(&self) -> u64 {
self.next_seq
.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
}
fn mark_used(&self, now_ms: u64) {
self.last_used_ms
.store(now_ms, std::sync::atomic::Ordering::Relaxed);
}
fn is_idle(&self, now_ms: u64, idle_ms: u64) -> bool {
let last_used = self.last_used_ms.load(std::sync::atomic::Ordering::Relaxed);
if now_ms.saturating_sub(last_used) < idle_ms {
return false;
}
let state = self.state.lock().expect("mac send flow state poisoned");
state.pending.is_empty()
&& state.next_send_seq == self.next_seq.load(std::sync::atomic::Ordering::Relaxed)
}
fn close(&self) {
let mut state = self.state.lock().expect("mac send flow state poisoned");
state.closed = true;
drop(state);
self.ready_cv.notify_one();
self.space_cv.notify_all();
}
fn complete_many(&self, items: Vec<(u64, MacSendItem)>) {
const PENDING_CAP: usize = 4096;
if items.is_empty() {
return;
}
let mut state = self.state.lock().expect("mac send flow state poisoned");
if state.closed {
return;
}
let mut wakes_sender = false;
for (seq, item) in items {
while state.pending.len() >= PENDING_CAP && seq != state.next_send_seq && !wakes_sender
{
state = self
.space_cv
.wait(state)
.expect("mac send flow state poisoned");
}
if seq == state.next_send_seq {
wakes_sender = true;
}
state.pending.insert(seq, item);
}
drop(state);
if wakes_sender {
self.ready_cv.notify_one();
}
}
fn run(self: Arc<Self>) {
trace!(
socket_fd = self.key.socket_fd,
connected_fd = ?self.key.connected_fd,
dest = %self.send_target.dest_addr(),
"macOS ordered UDP sender starting"
);
let (fd, connected) = self.send_target.fd_and_connected();
let mut backpressure = SendBackpressurePacer::default();
let mut rate_pacer = MacSendRatePacer::default();
loop {
let item = {
let mut state = self.state.lock().expect("mac send flow state poisoned");
loop {
let next = state.next_send_seq;
if let Some(item) = state.pending.remove(&next) {
state.next_send_seq = next.wrapping_add(1);
self.space_cv.notify_one();
break item;
}
if state.closed {
return;
}
state = self
.ready_cv
.wait(state)
.expect("mac send flow state poisoned");
}
};
match item {
MacSendItem::Packet {
packet,
bulk_endpoint_data,
drop_on_backpressure,
} => {
let _t = crate::perf_profile::Timer::start(crate::perf_profile::Stage::UdpSend);
if bulk_endpoint_data {
rate_pacer.pace(packet.len());
}
if let Err(err) = send_one_with_backpressure(
fd,
connected,
&self.send_target.dest_addr(),
&packet,
&mut backpressure,
bulk_endpoint_data,
drop_on_backpressure,
) {
debug!(
socket_fd = self.key.socket_fd,
connected_fd = ?self.key.connected_fd,
dest = %self.send_target.dest_addr(),
error = %err,
"macOS ordered UDP send failed"
);
}
}
MacSendItem::Skip => {}
}
}
}
}
#[cfg(target_os = "macos")]
fn push_mac_completion(
groups: &mut Vec<MacCompletionGroup>,
flow: Arc<MacSequencedSendFlow>,
seq: u64,
item: MacSendItem,
) {
if let Some(group) = groups
.iter_mut()
.find(|group| Arc::ptr_eq(&group.flow, &flow))
{
group.push(&flow, seq, item);
} else {
groups.push(MacCompletionGroup::new(flow, seq, item));
}
}