server/
server.rs

1// SPDX-FileCopyrightText: 2025 Foundation Devices, Inc. <hello@foundation.xyz>
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4use crate::{MessageDef, MessageHandler};
5
6/// A server.
7///
8/// The KeyOS operating system uses the `xous` kernel, which is a message-passing
9/// microkernel. The messages are passed between "servers," which represent communication
10/// endpoints.
11///
12/// This trait, and it's companion, ServerMessages, provide an idiomatic way for KeyOS to implement the
13/// servers that it needs.
14/// This trait contains optional hooks, but can be left empty, the ServerMessages trait contains the actual
15/// server definition.
16pub trait Server: ServerMessages {
17    /// Hook to do stuff with the context once the server is started.
18    fn on_start(&mut self, context: &mut ServerContext<Self>) { let _ = context; }
19}
20
21/// This trait contains the actual message definitions that the server can handle.
22/// It should not be used manually, but via the `#[derive(server::Server)]` macro.
23pub trait ServerMessages: Sized {
24    /// The name of the server, as registered in the nameserver.
25    /// If empty, a random SID will be assigned and no nameserver registration happens.
26    const NAME: &'static str;
27
28    /// Define the messages that are handled by this server. See [`MessageDef`] and the
29    /// related functions.
30    fn messages() -> &'static [MessageDef<Self>];
31}
32
33/// Handle representing the running server instance
34#[derive(Debug)]
35pub struct ServerContext<S> {
36    pub sid: xous::SID,
37    pub shutdown: bool,
38    // Vec instead of Map, because for a small number of message handlers, a linear
39    // search is way faster.
40    pub(crate) handlers: Vec<(xous::MessageId, MessageHandler<S>)>,
41}
42
43impl<S> ServerContext<S> {
44    /// Shut down the server.
45    pub fn shutdown(&mut self) { self.shutdown = true; }
46
47    pub fn sid(&self) -> xous::SID { self.sid }
48
49    /// Create a context from a manually created server. Use carefully.
50    pub fn from_raw_sid(sid: xous::SID) -> Self { Self { sid, shutdown: false, handlers: Vec::new() } }
51}
52
53impl<S: Server> ServerContext<S> {
54    pub(crate) fn remove_handler(&mut self, msg_id: xous::MessageId) {
55        let idx = self.handlers.iter().position(|(id, _)| *id == msg_id).unwrap();
56        self.handlers.swap_remove(idx);
57    }
58}
59
60/// Start a server in a background thread and open a connection to it.
61pub fn listen_and_connect<S: Server + Send + 'static>(mut server: S, pid: xous::PID) -> xous::CID {
62    let sid = create_sid(S::NAME);
63    std::thread::spawn(move || main_loop(&mut server, sid));
64    xous::connect_for_process(pid, sid).unwrap()
65}
66
67/// Start the main loop of the server, where messages will be handled as they come in.
68pub fn listen<S: Server + 'static>(mut server: S) {
69    let sid = create_sid(S::NAME);
70    main_loop(&mut server, sid);
71}
72
73pub fn listen_with<S: Server + 'static>(make_server: impl FnOnce(xous::SID) -> S) {
74    let sid = create_sid(S::NAME);
75    let mut server = make_server(sid);
76    main_loop(&mut server, sid);
77}
78
79fn main_loop<S: Server + 'static>(server: &mut S, sid: xous::SID) {
80    // Create a lookup table for message handlers. The size is arbitrary and can be changed at
81    // any point.
82    let mut lut = [None; 128];
83    for (id, handle) in S::messages() {
84        if *id > lut.len() {
85            panic!("message ID too large, either change the ID or increase LUT size");
86        }
87        if lut[*id].is_some() {
88            panic!("message ID {id} registered twice.");
89        }
90        lut[*id] = Some(handle);
91    }
92    let lut = lut;
93    let mut context = ServerContext::<S>::from_raw_sid(sid);
94    server.on_start(&mut context);
95    while !context.shutdown {
96        let msg = xous::receive_message(sid).unwrap();
97        match lut.get(msg.id()) {
98            Some(Some(handle)) => {
99                handle(server, msg, &mut context);
100            }
101            Some(None) => {
102                log::error!("Unexpected message for \"{}\" with message ID {}", S::NAME, msg.id())
103            }
104            None => {
105                if let Some((_, handler)) = context.handlers.iter().find(|s| s.0 == msg.id()) {
106                    handler(server, msg, &mut context);
107                } else {
108                    log::warn!("spurious event message for \"{}\": ID={}", S::NAME, msg.id());
109                }
110            }
111        }
112    }
113    xous::destroy_server(sid).unwrap();
114}
115
116pub fn create_sid(name: &str) -> xous::SID {
117    let sid = xous::create_server().unwrap();
118    if name.is_empty() {
119        log::info!("Starting anonymous server with sid={sid:?}");
120    } else {
121        log::info!("Starting server name={name:?}");
122        // Register the server with the xous names server.
123        let names = xous_names::XousNames::new().unwrap();
124        names.register_name(sid, name).unwrap();
125        // Keep this connection open so xous-names keeps the registration alive.
126        std::mem::forget(names);
127    }
128    sid
129}