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        if self.shared.status() != ServerStatus::Ready {
109            return Err(item);
110        }
111        let item = match self.router.dispatch(item, &self.shared.metrics) {
112            Ok(()) => return Ok(()),
113            // the server started stopping after the status check
114            Err(Rejected::Closed(item)) => return Err(item),
115            Err(Rejected::NoWorker(item)) => item,
116        };
117        // no worker is available, the command loop queues the item
118        if let Err(e) = self.cmd.try_send(ServerCommand::Item(item)) {
119            if let ServerCommand::Item(item) = e.into_inner() {
120                Err(item)
121            } else {
122                panic!()
123            }
124        } else {
125            Ok(())
126        }
127    }
128
129    /// Returns the current server status.
130    pub fn status(&self) -> ServerStatus {
131        self.shared.status()
132    }
133
134    /// Waits for the server status to change and returns the new status.
135    ///
136    /// The status is compared with the status at the time of this call, so
137    /// changes made before the future is polled are not missed. Changes in
138    /// quick succession may be reported once, with the latest status.
139    ///
140    /// Resolves immediately with [`Stopped`](ServerStatus::Stopped) if the
141    /// server has stopped.
142    pub fn status_changed(&self) -> impl Future<Output = ServerStatus> + use<T> {
143        let shared = self.shared.clone();
144        let start = shared.status.load(Ordering::Acquire);
145        async move {
146            loop {
147                let cur = shared.status.load(Ordering::Acquire);
148                if cur != start || status(cur) == ServerStatus::Stopped {
149                    return status(cur);
150                }
151                let listener = shared.status_event.listen();
152                let cur = shared.status.load(Ordering::Acquire);
153                if cur != start {
154                    return status(cur);
155                }
156                listener.await;
157            }
158        }
159    }
160
161    /// Pauses processing new items.
162    ///
163    /// For network servers, listeners stop accepting connections, so new
164    /// connections wait in the kernel listen backlog. Existing connections
165    /// remain active.
166    ///
167    /// The server stays [`Paused`](ServerStatus::Paused) until
168    /// [`resume`](Self::resume) is called.
169    pub fn pause(&self) -> impl Future<Output = ()> + use<T> {
170        let (tx, rx) = oneshot::channel();
171        let _ = self.cmd.try_send(ServerCommand::Pause(tx));
172        async move {
173            let _ = rx.await;
174        }
175    }
176
177    /// Resumes processing new items.
178    ///
179    /// The server becomes [`Ready`](ServerStatus::Ready) once a worker is
180    /// available.
181    pub fn resume(&self) -> impl Future<Output = ()> + use<T> {
182        let (tx, rx) = oneshot::channel();
183        let _ = self.cmd.try_send(ServerCommand::Resume(tx));
184        async move {
185            let _ = rx.await;
186        }
187    }
188
189    /// Stops processing new items and shuts down all workers.
190    ///
191    /// If `graceful` is `true`, workers are given up to the graceful shutdown
192    /// timeout to finish active work before they are stopped. A zero timeout
193    /// makes the stop non-graceful.
194    ///
195    /// The returned future resolves once the stop has completed. If the
196    /// server has already stopped, it resolves immediately.
197    pub fn stop(&self, graceful: bool) -> impl Future<Output = ()> + use<T> {
198        let (tx, rx) = oneshot::channel();
199        let _ = self.cmd.try_send(ServerCommand::Stop {
200            graceful,
201            completion: Some(tx),
202        });
203        async move {
204            let _ = rx.await;
205        }
206    }
207}
208
209impl<T> Clone for Server<T> {
210    fn clone(&self) -> Self {
211        Self {
212            cmd: self.cmd.clone(),
213            shared: self.shared.clone(),
214            router: self.router.clone(),
215            stop: None,
216        }
217    }
218}
219
220impl<T> Future for Server<T> {
221    type Output = io::Result<()>;
222
223    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
224        let this = self.get_mut();
225
226        if this.stop.is_none() {
227            let (tx, rx) = oneshot::async_channel();
228            if this.cmd.try_send(ServerCommand::NotifyStopped(tx)).is_err() {
229                return Poll::Ready(Ok(()));
230            }
231            this.stop = Some(rx);
232        }
233
234        let _ = ready!(Pin::new(this.stop.as_mut().unwrap()).poll(cx));
235
236        Poll::Ready(Ok(()))
237    }
238}