1use std::sync::atomic::{AtomicBool, Ordering};
2use std::task::{Context, Poll, ready};
3use std::{cmp, future::Future, future::poll_fn, hash, pin::Pin, sync::Arc};
4
5use async_channel::{Receiver, Sender, TrySendError, unbounded};
6use atomic_waker::AtomicWaker;
7use core_affinity::CoreId;
8
9use ntex_rt::{Arbiter, spawn};
10use ntex_service::Pipeline;
11use ntex_util::future::{Either, Stream, select, stream_recv};
12use ntex_util::time::{Millis, sleep, timeout_checked};
13
14use crate::ServerConfiguration;
15
16const STOP_TIMEOUT: Millis = Millis(3000);
17
18#[derive(Debug)]
19struct Shutdown {
21 timeout: Millis,
22 result: oneshot::Sender<bool>,
23}
24
25#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
26pub enum WorkerStatus {
28 Available,
30 #[default]
32 Unavailable,
33 Failed,
35}
36
37#[derive(Debug)]
38pub struct Worker<T> {
42 name: String,
43 reqs: Sender<T>,
44 stop: Sender<Shutdown>,
45 avail: WorkerAvailability,
46}
47
48#[derive(Debug)]
49pub struct WorkerStop(oneshot::AsyncReceiver<bool>);
54
55impl<T> Worker<T> {
56 pub fn start<F>(name: String, cfg: F, cid: Option<CoreId>) -> Worker<T>
58 where
59 T: Send + 'static,
60 F: ServerConfiguration<Item = T>,
61 {
62 let (reqs, r_rx) = unbounded();
63 let (stop, s_rx) = unbounded();
64 let (avail, a_tx) = WorkerAvailability::create();
65 let n = name.clone();
66 let inner = avail.inner.clone();
67
68 let worker = Worker {
69 reqs,
70 avail,
71 stop,
72 name: name.clone(),
73 };
74
75 Arbiter::with_name(name)
76 .on_stop(move || {
77 inner.failed.store(true, Ordering::Release);
78 inner.updated.store(true, Ordering::Release);
79 inner.available.store(false, Ordering::Release);
80 inner.waker.wake();
81 })
82 .handle()
83 .spawn(async move {
84 log::info!("Starting worker {n:?}");
85 if let Some(cid) = cid
86 && core_affinity::set_for_current(cid)
87 {
88 log::info!("Set affinity to {cid:?} for worker {n:?}");
89 }
90
91 spawn(async move {
92 match ServiceRunner::create(&n, cfg, r_rx, s_rx, a_tx).await {
93 Ok(wrk) => {
94 log::debug!("Server instance has been created in {n:?}");
95 wrk.run().await;
96 }
97 Err(()) => {
98 log::error!("Cannot start worker {n:?}");
99 }
100 }
101 Arbiter::current().stop();
102 });
103 });
104
105 worker
106 }
107
108 pub fn name(&self) -> &str {
110 &self.name
111 }
112
113 #[inline]
114 pub fn send(&self, msg: T) -> Result<(), T> {
119 self.reqs.try_send(msg).map_err(TrySendError::into_inner)
120 }
121
122 pub fn status(&self) -> WorkerStatus {
124 if self.avail.failed() {
125 WorkerStatus::Failed
126 } else if self.avail.available() {
127 WorkerStatus::Available
128 } else {
129 WorkerStatus::Unavailable
130 }
131 }
132
133 pub async fn wait_for_status(&mut self) -> WorkerStatus {
135 if self.avail.failed() {
136 WorkerStatus::Failed
137 } else {
138 self.avail.wait_for_update().await;
139 self.status()
140 }
141 }
142
143 pub fn stop(&self, timeout: Millis) -> WorkerStop {
147 let (result, rx) = oneshot::async_channel();
148 let _ = self.stop.try_send(Shutdown { timeout, result });
149 WorkerStop(rx)
150 }
151}
152
153impl<T> Eq for Worker<T> {}
154
155impl<T> PartialEq for Worker<T> {
156 fn eq(&self, other: &Worker<T>) -> bool {
157 self.name == other.name
158 }
159}
160
161impl<T> cmp::Ord for Worker<T> {
162 fn cmp(&self, other: &Self) -> cmp::Ordering {
163 self.name.cmp(&other.name)
164 }
165}
166
167impl<T> cmp::PartialOrd for Worker<T> {
168 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
169 Some(self.cmp(other))
170 }
171}
172
173impl<T> hash::Hash for Worker<T> {
174 fn hash<H: hash::Hasher>(&self, state: &mut H) {
175 self.name.hash(state);
176 }
177}
178
179impl<T> Clone for Worker<T> {
180 fn clone(&self) -> Self {
181 Worker {
182 name: self.name.clone(),
183 reqs: self.reqs.clone(),
184 stop: self.stop.clone(),
185 avail: self.avail.clone(),
186 }
187 }
188}
189
190impl Future for WorkerStop {
191 type Output = bool;
192
193 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
194 match ready!(Pin::new(&mut self.0).poll(cx)) {
195 Ok(res) => Poll::Ready(res),
196 Err(_) => Poll::Ready(true),
197 }
198 }
199}
200
201#[derive(Debug, Clone)]
202struct WorkerAvailability {
203 inner: Arc<Inner>,
204}
205
206#[derive(Debug, Clone)]
207struct WorkerAvailabilityTx {
208 inner: Arc<Inner>,
209}
210
211#[derive(Debug)]
212struct Inner {
213 waker: AtomicWaker,
214 updated: AtomicBool,
215 available: AtomicBool,
216 failed: AtomicBool,
217}
218
219impl WorkerAvailability {
220 fn create() -> (Self, WorkerAvailabilityTx) {
221 let inner = Arc::new(Inner {
222 waker: AtomicWaker::new(),
223 updated: AtomicBool::new(false),
224 available: AtomicBool::new(false),
225 failed: AtomicBool::new(false),
226 });
227
228 let avail = WorkerAvailability {
229 inner: inner.clone(),
230 };
231 let avail_tx = WorkerAvailabilityTx { inner };
232 (avail, avail_tx)
233 }
234
235 fn failed(&self) -> bool {
236 self.inner.failed.load(Ordering::Acquire)
237 }
238
239 fn available(&self) -> bool {
240 self.inner.available.load(Ordering::Acquire)
241 }
242
243 async fn wait_for_update(&self) {
244 poll_fn(|cx| {
245 self.inner.waker.register(cx.waker());
246 if self.inner.updated.swap(false, Ordering::AcqRel) {
247 Poll::Ready(())
248 } else {
249 Poll::Pending
250 }
251 })
252 .await;
253 }
254}
255
256impl WorkerAvailabilityTx {
257 fn set(&self, val: bool) {
258 let old = self.inner.available.swap(val, Ordering::Release);
259 if old != val {
260 self.inner.updated.store(true, Ordering::Release);
261 self.inner.waker.wake();
262 }
263 }
264}
265
266impl Drop for WorkerAvailabilityTx {
267 fn drop(&mut self) {
268 self.inner.failed.store(true, Ordering::Release);
269 self.inner.updated.store(true, Ordering::Release);
270 self.inner.available.store(false, Ordering::Release);
271 self.inner.waker.wake();
272 }
273}
274
275struct ServiceRunner<F: ServerConfiguration<Item = Req>, Req> {
279 name: String,
280 factory: F,
281 svc: Pipeline<Req, (), ()>,
282 reqs: Receiver<Req>,
283 stop: Pin<Box<dyn Stream<Item = Shutdown>>>,
284 availability: WorkerAvailabilityTx,
285}
286
287impl<F, Req> ServiceRunner<F, Req>
288where
289 Req: Send + 'static,
290 F: ServerConfiguration<Item = Req> + 'static,
291{
292 async fn create(
293 name: &str,
294 factory: F,
295 reqs: Receiver<Req>,
296 stop: Receiver<Shutdown>,
297 availability: WorkerAvailabilityTx,
298 ) -> Result<Self, ()> {
299 availability.set(false);
300 let mut stop = Box::pin(stop);
301
302 let svc = match select(factory.create(), stream_recv(&mut stop)).await {
303 Either::Left(Ok(svc)) => Pipeline::new((), svc),
304 Either::Right(Some(Shutdown { result, .. })) => {
305 log::trace!("Shutdown uninitialized worker");
306 let _ = result.send(false);
307 return Err(());
308 }
309 Either::Left(Err(_)) | Either::Right(None) => return Err(()),
310 };
311 availability.set(true);
312
313 Ok(ServiceRunner {
314 factory,
315 svc,
316 reqs,
317 stop,
318 availability,
319 name: name.to_string(),
320 })
321 }
322
323 async fn run(mut self) {
324 loop {
325 let mut recv = std::pin::pin!(self.reqs.recv());
326 let fut = poll_fn(|cx| {
327 match self.svc.poll_ready(cx) {
328 Poll::Ready(Ok(())) => {
329 self.availability.set(true);
330 }
331 Poll::Ready(Err(err)) => {
332 self.availability.set(false);
333 return Poll::Ready(Err(err));
334 }
335 Poll::Pending => {
336 self.availability.set(false);
337 return Poll::Pending;
338 }
339 }
340
341 if let Ok(item) = ready!(recv.as_mut().poll(cx)) {
342 Poll::Ready(Ok(Some(item)))
343 } else {
344 log::error!("Server is gone");
345 Poll::Ready(Ok(None))
346 }
347 });
348
349 match select(fut, stream_recv(&mut self.stop)).await {
350 Either::Left(Ok(Some(item))) => {
351 let _ = self.svc.call(item).await;
353 continue;
354 }
355 Either::Left(Err(())) => {
356 ntex_rt::spawn(async move {
358 self.svc.shutdown().await;
359 });
360 }
361 Either::Right(Some(Shutdown { timeout, result })) => {
362 log::info!("Shutting down {:?} worker gracefuly", self.name);
363 self.availability.set(false);
364
365 let timeout = if timeout.is_zero() { STOP_TIMEOUT } else { timeout };
366 self.stop(timeout, Some(result)).await;
367 return;
368 }
369 Either::Left(Ok(None)) | Either::Right(None) => {
370 log::info!("Shutting down {:?} worker", self.name);
371 self.availability.set(false);
372 self.stop(STOP_TIMEOUT, None).await;
373 return;
374 }
375 }
376
377 loop {
379 match select(self.factory.create(), stream_recv(&mut self.stop)).await {
380 Either::Left(Ok(service)) => {
381 self.svc = Pipeline::new((), service);
382 break;
383 }
384 Either::Left(Err(_)) => sleep(Millis::ONE_SEC).await,
385 Either::Right(_) => return,
386 }
387 }
388 }
389 }
390
391 async fn stop(&self, timeout: Millis, result: Option<oneshot::Sender<bool>>) {
392 let res = timeout_checked(timeout, self.svc.shutdown()).await;
393 if let Some(result) = result {
394 let _ = result.send(res.is_ok());
395 }
396
397 log::info!("Worker {:?} has been stopped", self.name);
398 }
399}