Skip to main content

zlink_core/server/
mod.rs

1pub(crate) mod listener;
2mod select_all;
3pub mod service;
4
5mod infallible;
6
7use alloc::vec::Vec;
8use futures_util::{FutureExt, StreamExt};
9use select_all::SelectAll;
10use service::MethodReply;
11
12use crate::{Call, Connection, Reply, connection::Socket};
13
14/// A server.
15///
16/// The server listens for incoming connections and handles method calls using a service.
17#[derive(Debug)]
18pub struct Server<Listener, Service> {
19    listener: Option<Listener>,
20    service: Service,
21}
22
23impl<Listener, Service> Server<Listener, Service>
24where
25    Listener: listener::Listener,
26    Service: service::Service<Listener::Socket>,
27{
28    /// Create a new server that serves `service` to incoming connections from `listener`.
29    pub fn new(listener: Listener, service: Service) -> Self {
30        Self {
31            listener: Some(listener),
32            service,
33        }
34    }
35
36    /// Run the server.
37    ///
38    /// # Caveats
39    ///
40    /// Due to [a bug in the rust compiler][abrc], the future returned by this method can not be
41    /// treated as `Send`, even if all the specific types involved are `Send`. A major consequence
42    /// of this fact unfortunately, is that it can not be spawned in a task of a multi-threaded
43    /// runtime. For example, you can not currently do `tokio::spawn(server.run())`.
44    ///
45    /// Fortunately, there are easy workarounds for this. You can either:
46    ///
47    /// * Use a thread-local runtime (for example [`tokio::runtime::LocalRuntime`] or
48    ///   [`tokio::task::LocalSet`]) to run the server in a local task, perhaps in a separate
49    ///   thread.
50    /// * Use some common API to run multiple futures at once, such as [`futures::select!`] or
51    ///   [`tokio::select!`].
52    ///
53    /// Most importantly, this is most likely a temporary issue and will be fixed in the future. 😊
54    ///
55    /// [abrc]: https://github.com/rust-lang/rust/issues/100013
56    /// [`tokio::runtime::LocalRuntime`]: https://docs.rs/tokio/latest/tokio/runtime/struct.LocalRuntime.html
57    /// [`tokio::task::LocalSet`]: https://docs.rs/tokio/latest/tokio/task/struct.LocalSet.html
58    /// [`futures::select!`]: https://docs.rs/futures/latest/futures/macro.select.html
59    /// [`tokio::select!`]: https://docs.rs/tokio/latest/tokio/macro.select.html
60    pub async fn run(mut self) -> crate::Result<()> {
61        let mut listener = self.listener.take().unwrap();
62        let mut connections = Vec::new();
63        let mut reply_streams = Vec::<ReplyStream<Service::ReplyStream, Listener::Socket>>::new();
64        let mut reply_stream_futures = Vec::new();
65        // Vec for futures from `Connection::receive_call`. Reused across iterations to avoid
66        // per-iteration allocations.
67        let mut read_futures = Vec::new();
68        let mut last_reply_stream_winner = None;
69        let mut last_method_call_winner = None;
70        let mut listener_closed = false;
71
72        loop {
73            if listener_closed && connections.is_empty() && reply_streams.is_empty() {
74                return Ok(());
75            }
76
77            // We re-populate the `reply_stream_futures` in each iteration so we must clear it
78            // first.
79            reply_stream_futures.clear();
80            {
81                // SAFETY: Rust has no way to know that we don't re-use the mutable references in
82                // each iteration (since we clear the `reply_stream_futures` vector) so we need to
83                // go through a pointer to work around this.
84                let reply_streams: &mut Vec<ReplyStream<Service::ReplyStream, Listener::Socket>> =
85                    unsafe { &mut *(&mut reply_streams as *mut Vec<_>) };
86                reply_stream_futures.extend(reply_streams.iter_mut().map(|s| s.stream.next()));
87            }
88            let start_index = last_reply_stream_winner.map(|idx| idx + 1);
89            let mut reply_stream_select_all = SelectAll::new(start_index);
90            for future in reply_stream_futures.iter_mut() {
91                reply_stream_select_all.push(future);
92            }
93
94            // Prepare futures for reading method calls from connections.
95            read_futures.clear();
96            {
97                // SAFETY: Same as above - mutable references are not reused across iterations.
98                let connections: &mut Vec<Connection<Listener::Socket>> =
99                    unsafe { &mut *(&mut connections as *mut Vec<_>) };
100                read_futures.extend(connections.iter_mut().map(|c| c.receive_call()));
101            }
102            let mut read_select_all = SelectAll::new(last_method_call_winner.map(|idx| idx + 1));
103            for future in &mut read_futures {
104                // SAFETY: Futures in `read_futures` are dropped in place via `clear()` at the
105                // start of the next iteration, never moved while pinned.
106                unsafe {
107                    read_select_all.push_unchecked(future);
108                }
109            }
110
111            futures_util::select_biased! {
112                // 1. Accept a new connection.
113                conn = listener.accept().fuse() => match conn? {
114                    Some(conn) => connections.push(conn),
115                    None => listener_closed = true,
116                },
117                // 2. Read method calls from the existing connections and handle them.
118                (idx, result) = read_select_all.fuse() => {
119                        #[cfg(feature = "std")]
120                        let (call, fds) = match result {
121                            Ok((call, fds)) => (Ok(call), fds),
122                            Err(e) => (Err(e), alloc::vec![]),
123                        };
124                        #[cfg(not(feature = "std"))]
125                        let call = result;
126                        last_method_call_winner = Some(idx);
127
128                        let mut stream = None;
129                        let mut remove = true;
130                        match call {
131                            Ok(call) => {
132                                #[cfg(feature = "std")]
133                                let result =
134                                    self.handle_call(call, &mut connections[idx], fds).await;
135                                #[cfg(not(feature = "std"))]
136                                let result =
137                                    self.handle_call(call, &mut connections[idx]).await;
138                                match result {
139                                    Ok(None) => remove = false,
140                                    Ok(Some(s)) => stream = Some(s),
141                                    Err(e) => warn!("Error writing to connection: {:?}", e),
142                                }
143                            }
144                            Err(e) => debug!("Error reading from socket: {:?}", e),
145                        }
146
147                        if stream.is_some() || remove {
148                            let conn = connections.swap_remove(idx);
149
150                            if let Some(stream) = stream {
151                                reply_streams.push(ReplyStream::new(stream, conn));
152                            }
153                        }
154                }
155                // 3. Read replies from the reply streams and send them off.
156                reply = reply_stream_select_all.fuse() => {
157                    let (idx, item) = reply;
158                    last_reply_stream_winner = Some(idx);
159                    let id = reply_streams[idx].conn.id();
160
161                    match item {
162                        Some(item) => {
163                            #[cfg(feature = "std")]
164                            let (item, fds) = item;
165                            #[cfg(feature = "std")]
166                            let send_result = match item {
167                                Ok(reply) => reply_streams[idx]
168                                    .conn
169                                    .send_reply(&reply, fds)
170                                    .await,
171                                Err(error) => reply_streams[idx]
172                                    .conn
173                                    .send_error(&error, fds)
174                                    .await,
175                            };
176                            #[cfg(not(feature = "std"))]
177                            let send_result = match item {
178                                Ok(reply) => reply_streams[idx].conn.send_reply(&reply).await,
179                                Err(error) => reply_streams[idx].conn.send_error(&error).await,
180                            };
181                            if let Err(e) = send_result {
182                                warn!("Error writing to client {}: {:?}", id, e);
183                                reply_streams.swap_remove(idx);
184                            }
185                        }
186                        None => {
187                            trace!("Stream closed for client {}", id);
188                            let stream = reply_streams.swap_remove(idx);
189                            connections.push(stream.conn);
190                        }
191                    }
192                }
193            }
194        }
195    }
196
197    async fn handle_call(
198        &mut self,
199        call: Call<Service::MethodCall<'_>>,
200        conn: &mut Connection<Listener::Socket>,
201        #[cfg(feature = "std")] fds: Vec<std::os::fd::OwnedFd>,
202    ) -> crate::Result<Option<Service::ReplyStream>> {
203        let mut stream = None;
204
205        #[cfg(feature = "std")]
206        let (reply, reply_fds) = self.service.handle(&call, conn, fds).await;
207        #[cfg(not(feature = "std"))]
208        let reply = self.service.handle(&call, conn).await;
209
210        match reply {
211            // Don't send replies or errors for oneway calls.
212            MethodReply::Single(_) | MethodReply::Error(_) if call.oneway() => (),
213            MethodReply::Single(params) => {
214                let reply = Reply::new(params).set_continues(Some(false));
215                #[cfg(feature = "std")]
216                conn.send_reply(&reply, reply_fds).await?;
217                #[cfg(not(feature = "std"))]
218                conn.send_reply(&reply).await?;
219            }
220            #[cfg(feature = "std")]
221            MethodReply::Error(err) => conn.send_error(&err, reply_fds).await?,
222            #[cfg(not(feature = "std"))]
223            MethodReply::Error(err) => conn.send_error(&err).await?,
224            MethodReply::Multi(s) => {
225                trace!("Client {} now turning into a reply stream", conn.id());
226                stream = Some(s)
227            }
228        }
229
230        Ok(stream)
231    }
232}
233
234/// Method reply stream and connection pair.
235#[derive(Debug)]
236struct ReplyStream<St, Sock: Socket> {
237    stream: St,
238    conn: Connection<Sock>,
239}
240
241impl<St, Sock> ReplyStream<St, Sock>
242where
243    Sock: Socket,
244{
245    fn new(stream: St, conn: Connection<Sock>) -> Self {
246        Self { stream, conn }
247    }
248}