wireshift_fallback/
lib.rs1#![warn(clippy::pedantic)]
2#![cfg_attr(
3 not(test),
4 deny(
5 clippy::unwrap_used,
6 clippy::expect_used,
7 clippy::todo,
8 clippy::unimplemented,
9 clippy::panic
10 )
11)]
12#![allow(
13 clippy::module_name_repetitions,
14 clippy::must_use_candidate,
15 clippy::missing_errors_doc,
16)]
17#![deny(unsafe_op_in_unsafe_fn)]
18#![warn(missing_docs)]
25#![allow(
26 clippy::needless_pass_by_value,
27 clippy::unnecessary_wraps,
28 clippy::fn_params_excessive_bools
29)]
30
31pub mod net_ops;
33pub mod ops;
35pub mod pool;
37
38use std::sync::atomic::{AtomicBool, Ordering};
39use std::sync::{Arc, Mutex};
40use std::thread;
41
42use crossbeam_channel::{bounded, Sender, TrySendError};
43use wireshift_core::backend::{
44 disconnected_error, Backend, BackendCompletion, BackendKind, BackendSubmission,
45 CancellationHandle,
46};
47use wireshift_core::{Error, Result, RingConfig};
48
49use crate::pool::{worker_loop, CancelRegistry, WorkerMessage};
50
51#[derive(Debug)]
53pub struct FallbackBackend {
54 sender: Sender<WorkerMessage>,
55 cancel_registry: Arc<CancelRegistry>,
56 workers: Mutex<Vec<thread::JoinHandle<()>>>,
57 shutting_down: AtomicBool,
58}
59
60impl FallbackBackend {
61 pub fn new(
68 config: &RingConfig,
69 completion_tx: std::sync::mpsc::Sender<BackendCompletion>,
70 ) -> Result<Self> {
71 let (sender, receiver) = bounded(config.queue_depth as usize);
72 let cancel_registry = Arc::new(CancelRegistry::new());
73 let mut workers = Vec::with_capacity(config.fallback_threads);
74 for _ in 0..config.fallback_threads {
75 let rx = receiver.clone();
76 let completion_tx = completion_tx.clone();
77 let cancel_registry = cancel_registry.clone();
78 workers.push(thread::spawn(move || {
79 worker_loop(rx, completion_tx, cancel_registry);
80 }));
81 }
82 Ok(Self {
83 sender,
84 cancel_registry,
85 workers: Mutex::new(workers),
86 shutting_down: AtomicBool::new(false),
87 })
88 }
89}
90
91impl Backend for FallbackBackend {
92 fn kind(&self) -> BackendKind {
93 BackendKind::Fallback
94 }
95
96 fn submit(&self, submission: BackendSubmission) -> Result<()> {
97 if self.shutting_down.load(Ordering::Acquire) {
98 return Err(Error::completion(
99 "fallback backend is shutting down",
100 "do not submit new requests while shutdown is in progress",
101 ));
102 }
103 let _ = self.cancel_registry.insert(submission.id)?;
104 match self.sender.try_send(WorkerMessage::Submit(submission)) {
105 Ok(()) => Ok(()),
106 Err(TrySendError::Full(WorkerMessage::Submit(submission))) => {
107 let _ = self.cancel_registry.remove(submission.id);
108 Err(Error::submission(
109 "fallback work queue is full",
110 "drain completions or increase queue_depth before submitting more work",
111 ))
112 }
113 Err(TrySendError::Disconnected(WorkerMessage::Submit(submission))) => {
114 let _ = self.cancel_registry.remove(submission.id);
115 Err(disconnected_error())
116 }
117 Err(
118 TrySendError::Full(WorkerMessage::Shutdown)
119 | TrySendError::Disconnected(WorkerMessage::Shutdown),
120 ) => Err(disconnected_error()),
121 }
122 }
123
124 fn cancel(&self, cancellation: CancellationHandle) -> Result<()> {
125 if self.cancel_registry.cancel(cancellation.target)? {
126 Ok(())
127 } else {
128 Err(Error::canceled(
129 format!("request {} is no longer cancelable", cancellation.target),
130 "cancel the request before it completes or starts running",
131 ))
132 }
133 }
134
135 fn shutdown(&self) -> Result<()> {
136 self.shutting_down.store(true, Ordering::Release);
137 let mut workers = self.workers.lock().map_err(|_| {
138 Error::completion(
139 "fallback worker set mutex was poisoned",
140 "avoid panicking while the ring is being dropped",
141 )
142 })?;
143 let worker_count = workers.len();
144 for _ in 0..worker_count {
145 self.sender
146 .send(WorkerMessage::Shutdown)
147 .map_err(|_| disconnected_error())?;
148 }
149 for worker in workers.drain(..) {
150 worker.join().map_err(|_| {
151 Error::completion(
152 "fallback worker thread panicked during shutdown",
153 "fix the backend worker panic before dropping the ring",
154 )
155 })?;
156 }
157 Ok(())
158 }
159}