1use std::sync::atomic::{AtomicU64, Ordering};
2use std::sync::{Arc, OnceLock};
3use std::task::{Context, Poll, ready};
4use std::{cmp, future::Future, future::poll_fn, hash, pin::Pin};
5
6use async_channel::{Receiver, Sender, TrySendError, unbounded};
7use event_listener::Event;
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, affinity::Placement, metrics::WorkerSlot};
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,
38}
39
40#[derive(Debug)]
41pub struct Worker<T> {
46 name: String,
47 reqs: Sender<T>,
48 queue: Receiver<T>,
50 stop: Sender<Shutdown>,
51 avail: WorkerAvailability,
52 arbiter: Arbiter,
53 metrics: Option<Arc<WorkerSlot>>,
54 cpus: Arc<OnceLock<Box<[usize]>>>,
56}
57
58#[derive(Debug)]
59pub struct WorkerStop(oneshot::AsyncReceiver<bool>);
65
66impl<T> Worker<T> {
67 pub fn start<F>(name: String, cfg: F, cpu: Option<usize>) -> Worker<T>
76 where
77 T: Send + 'static,
78 F: ServerConfiguration<Item = T>,
79 {
80 Self::start_with(name, cfg, cpu.map(Placement::cpu), None)
81 }
82
83 pub(crate) fn start_with<F>(
84 name: String,
85 cfg: F,
86 placement: Option<Placement>,
87 metrics: Option<Arc<WorkerSlot>>,
88 ) -> Worker<T>
89 where
90 T: Send + 'static,
91 F: ServerConfiguration<Item = T>,
92 {
93 let (reqs, r_rx) = unbounded();
94 let (stop, s_rx) = unbounded();
95 let (avail, a_tx) = WorkerAvailability::create();
96 let n = name.clone();
97 let inner = avail.inner.clone();
98 let cpus = Arc::new(OnceLock::new());
99
100 let n2 = name.clone();
101 let cpus2 = cpus.clone();
102 let init = move || {
103 if let Some(placement) = placement {
104 if placement.apply() {
105 log::info!("Set affinity to {placement} for worker {n2:?}");
106 let _ = cpus2.set(placement.cpus().into());
109 } else {
110 log::warn!("Cannot set affinity to {placement} for worker {n2:?}");
111 }
112 }
113 };
114
115 let arbiter =
118 Arbiter::with_init(name.clone(), init).on_stop(move || inner.set_failed(true));
119 if let Some(ref m) = metrics {
120 m.set_arbiter(arbiter.clone());
121 }
122 let worker = Worker {
123 reqs,
124 queue: r_rx.clone(),
125 avail,
126 stop,
127 name,
128 arbiter: arbiter.clone(),
129 metrics: metrics.clone(),
130 cpus,
131 };
132 let reqs = Queue(r_rx);
133
134 arbiter.handle().spawn_detached(async move {
135 log::info!("Starting worker {n:?}");
136
137 WorkerSlot::set_current(metrics.clone());
140 let task = spawn(async move {
141 match ServiceRunner::create(&n, cfg, reqs, s_rx, a_tx, metrics).await {
142 Ok(wrk) => {
143 log::debug!("Server instance has been created in {n:?}");
144 wrk.run().await;
145 }
146 Err(()) => {
147 log::error!("Cannot start worker {n:?}");
148 }
149 }
150 });
151 ntex_rt::spawn_detached(async move {
152 let _ = task.await;
153 Arbiter::current().stop();
154 });
155 });
156
157 worker
158 }
159
160 pub fn name(&self) -> &str {
162 &self.name
163 }
164
165 pub(crate) fn arbiter(&self) -> &Arbiter {
167 &self.arbiter
168 }
169
170 pub(crate) fn load(&self) -> usize {
172 self.metrics.as_ref().map_or(0, |m| m.active()) + self.reqs.len()
173 }
174
175 pub(crate) fn cpus(&self) -> &[usize] {
178 self.cpus.get().map_or(&[], |cpus| cpus)
179 }
180
181 #[cfg(test)]
182 pub(crate) fn set_cpus(&self, cpus: &[usize]) {
183 let _ = self.cpus.set(cpus.into());
184 }
185
186 #[inline]
187 pub fn send(&self, msg: T) -> Result<(), T> {
193 self.reqs.try_send(msg).map_err(TrySendError::into_inner)
194 }
195
196 pub(crate) fn take_queued(&self, close: bool) -> Vec<T> {
202 if close {
203 self.queue.close();
204 }
205 (0..self.queue.len())
206 .map_while(|_| self.queue.try_recv().ok())
207 .collect()
208 }
209
210 pub fn status(&self) -> WorkerStatus {
212 self.avail.inner.status()
213 }
214
215 pub async fn wait_for_status(&mut self) -> WorkerStatus {
223 self.avail.wait_for_update().await
224 }
225
226 pub fn stop(&self, timeout: Millis) -> WorkerStop {
231 let (result, rx) = oneshot::async_channel();
232 let _ = self.stop.try_send(Shutdown { timeout, result });
233 WorkerStop(rx)
234 }
235}
236
237impl<T> Eq for Worker<T> {}
238
239impl<T> PartialEq for Worker<T> {
240 fn eq(&self, other: &Worker<T>) -> bool {
241 self.name == other.name
242 }
243}
244
245impl<T> cmp::Ord for Worker<T> {
246 fn cmp(&self, other: &Self) -> cmp::Ordering {
247 self.name.cmp(&other.name)
248 }
249}
250
251impl<T> cmp::PartialOrd for Worker<T> {
252 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
253 Some(self.cmp(other))
254 }
255}
256
257impl<T> hash::Hash for Worker<T> {
258 fn hash<H: hash::Hasher>(&self, state: &mut H) {
259 self.name.hash(state);
260 }
261}
262
263impl<T> Clone for Worker<T> {
264 fn clone(&self) -> Self {
265 Worker {
266 name: self.name.clone(),
267 reqs: self.reqs.clone(),
268 queue: self.queue.clone(),
269 stop: self.stop.clone(),
270 avail: self.avail.clone(),
271 arbiter: self.arbiter.clone(),
272 metrics: self.metrics.clone(),
273 cpus: self.cpus.clone(),
274 }
275 }
276}
277
278impl Future for WorkerStop {
279 type Output = bool;
280
281 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
282 match ready!(Pin::new(&mut self.0).poll(cx)) {
283 Ok(res) => Poll::Ready(res),
284 Err(_) => Poll::Ready(true),
285 }
286 }
287}
288
289#[derive(Debug, Clone)]
290struct WorkerAvailability {
291 inner: Arc<Inner>,
292 seen: u64,
294}
295
296#[derive(Debug, Clone)]
297struct WorkerAvailabilityTx {
298 inner: Arc<Inner>,
299}
300
301const UNAVAILABLE: u64 = 0;
302const AVAILABLE: u64 = 1;
303const FAILED: u64 = 2;
304const STATUS_BITS: u32 = 2;
305const STATUS_MASK: u64 = (1 << STATUS_BITS) - 1;
306
307#[derive(Debug)]
308struct Inner {
309 state: AtomicU64,
311 event: Event,
312}
313
314fn status(state: u64) -> WorkerStatus {
315 match state & STATUS_MASK {
316 AVAILABLE => WorkerStatus::Available,
317 FAILED => WorkerStatus::Failed,
318 _ => WorkerStatus::Unavailable,
319 }
320}
321
322impl Inner {
323 fn status(&self) -> WorkerStatus {
324 status(self.state.load(Ordering::Acquire))
325 }
326
327 fn update(&self, st: u64) -> bool {
331 let mut cur = self.state.load(Ordering::Acquire);
332 loop {
333 let old = cur & STATUS_MASK;
334 if old == st || old == FAILED {
335 return false;
336 }
337 let next = (((cur >> STATUS_BITS).wrapping_add(1)) << STATUS_BITS) | st;
338 match self
339 .state
340 .compare_exchange_weak(cur, next, Ordering::AcqRel, Ordering::Acquire)
341 {
342 Ok(_) => return true,
343 Err(val) => cur = val,
344 }
345 }
346 }
347
348 fn set_failed(&self, notify: bool) {
349 self.update(FAILED);
350 if notify {
351 self.event.notify(usize::MAX);
352 }
353 }
354}
355
356impl WorkerAvailability {
357 fn create() -> (Self, WorkerAvailabilityTx) {
358 let inner = Arc::new(Inner {
359 state: AtomicU64::new(UNAVAILABLE),
360 event: Event::new(),
361 });
362
363 let avail = WorkerAvailability {
364 inner: inner.clone(),
365 seen: UNAVAILABLE,
366 };
367 let avail_tx = WorkerAvailabilityTx { inner };
368 (avail, avail_tx)
369 }
370
371 fn changed(&mut self) -> Option<WorkerStatus> {
372 let cur = self.inner.state.load(Ordering::Acquire);
373 let st = status(cur);
374 if cur != self.seen || st == WorkerStatus::Failed {
375 self.seen = cur;
376 Some(st)
377 } else {
378 None
379 }
380 }
381
382 async fn wait_for_update(&mut self) -> WorkerStatus {
383 loop {
384 if let Some(st) = self.changed() {
385 return st;
386 }
387 let listener = self.inner.event.listen();
388 if let Some(st) = self.changed() {
389 return st;
390 }
391 listener.await;
392 }
393 }
394}
395
396impl WorkerAvailabilityTx {
397 fn set(&self, val: bool) {
398 if self.inner.update(if val { AVAILABLE } else { UNAVAILABLE }) {
399 self.inner.event.notify(usize::MAX);
400 }
401 }
402}
403
404impl Drop for WorkerAvailabilityTx {
405 fn drop(&mut self) {
406 self.inner.set_failed(!std::thread::panicking());
409 }
410}
411
412struct Queue<T>(Receiver<T>);
417
418impl<T> Drop for Queue<T> {
419 fn drop(&mut self) {
420 self.0.close();
421 }
422}
423
424struct ServiceRunner<F: ServerConfiguration<Item = Req>, Req> {
428 name: String,
429 factory: F,
430 svc: Pipeline<Req, (), ()>,
431 reqs: Queue<Req>,
432 stop: Pin<Box<dyn Stream<Item = Shutdown>>>,
433 availability: WorkerAvailabilityTx,
434 metrics: Option<Arc<WorkerSlot>>,
435}
436
437impl<F, Req> ServiceRunner<F, Req>
438where
439 Req: Send + 'static,
440 F: ServerConfiguration<Item = Req> + 'static,
441{
442 async fn create(
443 name: &str,
444 factory: F,
445 reqs: Queue<Req>,
446 stop: Receiver<Shutdown>,
447 availability: WorkerAvailabilityTx,
448 metrics: Option<Arc<WorkerSlot>>,
449 ) -> Result<Self, ()> {
450 availability.set(false);
451 let mut stop = Box::pin(stop);
452
453 let svc = match select(factory.create(), stream_recv(&mut stop)).await {
454 Either::Left(Ok(svc)) => Pipeline::new((), svc),
455 Either::Right(Some(Shutdown { result, .. })) => {
456 log::trace!("Shutdown uninitialized worker");
457 let _ = result.send(false);
458 return Err(());
459 }
460 Either::Left(Err(_)) | Either::Right(None) => return Err(()),
461 };
462 availability.set(true);
463
464 Ok(ServiceRunner {
465 factory,
466 svc,
467 reqs,
468 stop,
469 availability,
470 metrics,
471 name: name.to_string(),
472 })
473 }
474
475 async fn run(mut self) {
476 loop {
477 let mut recv = std::pin::pin!(self.reqs.0.recv());
478 let fut = poll_fn(|cx| {
479 match self.svc.poll_ready(cx) {
480 Poll::Ready(Ok(())) => {
481 self.availability.set(true);
482 if let Some(ref m) = self.metrics {
483 m.ready(true);
484 }
485 }
486 Poll::Ready(Err(err)) => {
487 self.availability.set(false);
488 return Poll::Ready(Err(err));
489 }
490 Poll::Pending => {
491 self.availability.set(false);
492 if let Some(ref m) = self.metrics {
493 m.ready(false);
494 }
495 return Poll::Pending;
496 }
497 }
498
499 if let Ok(item) = ready!(recv.as_mut().poll(cx)) {
500 Poll::Ready(Ok(Some(item)))
501 } else {
502 log::error!("Server is gone");
503 Poll::Ready(Ok(None))
504 }
505 });
506
507 match select(fut, stream_recv(&mut self.stop)).await {
508 Either::Left(Ok(Some(item))) => {
509 if let Some(ref m) = self.metrics {
511 m.item();
512 }
513 let _ = self.svc.call(item).await;
514 continue;
515 }
516 Either::Left(Err(())) => {
517 ntex_rt::spawn_detached(async move {
519 self.svc.shutdown().await;
520 });
521 }
522 Either::Right(Some(Shutdown { timeout, result })) => {
523 log::info!("Shutting down {:?} worker gracefuly", self.name);
524 self.availability.set(false);
525
526 let timeout = if timeout.is_zero() { STOP_TIMEOUT } else { timeout };
527 self.stop(timeout, Some(result)).await;
528 return;
529 }
530 Either::Left(Ok(None)) | Either::Right(None) => {
531 log::info!("Shutting down {:?} worker", self.name);
532 self.availability.set(false);
533 self.stop(STOP_TIMEOUT, None).await;
534 return;
535 }
536 }
537
538 loop {
540 match select(self.factory.create(), stream_recv(&mut self.stop)).await {
541 Either::Left(Ok(service)) => {
542 self.svc = Pipeline::new((), service);
543 break;
544 }
545 Either::Left(Err(_)) => sleep(Millis::ONE_SEC).await,
546 Either::Right(_) => return,
547 }
548 }
549 }
550 }
551
552 async fn stop(&self, timeout: Millis, result: Option<oneshot::Sender<bool>>) {
553 if let Some(ref m) = self.metrics {
555 m.ready(true);
556 }
557 let res = timeout_checked(timeout, self.svc.shutdown()).await;
558 if let Some(result) = result {
559 let _ = result.send(res.is_ok());
560 }
561
562 log::info!("Worker {:?} has been stopped", self.name);
563 }
564}
565
566#[cfg(test)]
567mod tests {
568 #![allow(clippy::unused_async_trait_impl)]
569 use std::sync::atomic::AtomicUsize;
570 use std::task::{Wake, Waker};
571
572 use ntex_service::{Ctx, Service};
573
574 use super::*;
575
576 #[derive(Default)]
577 struct WakeCount(AtomicUsize);
578
579 impl Wake for WakeCount {
580 fn wake(self: Arc<Self>) {
581 self.0.fetch_add(1, Ordering::SeqCst);
582 }
583 }
584
585 fn poll<F: Future>(fut: Pin<&mut F>, wake: &Arc<WakeCount>) -> Poll<F::Output> {
586 let waker = Waker::from(wake.clone());
587 fut.poll(&mut Context::from_waker(&waker))
588 }
589
590 #[test]
592 fn status_cloned_observers() {
593 let (mut a, tx) = WorkerAvailability::create();
594 let mut b = a.clone();
595 let (w1, w2) = (
596 Arc::new(WakeCount::default()),
597 Arc::new(WakeCount::default()),
598 );
599 {
600 let mut first = std::pin::pin!(a.wait_for_update());
601 let mut second = std::pin::pin!(b.wait_for_update());
602 assert!(poll(first.as_mut(), &w1).is_pending());
603 assert!(poll(second.as_mut(), &w2).is_pending());
604
605 tx.set(true);
606 assert_eq!(w1.0.load(Ordering::SeqCst), 1);
607 assert_eq!(w2.0.load(Ordering::SeqCst), 1);
608 assert_eq!(
609 poll(second.as_mut(), &w2),
610 Poll::Ready(WorkerStatus::Available)
611 );
612 assert_eq!(
613 poll(first.as_mut(), &w1),
614 Poll::Ready(WorkerStatus::Available)
615 );
616 }
617
618 let mut c = a.clone();
620 assert!(poll(std::pin::pin!(a.wait_for_update()), &w1).is_pending());
621 assert!(poll(std::pin::pin!(c.wait_for_update()), &w1).is_pending());
622
623 tx.set(false);
625 tx.set(true);
626 tx.set(false);
627 let st = poll(std::pin::pin!(a.wait_for_update()), &w1);
628 assert_eq!(st, Poll::Ready(WorkerStatus::Unavailable));
629 assert!(poll(std::pin::pin!(a.wait_for_update()), &w1).is_pending());
630
631 let mut d = a.clone();
633 let mut fut = std::pin::pin!(d.wait_for_update());
634 assert!(poll(fut.as_mut(), &w2).is_pending());
635 drop(tx);
636 assert_eq!(poll(fut.as_mut(), &w2), Poll::Ready(WorkerStatus::Failed));
637 for wrk in [&mut a, &mut b, &mut c] {
638 for _ in 0..2 {
639 let st = poll(std::pin::pin!(wrk.wait_for_update()), &w1);
640 assert_eq!(st, Poll::Ready(WorkerStatus::Failed));
641 }
642 }
643 assert_eq!(a.inner.status(), WorkerStatus::Failed);
644 }
645
646 #[test]
648 fn status_failed_is_terminal() {
649 let (a, tx) = WorkerAvailability::create();
650 a.inner.set_failed(false);
651 tx.set(true);
652 assert_eq!(a.inner.status(), WorkerStatus::Failed);
653 }
654
655 #[derive(Clone)]
656 struct Cfg;
657
658 struct Srv;
659
660 impl Service<(), usize> for Srv {
661 type Res = ();
662 type Error = ();
663
664 async fn call(&self, _: usize, _: Ctx<'_, Self, ()>) -> Result<(), ()> {
665 Ok(())
666 }
667 }
668
669 impl ServerConfiguration for Cfg {
670 type Item = usize;
671 type Service = Srv;
672
673 async fn create(&self) -> std::io::Result<Srv> {
674 Ok(Srv)
675 }
676 }
677
678 #[ntex::test]
680 async fn worker_cpus() {
681 let cpu = crate::affinity::plan(crate::Affinity::Cpu).pop();
682 for (placement, placed) in [
683 (Placement::cpu(usize::MAX >> 1), false),
684 (
685 cpu.unwrap_or_else(|| Placement::cpu(0)),
686 cfg!(any(windows, target_os = "linux", target_os = "android")),
687 ),
688 ] {
689 let mut wrk = Worker::start_with("test".into(), Cfg, Some(placement), None);
690 assert_eq!(wrk.wait_for_status().await, WorkerStatus::Available);
691 assert_eq!(!wrk.cpus().is_empty(), placed);
692 wrk.stop(Millis::ZERO).await;
693 }
694 }
695}