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                unsafe { self.poller.add(&info.sock, Event::readable(idx)) }
471            };
472            if let Err(err) = result {
473                if err.kind() == io::ErrorKind::WouldBlock {
474                    continue;
475                }
476                log::error!("Cannot register socket listener: {err}");
477
478                // sleep after error
479                info.timeout.set(Some(Instant::now() + ERR_TIMEOUT));
480
481                let notify = self.notify.clone();
482                // timers are thread-local, create the sleep on the system thread
483                System::current().handle().spawn_detached(async move {
484                    ntex_rt::spawn_detached(async move {
485                        sleep(ERR_SLEEP_TIMEOUT).await;
486                        notify.send(Command::Timer);
487                    });
488                });
489            } else {
490                info.registered.set(true);
491            }
492
493            break;
494        }
495    }
496
497    fn remove_source(&self, key: usize) {
498        let info = &self.sockets[key];
499
500        let result = if info.registered.get() {
501            self.poller.modify(&info.sock, Event::none(key))
502        } else {
503            return;
504        };
505
506        // stop listening for incoming connections
507        if let Err(err) = result {
508            log::error!("Cannot stop socket listener for {} err: {}", info.addr, err);
509        }
510    }
511
512    fn process_timer(&mut self) {
513        let now = Instant::now();
514        for key in 0..self.sockets.len() {
515            let info = &mut self.sockets[key];
516            if let Some(inst) = info.timeout.get()
517                && now > inst
518                && !self.backpressure
519            {
520                log::info!("Resuming socket listener on {} after timeout", info.addr);
521                info.timeout.take();
522                self.add_source(key);
523            }
524        }
525    }
526
527    fn process_cmd(&mut self) -> Either<(), Option<Arc<StopSignal>>> {
528        loop {
529            match self.rx.try_recv() {
530                Ok(cmd) => match cmd {
531                    Command::Stop(rx) => {
532                        if !self.backpressure {
533                            log::info!("Stopping accept loop {:?}", self.name);
534                            self.backpressure(true);
535                        }
536                        break Either::Right(Some(rx));
537                    }
538                    Command::Terminate => {
539                        log::info!("Stopping accept loop {:?}", self.name);
540                        self.backpressure(true);
541                        break Either::Right(None);
542                    }
543                    Command::Pause => {
544                        if !self.backpressure {
545                            log::info!("Pausing accept loop {:?}", self.name);
546                            self.backpressure(true);
547                        }
548                    }
549                    Command::Resume => {
550                        if self.backpressure {
551                            log::info!("Resuming accept loop {:?}", self.name);
552                            self.backpressure(false);
553                        }
554                    }
555                    Command::Timer => {
556                        self.process_timer();
557                    }
558                },
559                Err(err) => {
560                    break match err {
561                        mpsc::TryRecvError::Empty => Either::Left(()),
562                        mpsc::TryRecvError::Disconnected => {
563                            log::error!("Dropping accept loop {:?}", self.name);
564                            self.backpressure(true);
565                            Either::Right(None)
566                        }
567                    };
568                }
569            }
570        }
571    }
572
573    fn backpressure(&mut self, on: bool) {
574        if self.backpressure && !on {
575            // handle backlog
576            while let Some(msg) = self.backlog.pop_front() {
577                if let Err(msg) = self.srv.process_with(msg, Connection::incoming_cpu) {
578                    log::trace!("Server is unavailable");
579                    self.backlog.push_front(msg);
580                    self.report_backlog();
581                    return;
582                }
583            }
584            self.report_backlog();
585            self.report_paused(false);
586
587            // re-enable acceptors
588            self.backpressure = false;
589            for (key, info) in self.sockets.iter().enumerate() {
590                if info.timeout.get().is_none() {
591                    // socket with timeout will re-register itself after timeout
592                    log::info!(
593                        "Resuming socket listener on {} after back-pressure",
594                        info.addr
595                    );
596                    self.add_source(key);
597                }
598            }
599        } else if !self.backpressure && on {
600            self.backpressure = true;
601            self.report_paused(true);
602            for key in 0..self.sockets.len() {
603                // disable err timeout
604                let info = &mut self.sockets[key];
605                if info.timeout.take().is_none() {
606                    log::info!("Stopping socket listener on {}", info.addr);
607                    self.remove_source(key);
608                }
609            }
610        }
611    }
612
613    fn accept(&mut self, token: usize) -> bool {
614        loop {
615            if let Some(info) = self.sockets.get_mut(token) {
616                let idx = info.idx;
617                match info.sock.accept() {
618                    Ok(Some(io)) => {
619                        self.srv.metrics_ref().listeners([]).accepted(idx);
620                        let msg = Connection {
621                            io,
622                            token: info.token,
623                        };
624                        if let Err(msg) = self.srv.process_with(msg, Connection::incoming_cpu) {
625                            log::trace!("Server is unavailable");
626                            self.backlog.push_back(msg);
627                            self.report_backlog();
628                            self.backpressure(true);
629                            return false;
630                        }
631                    }
632                    Ok(None) => return true,
633                    Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => return true,
634                    Err(ref e) if connection_error(e) => {
635                        self.srv.metrics_ref().listeners([]).connection_error(idx);
636                    }
637                    Err(e) => {
638                        log::error!("Error accepting socket: {e}");
639                        self.srv.metrics_ref().listeners([]).resource_error(idx);
640
641                        // sleep after error
642                        info.timeout.set(Some(Instant::now() + ERR_TIMEOUT));
643
644                        let notify = self.notify.clone();
645                        // timers are thread-local, create the sleep on the system thread
646                        System::current().handle().spawn_detached(async move {
647                            ntex_rt::spawn_detached(async move {
648                                sleep(ERR_SLEEP_TIMEOUT).await;
649                                notify.send(Command::Timer);
650                            });
651                        });
652                        return false;
653                    }
654                }
655            }
656        }
657    }
658}
659
660impl Drop for Accept {
661    fn drop(&mut self) {
662        // the normal shutdown path drains the sockets, this covers panics
663        for info in &self.sockets {
664            delete_source(&self.poller, info);
665        }
666    }
667}
668
669/// Deletes a registered listener from the poller, as `Poller::add` requires
670/// before the listener is dropped.
671fn delete_source(poller: &Poller, info: &ServerSocketInfo) {
672    if info.registered.replace(false)
673        && let Err(err) = poller.delete(&info.sock)
674    {
675        log::error!(
676            "Cannot delete socket listener for {} err: {}",
677            info.addr,
678            err
679        );
680    }
681}
682
683/// This function defines errors that are per-connection. Which basically
684/// means that if we get this error from `accept()` system call it means
685/// next connection might be ready to be accepted.
686///
687/// All other errors will incur a timeout before next `accept()` is performed.
688/// The timeout is useful to handle resource exhaustion errors like ENFILE
689/// and EMFILE. Otherwise, could enter into tight loop.
690fn connection_error(e: &io::Error) -> bool {
691    matches!(
692        e.kind(),
693        io::ErrorKind::ConnectionRefused
694            | io::ErrorKind::ConnectionAborted
695            | io::ErrorKind::ConnectionReset
696    )
697}