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