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