Skip to main content

ntex_server/
server.rs

1#![allow(clippy::missing_panics_doc)]
2use std::sync::{Arc, atomic::AtomicU64, atomic::Ordering};
3use std::task::{Context, Poll, ready};
4use std::{future::Future, io, pin::Pin};
5
6use async_channel::Sender;
7use event_listener::Event;
8use ntex_rt::signals::Signal;
9
10use crate::manager::{Rejected, Routing, ServerCommand};
11use crate::{ServerStatus, metrics::Metrics};
12
13#[derive(Debug)]
14pub(crate) struct ServerShared {
15    /// Change counter in the high bits, [`ServerStatus`] in the low byte.
16    status: AtomicU64,
17    status_event: Event,
18    pub(crate) metrics: Metrics,
19}
20
21impl ServerShared {
22    pub(crate) fn new(metrics: Metrics) -> Self {
23        ServerShared {
24            status: AtomicU64::new(u64::from(ServerStatus::Starting.as_u8())),
25            status_event: Event::new(),
26            metrics,
27        }
28    }
29
30    pub(crate) fn status(&self) -> ServerStatus {
31        status(self.status.load(Ordering::Acquire))
32    }
33
34    /// Sets the status and wakes the waiters if the status has changed.
35    ///
36    /// Only the server's command loop changes the status.
37    pub(crate) fn set_status(&self, st: ServerStatus) {
38        let cur = self.status.load(Ordering::Acquire);
39        if status(cur) != st {
40            let next = ((cur >> 8).wrapping_add(1) << 8) | u64::from(st.as_u8());
41            self.status.store(next, Ordering::Release);
42            self.status_event.notify(usize::MAX);
43        }
44    }
45}
46
47fn status(val: u64) -> ServerStatus {
48    ServerStatus::from_u8(val.to_le_bytes()[0])
49}
50
51/// Controller and completion future for a running server.
52///
53/// Clones can pause, resume, stop, or submit items to the server. Awaiting a
54/// `Server` resolves when the server has stopped. It always resolves to
55/// `Ok(())`.
56#[derive(Debug)]
57pub struct Server<T> {
58    shared: Arc<ServerShared>,
59    router: Arc<Routing<T>>,
60    cmd: Sender<ServerCommand<T>>,
61    stop: Option<oneshot::AsyncReceiver<()>>,
62}
63
64impl Server<crate::net::Connection> {
65    /// Creates a network server builder with no application configuration.
66    pub fn builder() -> crate::net::ServerBuilder {
67        crate::net::ServerBuilder::default()
68    }
69}
70
71impl<T> Server<T> {
72    pub(crate) fn new(
73        cmd: Sender<ServerCommand<T>>,
74        shared: Arc<ServerShared>,
75        router: Arc<Routing<T>>,
76    ) -> Self {
77        Server {
78            cmd,
79            shared,
80            router,
81            stop: None,
82        }
83    }
84
85    pub(crate) fn metrics_ref(&self) -> &Metrics {
86        &self.shared.metrics
87    }
88
89    /// Returns a snapshot of the server metrics.
90    pub fn metrics(&self) -> crate::ServerMetrics {
91        self.shared.metrics.snapshot()
92    }
93
94    pub(crate) fn signal(&self, sig: Signal) {
95        let _ = self.cmd.try_send(ServerCommand::Signal(sig));
96    }
97
98    /// Submits an item to the worker pool.
99    ///
100    /// The worker is chosen on the calling thread, see
101    /// [`LoadBalancing`](crate::LoadBalancing).
102    ///
103    /// Returns the item unchanged if the server status is not
104    /// [`Ready`](ServerStatus::Ready) or the server cannot accept it, so
105    /// items submitted right after start or while no worker is available are
106    /// rejected.
107    pub fn process(&self, item: T) -> Result<(), T> {
108        self.process_with(item, |item| self.router.incoming_cpu(item))
109    }
110
111    /// Submits an item like [`process`](Self::process), `cpu` returns the
112    /// incoming CPU of the item instead of the configuration's callback.
113    ///
114    /// Unlike the callback, `cpu` may run concurrently on several threads.
115    pub(crate) fn process_with(
116        &self,
117        item: T,
118        cpu: impl FnOnce(&T) -> Option<usize>,
119    ) -> Result<(), T> {
120        if self.shared.status() != ServerStatus::Ready {
121            return Err(item);
122        }
123        let item = match self.router.dispatch_with(item, &self.shared.metrics, cpu) {
124            Ok(()) => return Ok(()),
125            // the server started stopping after the status check
126            Err(Rejected::Closed(item)) => return Err(item),
127            Err(Rejected::NoWorker(item)) => item,
128        };
129        // no worker is available, the command loop queues the item
130        if let Err(e) = self.cmd.try_send(ServerCommand::Item(item)) {
131            if let ServerCommand::Item(item) = e.into_inner() {
132                Err(item)
133            } else {
134                panic!()
135            }
136        } else {
137            Ok(())
138        }
139    }
140
141    /// Returns the current server status.
142    pub fn status(&self) -> ServerStatus {
143        self.shared.status()
144    }
145
146    /// Waits for the server status to change and returns the new status.
147    ///
148    /// The status is compared with the status at the time of this call, so
149    /// changes made before the future is polled are not missed. Changes in
150    /// quick succession may be reported once, with the latest status.
151    ///
152    /// Resolves immediately with [`Stopped`](ServerStatus::Stopped) if the
153    /// server has stopped.
154    pub fn status_changed(&self) -> impl Future<Output = ServerStatus> + use<T> {
155        let shared = self.shared.clone();
156        let start = shared.status.load(Ordering::Acquire);
157        async move {
158            loop {
159                let cur = shared.status.load(Ordering::Acquire);
160                if cur != start || status(cur) == ServerStatus::Stopped {
161                    return status(cur);
162                }
163                let listener = shared.status_event.listen();
164                let cur = shared.status.load(Ordering::Acquire);
165                if cur != start {
166                    return status(cur);
167                }
168                listener.await;
169            }
170        }
171    }
172
173    /// Pauses processing new items.
174    ///
175    /// For network servers, listeners stop accepting connections, so new
176    /// connections wait in the kernel listen backlog. Existing connections
177    /// remain active.
178    ///
179    /// The server stays [`Paused`](ServerStatus::Paused) until
180    /// [`resume`](Self::resume) is called.
181    pub fn pause(&self) -> impl Future<Output = ()> + use<T> {
182        let (tx, rx) = oneshot::channel();
183        let _ = self.cmd.try_send(ServerCommand::Pause(tx));
184        async move {
185            let _ = rx.await;
186        }
187    }
188
189    /// Resumes processing new items.
190    ///
191    /// The server becomes [`Ready`](ServerStatus::Ready) once a worker is
192    /// available.
193    pub fn resume(&self) -> impl Future<Output = ()> + use<T> {
194        let (tx, rx) = oneshot::channel();
195        let _ = self.cmd.try_send(ServerCommand::Resume(tx));
196        async move {
197            let _ = rx.await;
198        }
199    }
200
201    /// Stops processing new items and shuts down all workers.
202    ///
203    /// If `graceful` is `true`, workers are given up to the graceful shutdown
204    /// timeout to finish active work before they are stopped. A zero timeout
205    /// makes the stop non-graceful.
206    ///
207    /// The returned future resolves once the stop has completed. If the
208    /// server has already stopped, it resolves immediately.
209    pub fn stop(&self, graceful: bool) -> impl Future<Output = ()> + use<T> {
210        let (tx, rx) = oneshot::channel();
211        let _ = self.cmd.try_send(ServerCommand::Stop {
212            graceful,
213            completion: Some(tx),
214        });
215        async move {
216            let _ = rx.await;
217        }
218    }
219}
220
221impl<T> Clone for Server<T> {
222    fn clone(&self) -> Self {
223        Self {
224            cmd: self.cmd.clone(),
225            shared: self.shared.clone(),
226            router: self.router.clone(),
227            stop: None,
228        }
229    }
230}
231
232impl<T> Future for Server<T> {
233    type Output = io::Result<()>;
234
235    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
236        let this = self.get_mut();
237
238        if this.stop.is_none() {
239            let (tx, rx) = oneshot::async_channel();
240            if this.cmd.try_send(ServerCommand::NotifyStopped(tx)).is_err() {
241                return Poll::Ready(Ok(()));
242            }
243            this.stop = Some(rx);
244        }
245
246        let _ = ready!(Pin::new(this.stop.as_mut().unwrap()).poll(cx));
247
248        Poll::Ready(Ok(()))
249    }
250}