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,
38}
39
40#[derive(Debug)]
41pub struct Worker<T> {
46 name: String,
47 reqs: Sender<T>,
48 stop: Sender<Shutdown>,
49 avail: WorkerAvailability,
50}
51
52#[derive(Debug)]
53pub struct WorkerStop(oneshot::AsyncReceiver<bool>);
59
60impl<T> Worker<T> {
61 pub fn start<F>(name: String, cfg: F, cid: Option<CoreId>) -> Worker<T>
67 where
68 T: Send + 'static,
69 F: ServerConfiguration<Item = T>,
70 {
71 let (reqs, r_rx) = unbounded();
72 let (stop, s_rx) = unbounded();
73 let (avail, a_tx) = WorkerAvailability::create();
74 let n = name.clone();
75 let inner = avail.inner.clone();
76
77 let worker = Worker {
78 reqs,
79 avail,
80 stop,
81 name: name.clone(),
82 };
83
84 Arbiter::with_name(name)
85 .on_stop(move || {
86 inner.failed.store(true, Ordering::Release);
87 inner.updated.store(true, Ordering::Release);
88 inner.available.store(false, Ordering::Release);
89 inner.waker.wake();
90 })
91 .handle()
92 .spawn(async move {
93 log::info!("Starting worker {n:?}");
94 if let Some(cid) = cid
95 && core_affinity::set_for_current(cid)
96 {
97 log::info!("Set affinity to {cid:?} for worker {n:?}");
98 }
99
100 let _ = spawn(async move {
103 match ServiceRunner::create(&n, cfg, r_rx, s_rx, a_tx).await {
104 Ok(wrk) => {
105 log::debug!("Server instance has been created in {n:?}");
106 wrk.run().await;
107 }
108 Err(()) => {
109 log::error!("Cannot start worker {n:?}");
110 }
111 }
112 })
113 .await;
114 Arbiter::current().stop();
115 });
116
117 worker
118 }
119
120 pub fn name(&self) -> &str {
122 &self.name
123 }
124
125 #[inline]
126 pub fn send(&self, msg: T) -> Result<(), T> {
132 self.reqs.try_send(msg).map_err(TrySendError::into_inner)
133 }
134
135 pub fn status(&self) -> WorkerStatus {
137 if self.avail.failed() {
138 WorkerStatus::Failed
139 } else if self.avail.available() {
140 WorkerStatus::Available
141 } else {
142 WorkerStatus::Unavailable
143 }
144 }
145
146 pub async fn wait_for_status(&mut self) -> WorkerStatus {
150 if self.avail.failed() {
151 WorkerStatus::Failed
152 } else {
153 self.avail.wait_for_update().await;
154 self.status()
155 }
156 }
157
158 pub fn stop(&self, timeout: Millis) -> WorkerStop {
163 let (result, rx) = oneshot::async_channel();
164 let _ = self.stop.try_send(Shutdown { timeout, result });
165 WorkerStop(rx)
166 }
167}
168
169impl<T> Eq for Worker<T> {}
170
171impl<T> PartialEq for Worker<T> {
172 fn eq(&self, other: &Worker<T>) -> bool {
173 self.name == other.name
174 }
175}
176
177impl<T> cmp::Ord for Worker<T> {
178 fn cmp(&self, other: &Self) -> cmp::Ordering {
179 self.name.cmp(&other.name)
180 }
181}
182
183impl<T> cmp::PartialOrd for Worker<T> {
184 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
185 Some(self.cmp(other))
186 }
187}
188
189impl<T> hash::Hash for Worker<T> {
190 fn hash<H: hash::Hasher>(&self, state: &mut H) {
191 self.name.hash(state);
192 }
193}
194
195impl<T> Clone for Worker<T> {
196 fn clone(&self) -> Self {
197 Worker {
198 name: self.name.clone(),
199 reqs: self.reqs.clone(),
200 stop: self.stop.clone(),
201 avail: self.avail.clone(),
202 }
203 }
204}
205
206impl Future for WorkerStop {
207 type Output = bool;
208
209 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
210 match ready!(Pin::new(&mut self.0).poll(cx)) {
211 Ok(res) => Poll::Ready(res),
212 Err(_) => Poll::Ready(true),
213 }
214 }
215}
216
217#[derive(Debug, Clone)]
218struct WorkerAvailability {
219 inner: Arc<Inner>,
220}
221
222#[derive(Debug, Clone)]
223struct WorkerAvailabilityTx {
224 inner: Arc<Inner>,
225}
226
227#[derive(Debug)]
228struct Inner {
229 waker: AtomicWaker,
230 updated: AtomicBool,
231 available: AtomicBool,
232 failed: AtomicBool,
233}
234
235impl WorkerAvailability {
236 fn create() -> (Self, WorkerAvailabilityTx) {
237 let inner = Arc::new(Inner {
238 waker: AtomicWaker::new(),
239 updated: AtomicBool::new(false),
240 available: AtomicBool::new(false),
241 failed: AtomicBool::new(false),
242 });
243
244 let avail = WorkerAvailability {
245 inner: inner.clone(),
246 };
247 let avail_tx = WorkerAvailabilityTx { inner };
248 (avail, avail_tx)
249 }
250
251 fn failed(&self) -> bool {
252 self.inner.failed.load(Ordering::Acquire)
253 }
254
255 fn available(&self) -> bool {
256 self.inner.available.load(Ordering::Acquire)
257 }
258
259 async fn wait_for_update(&self) {
260 poll_fn(|cx| {
261 self.inner.waker.register(cx.waker());
262 if self.inner.updated.swap(false, Ordering::AcqRel) {
263 Poll::Ready(())
264 } else {
265 Poll::Pending
266 }
267 })
268 .await;
269 }
270}
271
272impl WorkerAvailabilityTx {
273 fn set(&self, val: bool) {
274 let old = self.inner.available.swap(val, Ordering::Release);
275 if old != val {
276 self.inner.updated.store(true, Ordering::Release);
277 self.inner.waker.wake();
278 }
279 }
280}
281
282impl Drop for WorkerAvailabilityTx {
283 fn drop(&mut self) {
284 self.inner.failed.store(true, Ordering::Release);
285 self.inner.updated.store(true, Ordering::Release);
286 self.inner.available.store(false, Ordering::Release);
287 if !std::thread::panicking() {
290 self.inner.waker.wake();
291 }
292 }
293}
294
295struct ServiceRunner<F: ServerConfiguration<Item = Req>, Req> {
299 name: String,
300 factory: F,
301 svc: Pipeline<Req, (), ()>,
302 reqs: Receiver<Req>,
303 stop: Pin<Box<dyn Stream<Item = Shutdown>>>,
304 availability: WorkerAvailabilityTx,
305}
306
307impl<F, Req> ServiceRunner<F, Req>
308where
309 Req: Send + 'static,
310 F: ServerConfiguration<Item = Req> + 'static,
311{
312 async fn create(
313 name: &str,
314 factory: F,
315 reqs: Receiver<Req>,
316 stop: Receiver<Shutdown>,
317 availability: WorkerAvailabilityTx,
318 ) -> Result<Self, ()> {
319 availability.set(false);
320 let mut stop = Box::pin(stop);
321
322 let svc = match select(factory.create(), stream_recv(&mut stop)).await {
323 Either::Left(Ok(svc)) => Pipeline::new((), svc),
324 Either::Right(Some(Shutdown { result, .. })) => {
325 log::trace!("Shutdown uninitialized worker");
326 let _ = result.send(false);
327 return Err(());
328 }
329 Either::Left(Err(_)) | Either::Right(None) => return Err(()),
330 };
331 availability.set(true);
332
333 Ok(ServiceRunner {
334 factory,
335 svc,
336 reqs,
337 stop,
338 availability,
339 name: name.to_string(),
340 })
341 }
342
343 async fn run(mut self) {
344 loop {
345 let mut recv = std::pin::pin!(self.reqs.recv());
346 let fut = poll_fn(|cx| {
347 match self.svc.poll_ready(cx) {
348 Poll::Ready(Ok(())) => {
349 self.availability.set(true);
350 }
351 Poll::Ready(Err(err)) => {
352 self.availability.set(false);
353 return Poll::Ready(Err(err));
354 }
355 Poll::Pending => {
356 self.availability.set(false);
357 return Poll::Pending;
358 }
359 }
360
361 if let Ok(item) = ready!(recv.as_mut().poll(cx)) {
362 Poll::Ready(Ok(Some(item)))
363 } else {
364 log::error!("Server is gone");
365 Poll::Ready(Ok(None))
366 }
367 });
368
369 match select(fut, stream_recv(&mut self.stop)).await {
370 Either::Left(Ok(Some(item))) => {
371 let _ = self.svc.call(item).await;
373 continue;
374 }
375 Either::Left(Err(())) => {
376 ntex_rt::spawn(async move {
378 self.svc.shutdown().await;
379 });
380 }
381 Either::Right(Some(Shutdown { timeout, result })) => {
382 log::info!("Shutting down {:?} worker gracefuly", self.name);
383 self.availability.set(false);
384
385 let timeout = if timeout.is_zero() { STOP_TIMEOUT } else { timeout };
386 self.stop(timeout, Some(result)).await;
387 return;
388 }
389 Either::Left(Ok(None)) | Either::Right(None) => {
390 log::info!("Shutting down {:?} worker", self.name);
391 self.availability.set(false);
392 self.stop(STOP_TIMEOUT, None).await;
393 return;
394 }
395 }
396
397 loop {
399 match select(self.factory.create(), stream_recv(&mut self.stop)).await {
400 Either::Left(Ok(service)) => {
401 self.svc = Pipeline::new((), service);
402 break;
403 }
404 Either::Left(Err(_)) => sleep(Millis::ONE_SEC).await,
405 Either::Right(_) => return,
406 }
407 }
408 }
409 }
410
411 async fn stop(&self, timeout: Millis, result: Option<oneshot::Sender<bool>>) {
412 let res = timeout_checked(timeout, self.svc.shutdown()).await;
413 if let Some(result) = result {
414 let _ = result.send(res.is_ok());
415 }
416
417 log::info!("Worker {:?} has been stopped", self.name);
418 }
419}