Skip to main content

ntex_server/net/
accept.rs

1#![allow(clippy::missing_panics_doc)]
2use std::sync::{Arc, mpsc};
3use std::time::{Duration, Instant};
4use std::{cell::Cell, fmt, io, thread};
5use std::{collections::VecDeque, num::NonZeroUsize};
6
7use ntex_polling::{Event, Events, Notifier, Poller};
8use ntex_rt::System;
9use ntex_util::{future::Either, time::Millis, time::sleep};
10use parking_lot::Mutex;
11
12use super::socket::{Connection, Listener, SocketAddr};
13use super::{Server, Token};
14use crate::metrics::AcceptSlot;
15
16const EXIT_TIMEOUT: Duration = Duration::from_millis(100);
17const ERR_TIMEOUT: Duration = Duration::from_millis(500);
18const ERR_SLEEP_TIMEOUT: Millis = Millis(525);
19const DEFAULT_BACKLOG: i32 = 2048;
20
21/// Command sent to the accept loop.
22#[derive(Debug)]
23pub enum AcceptorCommand {
24    /// Stops accepting and closes the listeners, then signals the sender.
25    Stop(oneshot::Sender<()>),
26    /// Stops accepting and closes the listeners immediately.
27    Terminate,
28    /// Stops accepting connections.
29    Pause,
30    /// Resumes accepting connections.
31    Resume,
32    /// Re-registers listeners that were paused after an accept error.
33    Timer,
34}
35
36/// Command delivered to a single accept thread.
37#[derive(Debug, Clone)]
38enum Command {
39    Stop(Arc<StopSignal>),
40    Terminate,
41    Pause,
42    Resume,
43    Timer,
44}
45
46/// Completes a stop request once every accept thread has released it.
47#[derive(Debug)]
48struct StopSignal(Option<oneshot::Sender<()>>);
49
50impl Drop for StopSignal {
51    fn drop(&mut self) {
52        if let Some(tx) = self.0.take() {
53            let _ = tx.send(());
54        }
55    }
56}
57
58#[derive(Debug)]
59struct ServerSocketInfo {
60    addr: SocketAddr,
61    token: Token,
62    sock: Listener,
63    /// Listener index used for metrics, shared by replicated sockets.
64    idx: usize,
65    registered: Cell<bool>,
66    timeout: Cell<Option<Instant>>,
67}
68
69/// Command channel of a single accept thread.
70#[derive(Debug, Clone)]
71struct AcceptHandle(Notifier, mpsc::Sender<Command>);
72
73impl AcceptHandle {
74    fn send(&self, cmd: Command) {
75        let _ = self.1.send(cmd);
76        let _ = self.0.notify();
77    }
78}
79
80/// Handle for sending commands to the accept loop.
81///
82/// Commands are delivered to every accept thread.
83#[derive(Debug, Clone)]
84pub struct AcceptNotify(Arc<Mutex<Vec<AcceptHandle>>>);
85
86impl AcceptNotify {
87    /// Sends a command and wakes the accept loop.
88    ///
89    /// A `Stop` command is signaled after every accept thread has stopped.
90    pub fn send(&self, cmd: AcceptorCommand) {
91        let handles = self.0.lock().clone();
92        let cmd = match cmd {
93            AcceptorCommand::Stop(tx) => Command::Stop(Arc::new(StopSignal(Some(tx)))),
94            AcceptorCommand::Terminate => Command::Terminate,
95            AcceptorCommand::Pause => Command::Pause,
96            AcceptorCommand::Resume => Command::Resume,
97            AcceptorCommand::Timer => Command::Timer,
98        };
99        for h in &handles {
100            h.send(cmd.clone());
101        }
102    }
103}
104
105/// Accept loop for network listeners.
106///
107/// The loop runs on its own threads and passes accepted connections to the
108/// server.
109pub struct AcceptLoop {
110    name: String,
111    testing: bool,
112    backlog: i32,
113    notify: AcceptNotify,
114    inner: Vec<(mpsc::Receiver<Command>, Poller)>,
115}
116
117impl Default for AcceptLoop {
118    fn default() -> Self {
119        Self::new()
120    }
121}
122
123impl AcceptLoop {
124    /// Creates an accept loop with one accept thread.
125    ///
126    /// # Panics
127    ///
128    /// Panics if the poller cannot be created.
129    pub fn new() -> AcceptLoop {
130        let mut slf = AcceptLoop {
131            notify: AcceptNotify(Arc::new(Mutex::new(Vec::new()))),
132            name: "ntex:accept".to_string(),
133            inner: Vec::new(),
134            testing: false,
135            backlog: DEFAULT_BACKLOG,
136        };
137        slf.add_acceptor();
138        slf
139    }
140
141    fn add_acceptor(&mut self) {
142        let poll = Poller::new()
143            .map_err(|e| panic!("Cannot create Poller {e}"))
144            .unwrap();
145        let (tx, rx) = mpsc::channel();
146        self.notify.0.lock().push(AcceptHandle(poll.notifier(), tx));
147        self.inner.push((rx, poll));
148    }
149
150    /// Sets the server name.
151    ///
152    /// The first accept thread is named `{name}:accept`, additional threads
153    /// are named `{name}:accept{n}`.
154    pub fn name<T: AsRef<str>>(&mut self, name: T) {
155        self.name = format!("{}:accept", name.as_ref());
156    }
157
158    /// Sets the number of accept threads.
159    ///
160    /// Every TCP listener is replicated with `SO_REUSEPORT`, so each accept
161    /// thread has its own socket for the same address and the kernel spreads
162    /// incoming connections between them. Unix domain listeners are served
163    /// by the first thread only.
164    ///
165    /// Multiple accept threads are supported on Linux and Android. On other
166    /// platforms, a single thread is used.
167    ///
168    /// # Panics
169    ///
170    /// Panics if a poller cannot be created.
171    pub fn acceptors(&mut self, num: usize) {
172        let num = if cfg!(any(target_os = "linux", target_os = "android")) {
173            num.max(1)
174        } else {
175            if num > 1 {
176                log::warn!("Multiple accept threads are not supported on this platform");
177            }
178            1
179        };
180        while self.inner.len() < num {
181            self.add_acceptor();
182        }
183        self.inner.truncate(num);
184        self.notify.0.lock().truncate(num);
185    }
186
187    /// Sets the listen backlog for replicated listeners.
188    pub(crate) fn backlog(&mut self, num: i32) {
189        self.backlog = num;
190    }
191
192    /// Returns a handle for sending commands to the loop.
193    pub fn notify(&self) -> AcceptNotify {
194        self.notify.clone()
195    }
196
197    /// Enables test mode, which skips the exit delay on stop.
198    pub fn testing(&mut self) {
199        self.testing = true;
200    }
201
202    /// Starts the accept loop on new threads.
203    ///
204    /// Blocks until the listeners are registered.
205    #[allow(clippy::needless_pass_by_value)]
206    pub fn start(self, socks: Vec<(Token, Listener)>, srv: Server) {
207        // the builder registers named listeners, others only have an address
208        srv.metrics_ref().listeners(
209            socks
210                .iter()
211                .map(|(_, lst)| (String::new(), lst.local_addr().to_string())),
212        );
213
214        let num = self.inner.len();
215        let mut groups: Vec<Vec<(usize, Token, Listener)>> = (0..num).map(|_| Vec::new()).collect();
216        for (idx, (token, lst)) in socks.into_iter().enumerate() {
217            if num > 1 {
218                for (n, lst) in replicate(&lst, num - 1, self.backlog)
219                    .into_iter()
220                    .enumerate()
221                {
222                    groups[n + 1].push((idx, token, lst));
223                }
224            }
225            groups[0].push((idx, token, lst));
226        }
227
228        let handles = self.notify.0.lock().clone();
229        let mut started = Vec::new();
230        for (n, ((rx, poll), socks)) in self.inner.into_iter().zip(groups).enumerate() {
231            // the first thread always runs, others only serve replicated sockets
232            if n > 0 && socks.is_empty() {
233                continue;
234            }
235            let name = if n == 0 {
236                self.name.clone()
237            } else {
238                format!("{}{n}", self.name)
239            };
240            let (tx, rx_start) = oneshot::channel();
241            Accept::start(
242                tx,
243                rx,
244                poll,
245                handles[n].clone(),
246                socks,
247                srv.clone(),
248                name,
249                self.testing,
250            );
251            started.push(rx_start);
252        }
253
254        for rx in started {
255            let _ = rx.recv();
256        }
257    }
258}
259
260/// Creates `num` more listeners bound to the address of `lst`.
261///
262/// `SO_REUSEPORT` is enabled on `lst` and on the new listeners, so the
263/// kernel balances incoming connections between all of them.
264fn replicate(lst: &Listener, num: usize, backlog: i32) -> Vec<Listener> {
265    #[cfg(any(target_os = "linux", target_os = "android"))]
266    if let Listener::Tcp(lst) = lst {
267        use socket2::{Domain, SockAddr, SockRef, Socket, Type};
268
269        let create = || -> io::Result<std::net::TcpListener> {
270            let addr = lst.local_addr()?;
271            let sock = Socket::new(Domain::for_address(addr), Type::STREAM, None)?;
272            sock.set_reuse_address(true)?;
273            sock.set_reuse_port(true)?;
274            if addr.is_ipv6() {
275                sock.set_only_v6(SockRef::from(lst).only_v6()?)?;
276            }
277            sock.bind(&SockAddr::from(addr))?;
278            sock.listen(backlog)?;
279            Ok(sock.into())
280        };
281
282        if let Err(e) = SockRef::from(lst).set_reuse_port(true) {
283            log::warn!("Cannot enable SO_REUSEPORT for {lst:?}: {e}");
284            return Vec::new();
285        }
286        let mut items = Vec::with_capacity(num);
287        for _ in 0..num {
288            match create() {
289                Ok(item) => items.push(Listener::from_tcp(item)),
290                Err(e) => {
291                    log::warn!("Cannot replicate listener {lst:?}: {e}");
292                    break;
293                }
294            }
295        }
296        return items;
297    }
298    let _ = (lst, num, backlog);
299    Vec::new()
300}
301
302impl fmt::Debug for AcceptLoop {
303    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
304        f.debug_struct("AcceptLoop")
305            .field("name", &self.name)
306            .field("acceptors", &self.inner.len())
307            .field("notify", &self.notify)
308            .finish()
309    }
310}
311
312struct Accept {
313    name: String,
314    poller: Poller,
315    rx: mpsc::Receiver<Command>,
316    tx: Option<oneshot::Sender<()>>,
317    sockets: Vec<ServerSocketInfo>,
318    srv: Server,
319    notify: AcceptHandle,
320    testing: bool,
321    backpressure: bool,
322    backlog: VecDeque<Connection>,
323    /// Backlog length last added to the shared metrics.
324    backlog_reported: usize,
325    /// Whether this thread is counted as paused in the shared metrics.
326    paused_reported: bool,
327}
328
329impl Accept {
330    #[allow(clippy::too_many_arguments)]
331    fn start(
332        tx: oneshot::Sender<()>,
333        rx: mpsc::Receiver<Command>,
334        poller: Poller,
335        notify: AcceptHandle,
336        socks: Vec<(usize, Token, Listener)>,
337        srv: Server,
338        name: String,
339        testing: bool,
340    ) {
341        log::info!("Starting {name:?} accept loop");
342
343        // start accept thread
344        let sys = System::current();
345        let _ = thread::Builder::new().name(name.clone()).spawn(move || {
346            System::set_current(sys);
347            Accept::new(name, tx, rx, poller, notify, socks, srv, testing).poll();
348        });
349    }
350
351    #[allow(clippy::too_many_arguments)]
352    fn new(
353        name: String,
354        tx: oneshot::Sender<()>,
355        rx: mpsc::Receiver<Command>,
356        poller: Poller,
357        notify: AcceptHandle,
358        socks: Vec<(usize, Token, Listener)>,
359        srv: Server,
360        testing: bool,
361    ) -> Accept {
362        let sockets = socks
363            .into_iter()
364            .map(|(idx, token, sock)| ServerSocketInfo {
365                addr: sock.local_addr(),
366                sock,
367                idx,
368                token,
369                registered: Cell::new(false),
370                timeout: Cell::new(None),
371            })
372            .collect();
373
374        Accept {
375            name,
376            poller,
377            rx,
378            sockets,
379            notify,
380            srv,
381            testing,
382            tx: Some(tx),
383            backpressure: true,
384            backlog: VecDeque::new(),
385            backlog_reported: 0,
386            paused_reported: false,
387        }
388    }
389
390    fn report_paused(&mut self, paused: bool) {
391        if self.paused_reported != paused {
392            self.paused_reported = paused;
393            self.metrics().paused(paused);
394        }
395    }
396
397    fn report_backlog(&mut self) {
398        let len = self.backlog.len();
399        self.metrics().backlog(self.backlog_reported, len);
400        self.backlog_reported = len;
401    }
402
403    fn metrics(&self) -> &AcceptSlot {
404        self.srv.metrics_ref().listeners([])
405    }
406
407    fn poll(mut self) {
408        // Create storage for events
409        let mut events = Events::with_capacity(NonZeroUsize::new(512).unwrap());
410
411        // notify start
412        for idx in 0..self.sockets.len() {
413            self.add_source(idx);
414        }
415        if let Some(tx) = self.tx.take() {
416            thread::sleep(Duration::from_millis(25));
417            let _ = tx.send(());
418        }
419
420        loop {
421            for idx in 0..self.sockets.len() {
422                // paused sockets and sockets in error timeout must not be
423                // polled, a wakeup would re-register them
424                let info = &self.sockets[idx];
425                if !self.backpressure && info.registered.get() && info.timeout.get().is_none() {
426                    let readd = self.accept(idx);
427                    if readd {
428                        self.add_source(idx);
429                    }
430                }
431            }
432
433            if let Either::Right(rx) = self.process_cmd() {
434                // cleanup
435                for info in self.sockets.drain(..) {
436                    delete_source(&self.poller, &info);
437                    info.sock.remove_source();
438                }
439                log::info!("Accept loop {:?} has been stopped", self.name);
440
441                // the stop request completes when every accept thread releases it
442                if let Some(signal) = rx {
443                    if !self.testing {
444                        thread::sleep(EXIT_TIMEOUT);
445                    }
446                    drop(signal);
447                }
448
449                break;
450            }
451
452            events.clear();
453            if let Err(e) = self.poller.wait(&mut events, None) {
454                assert!(
455                    e.kind() == io::ErrorKind::Interrupted,
456                    "Cannot wait for events in poller: {e}"
457                );
458            }
459        }
460    }
461
462    fn add_source(&self, idx: usize) {
463        let info = &self.sockets[idx];
464
465        loop {
466            // try to register poller source
467            let result = if info.registered.get() {
468                self.poller.modify(&info.sock, Event::readable(idx))
469            } else {
470                // SAFETY: The listener stays in `self.sockets` and is deleted from the poller before drop.
471                unsafe { self.poller.add(&info.sock, Event::readable(idx)) }
472            };
473            if let Err(err) = result {
474                if err.kind() == io::ErrorKind::WouldBlock {
475                    continue;
476                }
477                log::error!("Cannot register socket listener: {err}");
478
479                // sleep after error
480                info.timeout.set(Some(Instant::now() + ERR_TIMEOUT));
481
482                let notify = self.notify.clone();
483                // timers are thread-local, create the sleep on the system thread
484                System::current().handle().spawn_detached(async move {
485                    ntex_rt::spawn_detached(async move {
486                        sleep(ERR_SLEEP_TIMEOUT).await;
487                        notify.send(Command::Timer);
488                    });
489                });
490            } else {
491                info.registered.set(true);
492            }
493
494            break;
495        }
496    }
497
498    fn remove_source(&self, key: usize) {
499        let info = &self.sockets[key];
500
501        let result = if info.registered.get() {
502            self.poller.modify(&info.sock, Event::none(key))
503        } else {
504            return;
505        };
506
507        // stop listening for incoming connections
508        if let Err(err) = result {
509            log::error!("Cannot stop socket listener for {} err: {}", info.addr, err);
510        }
511    }
512
513    fn process_timer(&mut self) {
514        let now = Instant::now();
515        for key in 0..self.sockets.len() {
516            let info = &mut self.sockets[key];
517            if let Some(inst) = info.timeout.get()
518                && now > inst
519                && !self.backpressure
520            {
521                log::info!("Resuming socket listener on {} after timeout", info.addr);
522                info.timeout.take();
523                self.add_source(key);
524            }
525        }
526    }
527
528    fn process_cmd(&mut self) -> Either<(), Option<Arc<StopSignal>>> {
529        loop {
530            match self.rx.try_recv() {
531                Ok(cmd) => match cmd {
532                    Command::Stop(rx) => {
533                        if !self.backpressure {
534                            log::info!("Stopping accept loop {:?}", self.name);
535                            self.backpressure(true);
536                        }
537                        break Either::Right(Some(rx));
538                    }
539                    Command::Terminate => {
540                        log::info!("Stopping accept loop {:?}", self.name);
541                        self.backpressure(true);
542                        break Either::Right(None);
543                    }
544                    Command::Pause => {
545                        if !self.backpressure {
546                            log::info!("Pausing accept loop {:?}", self.name);
547                            self.backpressure(true);
548                        }
549                    }
550                    Command::Resume => {
551                        if self.backpressure {
552                            log::info!("Resuming accept loop {:?}", self.name);
553                            self.backpressure(false);
554                        }
555                    }
556                    Command::Timer => {
557                        self.process_timer();
558                    }
559                },
560                Err(err) => {
561                    break match err {
562                        mpsc::TryRecvError::Empty => Either::Left(()),
563                        mpsc::TryRecvError::Disconnected => {
564                            log::error!("Dropping accept loop {:?}", self.name);
565                            self.backpressure(true);
566                            Either::Right(None)
567                        }
568                    };
569                }
570            }
571        }
572    }
573
574    fn backpressure(&mut self, on: bool) {
575        if self.backpressure && !on {
576            // handle backlog
577            while let Some(msg) = self.backlog.pop_front() {
578                if let Err(msg) = self.srv.process_with(msg, Connection::incoming_cpu) {
579                    log::trace!("Server is unavailable");
580                    self.backlog.push_front(msg);
581                    self.report_backlog();
582                    return;
583                }
584            }
585            self.report_backlog();
586            self.report_paused(false);
587
588            // re-enable acceptors
589            self.backpressure = false;
590            for (key, info) in self.sockets.iter().enumerate() {
591                if info.timeout.get().is_none() {
592                    // socket with timeout will re-register itself after timeout
593                    log::info!(
594                        "Resuming socket listener on {} after back-pressure",
595                        info.addr
596                    );
597                    self.add_source(key);
598                }
599            }
600        } else if !self.backpressure && on {
601            self.backpressure = true;
602            self.report_paused(true);
603            for key in 0..self.sockets.len() {
604                // disable err timeout
605                let info = &mut self.sockets[key];
606                if info.timeout.take().is_none() {
607                    log::info!("Stopping socket listener on {}", info.addr);
608                    self.remove_source(key);
609                }
610            }
611        }
612    }
613
614    fn accept(&mut self, token: usize) -> bool {
615        loop {
616            if let Some(info) = self.sockets.get_mut(token) {
617                let idx = info.idx;
618                match info.sock.accept() {
619                    Ok(Some(io)) => {
620                        self.srv.metrics_ref().listeners([]).accepted(idx);
621                        let msg = Connection {
622                            io,
623                            token: info.token,
624                        };
625                        if let Err(msg) = self.srv.process_with(msg, Connection::incoming_cpu) {
626                            log::trace!("Server is unavailable");
627                            self.backlog.push_back(msg);
628                            self.report_backlog();
629                            self.backpressure(true);
630                            return false;
631                        }
632                    }
633                    Ok(None) => return true,
634                    Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => return true,
635                    Err(ref e) if connection_error(e) => {
636                        self.srv.metrics_ref().listeners([]).connection_error(idx);
637                    }
638                    Err(e) => {
639                        log::error!("Error accepting socket: {e}");
640                        self.srv.metrics_ref().listeners([]).resource_error(idx);
641
642                        // sleep after error
643                        info.timeout.set(Some(Instant::now() + ERR_TIMEOUT));
644
645                        let notify = self.notify.clone();
646                        // timers are thread-local, create the sleep on the system thread
647                        System::current().handle().spawn_detached(async move {
648                            ntex_rt::spawn_detached(async move {
649                                sleep(ERR_SLEEP_TIMEOUT).await;
650                                notify.send(Command::Timer);
651                            });
652                        });
653                        return false;
654                    }
655                }
656            }
657        }
658    }
659}
660
661impl Drop for Accept {
662    fn drop(&mut self) {
663        // the normal shutdown path drains the sockets, this covers panics
664        for info in &self.sockets {
665            delete_source(&self.poller, info);
666        }
667    }
668}
669
670/// Deletes a registered listener from the poller, as `Poller::add` requires
671/// before the listener is dropped.
672fn delete_source(poller: &Poller, info: &ServerSocketInfo) {
673    if info.registered.replace(false)
674        && let Err(err) = poller.delete(&info.sock)
675    {
676        log::error!(
677            "Cannot delete socket listener for {} err: {}",
678            info.addr,
679            err
680        );
681    }
682}
683
684/// This function defines errors that are per-connection. Which basically
685/// means that if we get this error from `accept()` system call it means
686/// next connection might be ready to be accepted.
687///
688/// All other errors will incur a timeout before next `accept()` is performed.
689/// The timeout is useful to handle resource exhaustion errors like ENFILE
690/// and EMFILE. Otherwise, could enter into tight loop.
691fn connection_error(e: &io::Error) -> bool {
692    matches!(
693        e.kind(),
694        io::ErrorKind::ConnectionRefused
695            | io::ErrorKind::ConnectionAborted
696            | io::ErrorKind::ConnectionReset
697    )
698}