The open source OpenXR runtime
1// Copyright 2020-2023, Collabora, Ltd.
2// Copyright 2025, NVIDIA CORPORATION.
3// SPDX-License-Identifier: BSL-1.0
4/*!
5 * @file
6 * @brief Common server side code.
7 * @author Pete Black <pblack@collabora.com>
8 * @author Jakob Bornecrantz <jakob@collabora.com>
9 * @author Rylie Pavlik <rylie.pavlik@collabora.com>
10 * @ingroup ipc_server
11 */
12
13#pragma once
14
15#include "xrt/xrt_compiler.h"
16#include "xrt/xrt_limits.h"
17#include "xrt/xrt_space.h"
18#include "xrt/xrt_system.h"
19
20#include "os/os_threading.h"
21
22#include "util/u_logging.h"
23
24#include "shared/ipc_protocol.h"
25#include "shared/ipc_message_channel.h"
26
27#include "ipc_server_interface.h"
28
29#include <stdio.h>
30
31
32#ifdef __cplusplus
33extern "C" {
34#endif
35
36/*
37 *
38 * Logging
39 *
40 */
41
42#define IPC_TRACE(d, ...) U_LOG_IFL_T(d->log_level, __VA_ARGS__)
43#define IPC_DEBUG(d, ...) U_LOG_IFL_D(d->log_level, __VA_ARGS__)
44#define IPC_INFO(d, ...) U_LOG_IFL_I(d->log_level, __VA_ARGS__)
45#define IPC_WARN(d, ...) U_LOG_IFL_W(d->log_level, __VA_ARGS__)
46#define IPC_ERROR(d, ...) U_LOG_IFL_E(d->log_level, __VA_ARGS__)
47
48#define IPC_CHK_AND_RET(S, ...) U_LOG_CHK_AND_RET((S)->log_level, __VA_ARGS__)
49#define IPC_CHK_WITH_GOTO(S, ...) U_LOG_CHK_WITH_GOTO((S)->log_level, __VA_ARGS__)
50#define IPC_CHK_WITH_RET(S, ...) U_LOG_CHK_WITH_RET((S)->log_level, __VA_ARGS__)
51#define IPC_CHK_ONLY_PRINT(S, ...) U_LOG_CHK_ONLY_PRINT((S)->log_level, __VA_ARGS__)
52#define IPC_CHK_ALWAYS_RET(S, ...) U_LOG_CHK_ALWAYS_RET((S)->log_level, __VA_ARGS__)
53
54
55/*
56 *
57 * Structs
58 *
59 */
60
61#define IPC_MAX_CLIENT_SEMAPHORES 8
62#define IPC_MAX_CLIENT_SWAPCHAINS (XRT_MAX_LAYERS * 2)
63#define IPC_MAX_CLIENT_SPACES 128
64#define IPC_MAX_CLIENT_FUTURES 128
65
66struct xrt_instance;
67struct xrt_compositor;
68struct xrt_compositor_native;
69
70
71/*!
72 * Information about a single swapchain.
73 *
74 * @ingroup ipc_server
75 */
76struct ipc_swapchain_data
77{
78 uint32_t width;
79 uint32_t height;
80 uint64_t format;
81 uint32_t image_count;
82
83 bool active;
84};
85
86/*!
87 * Holds the state for a single client.
88 *
89 * @ingroup ipc_server
90 */
91struct ipc_client_state
92{
93 //! Link back to the main server.
94 struct ipc_server *server;
95
96 //! Has the system part of the shm initialized.
97 bool has_init_shm_system;
98
99 //! Session for this client.
100 struct xrt_session *xs;
101
102 //! Compositor for this client.
103 struct xrt_compositor *xc;
104
105 //! Is the inputs and outputs active.
106 bool io_active;
107
108 //! Number of swapchains in use by client
109 uint32_t swapchain_count;
110
111 //! Ptrs to the swapchains
112 struct xrt_swapchain *xscs[IPC_MAX_CLIENT_SWAPCHAINS];
113
114 //! Data for the swapchains.
115 struct ipc_swapchain_data swapchain_data[IPC_MAX_CLIENT_SWAPCHAINS];
116
117 //! Number of compositor semaphores in use by client
118 uint32_t compositor_semaphore_count;
119
120 //! Ptrs to the semaphores.
121 struct xrt_compositor_semaphore *xcsems[IPC_MAX_CLIENT_SEMAPHORES];
122
123 //! Ptrs to the futures.
124 struct xrt_future *xfts[IPC_MAX_CLIENT_FUTURES];
125
126 struct
127 {
128 uint32_t root;
129 uint32_t local;
130 uint32_t stage;
131 uint32_t unbounded;
132 } semantic_spaces;
133
134 //! Number of spaces.
135 uint32_t space_count;
136 //! Index of localspace in ipc client.
137 uint32_t local_space_index;
138 //! Index of localspace in space overseer.
139 uint32_t local_space_overseer_index;
140 //! Index of localfloorspace in ipc client.
141 uint32_t local_floor_space_index;
142 //! Index of localfloorspace in space overseer.
143 uint32_t local_floor_space_overseer_index;
144
145 //! Ptrs to the spaces.
146 struct xrt_space *xspcs[IPC_MAX_CLIENT_SPACES];
147
148 //! Which of the references spaces is the client using.
149 bool ref_space_used[XRT_SPACE_REFERENCE_TYPE_COUNT];
150
151 //! Which of the device features is the client using.
152 bool device_feature_used[XRT_DEVICE_FEATURE_MAX_ENUM];
153
154 //! Socket fd used for client comms
155 struct ipc_message_channel imc;
156
157 struct ipc_app_state client_state;
158
159
160 uint64_t plane_detection_size;
161 uint64_t plane_detection_count;
162
163 //! Array of plane detection ids with plane_detection_size entries.
164 uint64_t *plane_detection_ids;
165
166 //! Array of xrt_devices with plane_detection_size entries.
167 struct xrt_device **plane_detection_xdev;
168
169 int server_thread_index;
170
171 xrt_shmem_handle_t ism_handle;
172};
173
174enum ipc_thread_state
175{
176 IPC_THREAD_READY,
177 IPC_THREAD_STARTING,
178 IPC_THREAD_RUNNING,
179 IPC_THREAD_STOPPING,
180};
181
182struct ipc_thread
183{
184 struct os_thread thread;
185 volatile enum ipc_thread_state state;
186 volatile struct ipc_client_state ics;
187};
188
189
190/*!
191 *
192 */
193struct ipc_device
194{
195 //! The actual device.
196 struct xrt_device *xdev;
197};
198
199/*!
200 * Platform-specific mainloop object for the IPC server.
201 *
202 * Contents are essentially implementation details, but are listed in full here so they may be included by value in the
203 * main ipc_server struct.
204 *
205 * @see ipc_design
206 *
207 * @ingroup ipc_server
208 */
209struct ipc_server_mainloop
210{
211
212#if defined(XRT_OS_ANDROID) || defined(XRT_OS_LINUX) || defined(XRT_DOXYGEN)
213 //! For waiting on various events in the main thread.
214 int epoll_fd;
215#endif
216
217#if defined(XRT_OS_ANDROID) || defined(XRT_DOXYGEN)
218 /*!
219 * @name Android Mainloop Members
220 * @{
221 */
222
223 //! File descriptor for the read end of our pipe for submitting new clients
224 int pipe_read;
225
226 /*!
227 * File descriptor for the write end of our pipe for submitting new clients
228 *
229 * Must hold client_push_mutex while writing.
230 */
231 int pipe_write;
232
233 /*!
234 * Mutex for being able to register oneself as a new client.
235 *
236 * Locked only by threads in `ipc_server_mainloop_add_fd()`.
237 *
238 * This must be locked first, and kept locked the entire time a client is attempting to register and wait for
239 * confirmation. It ensures no acknowledgements of acceptance are lost and moves the overhead of ensuring this
240 * to the client thread.
241 */
242 pthread_mutex_t client_push_mutex;
243
244
245 /*!
246 * The last client fd we accepted, to acknowledge client acceptance.
247 *
248 * Also used as a sentinel during shutdown.
249 *
250 * Must hold accept_mutex while writing.
251 */
252 int last_accepted_fd;
253
254 /*!
255 * Condition variable for accepting clients.
256 *
257 * Signalled when @ref last_accepted_fd is updated.
258 *
259 * Associated with @ref accept_mutex
260 */
261 pthread_cond_t accept_cond;
262
263 /*!
264 * Mutex for accepting clients.
265 *
266 * Locked by both clients and server: that is, by threads in `ipc_server_mainloop_add_fd()` and in the
267 * server/compositor thread in an implementation function called from `ipc_server_mainloop_poll()`.
268 *
269 * Exists to operate in conjunction with @ref accept_cond - it exists to make sure that the client can be woken
270 * when the server accepts it.
271 */
272 pthread_mutex_t accept_mutex;
273
274
275 /*! @} */
276#define XRT_IPC_GOT_IMPL
277#endif
278
279#if (defined(XRT_OS_LINUX) && !defined(XRT_OS_ANDROID)) || defined(XRT_DOXYGEN)
280 /*!
281 * @name Desktop Linux Mainloop Members
282 * @{
283 */
284
285 //! Socket that we accept connections on.
286 int listen_socket;
287
288 //! Were we launched by socket activation, instead of explicitly?
289 bool launched_by_socket;
290
291 //! The socket filename we bound to, if any.
292 char *socket_filename;
293
294 /*! @} */
295
296#define XRT_IPC_GOT_IMPL
297#endif
298
299#if defined(XRT_OS_WINDOWS) || defined(XRT_DOXYGEN)
300 /*!
301 * @name Desktop Windows Mainloop Members
302 * @{
303 */
304
305 //! Named Pipe that we accept connections on.
306 HANDLE pipe_handle;
307
308 //! Name of the Pipe that we accept connections on.
309 char *pipe_name;
310
311 /*! @} */
312
313#define XRT_IPC_GOT_IMPL
314#endif
315
316#ifndef XRT_IPC_GOT_IMPL
317#error "Need port"
318#endif
319};
320
321/*!
322 * De-initialize the mainloop object.
323 * @public @memberof ipc_server_mainloop
324 */
325void
326ipc_server_mainloop_deinit(struct ipc_server_mainloop *ml);
327
328/*!
329 * Initialize the mainloop object.
330 *
331 * @return <0 on error.
332 * @public @memberof ipc_server_mainloop
333 */
334int
335ipc_server_mainloop_init(struct ipc_server_mainloop *ml, bool no_stdin);
336
337/*!
338 * @brief Poll the mainloop.
339 *
340 * Any errors are signalled by calling ipc_server_handle_failure()
341 * @public @memberof ipc_server_mainloop
342 */
343void
344ipc_server_mainloop_poll(struct ipc_server *vs, struct ipc_server_mainloop *ml);
345
346/*!
347 * Main IPC object for the server.
348 *
349 * @ingroup ipc_server
350 */
351struct ipc_server
352{
353 struct xrt_instance *xinst;
354
355 //! Handle for the current process, e.g. pidfile on linux
356 struct u_process *process;
357
358 struct u_debug_gui *debug_gui;
359
360 //! The @ref xrt_iface level system.
361 struct xrt_system *xsys;
362
363 //! System devices.
364 struct xrt_system_devices *xsysd;
365
366 //! Space overseer.
367 struct xrt_space_overseer *xso;
368
369 //! System compositor.
370 struct xrt_system_compositor *xsysc;
371
372 struct ipc_device idevs[XRT_SYSTEM_MAX_DEVICES];
373 struct xrt_tracking_origin *xtracks[XRT_SYSTEM_MAX_DEVICES];
374
375 struct ipc_shared_memory *isms[IPC_MAX_CLIENTS];
376
377 struct ipc_server_mainloop ml;
378
379 // Is the mainloop supposed to run.
380 volatile bool running;
381
382 // Should we exit when a client disconnects.
383 bool exit_on_disconnect;
384
385 // Should we exit when no clients are connected.
386 bool exit_when_idle;
387
388 // Timestamp when last client disconnected (for exit_when_idle delay)
389 uint64_t last_client_disconnect_ns;
390
391 // How long to wait after all clients disconnect before exiting (in nanoseconds)
392 uint64_t exit_when_idle_delay_ns;
393
394 enum u_logging_level log_level;
395
396 struct ipc_thread threads[IPC_MAX_CLIENTS];
397
398 volatile uint32_t current_slot_index;
399
400 //! Generator for IDs.
401 uint32_t id_generator;
402
403 struct
404 {
405 int active_client_index;
406 int last_active_client_index;
407
408 // Counter for total number of connected clients
409 uint32_t connected_client_count;
410
411 struct os_mutex lock;
412 } global_state;
413
414 /*!
415 * Callbacks for server events.
416 */
417 const struct ipc_server_callbacks *callbacks;
418
419 /*!
420 * User data passed to callbacks.
421 */
422 void *callback_data;
423
424 //! Disable listening on stdin for server stop.
425 bool no_stdin;
426};
427
428/*!
429 * Finish setting up the server by creating the system, compositor and devices.
430 *
431 * @ingroup ipc_server
432 */
433xrt_result_t
434ipc_server_init_system_if_available_locked(struct ipc_server *s,
435 volatile struct ipc_client_state *ics,
436 bool *out_available);
437
438/*!
439 * Get the current state of a client.
440 *
441 * @ingroup ipc_server
442 */
443xrt_result_t
444ipc_server_get_client_app_state(struct ipc_server *s, uint32_t client_id, struct ipc_app_state *out_ias);
445
446/*!
447 * Set the new active client.
448 *
449 * @ingroup ipc_server
450 */
451xrt_result_t
452ipc_server_set_active_client(struct ipc_server *s, uint32_t client_id);
453
454/*!
455 * Toggle the io for this client.
456 *
457 * @ingroup ipc_server
458 */
459xrt_result_t
460ipc_server_toggle_io_client(struct ipc_server *s, uint32_t client_id);
461
462/*!
463 * Called by client threads to set a session to active.
464 *
465 * @ingroup ipc_server
466 */
467void
468ipc_server_activate_session(volatile struct ipc_client_state *ics);
469
470/*!
471 * Called by client threads to set a session to deactivate.
472 *
473 * @ingroup ipc_server
474 */
475void
476ipc_server_deactivate_session(volatile struct ipc_client_state *ics);
477
478/*!
479 * Called by client threads to recalculate active client.
480 *
481 * @ingroup ipc_server
482 */
483void
484ipc_server_update_state(struct ipc_server *s);
485
486/*!
487 * Thread function for the client side dispatching.
488 *
489 * @ingroup ipc_server
490 */
491void *
492ipc_server_client_thread(void *_ics);
493
494/*!
495 * This destroys the native compositor for this client and any extra objects
496 * created from it, like all of the swapchains.
497 */
498void
499ipc_server_client_destroy_session_and_compositor(volatile struct ipc_client_state *ics);
500
501/*!
502 * @defgroup ipc_server_internals Server Internals
503 * @brief These are only called by the platform-specific mainloop polling code.
504 * @ingroup ipc_server
505 * @{
506 */
507/*!
508 * Called when a client has connected, it takes the client's ipc handle.
509 * Handles all things needed to be done for a client connecting, like starting
510 * it's thread.
511 *
512 * @param vs The IPC server.
513 * @param ipc_handle Handle to communicate over.
514 * @memberof ipc_server
515 */
516void
517ipc_server_handle_client_connected(struct ipc_server *vs, xrt_ipc_handle_t ipc_handle);
518
519/*!
520 * Perform whatever needs to be done when the mainloop polling encounters a failure.
521 * @memberof ipc_server
522 */
523void
524ipc_server_handle_failure(struct ipc_server *vs);
525
526/*!
527 * Perform whatever needs to be done when the mainloop polling identifies that the server should be shut down.
528 *
529 * Does something like setting a flag or otherwise signalling for shutdown: does not itself explicitly exit.
530 * @memberof ipc_server
531 */
532void
533ipc_server_handle_shutdown_signal(struct ipc_server *vs);
534
535xrt_result_t
536ipc_server_get_system_properties(struct ipc_server *vs, struct xrt_system_properties *out_properties);
537//! @}
538
539/*
540 *
541 * Helpers
542 *
543 */
544
545/*!
546 * Get a xdev with the given device_id.
547 */
548static inline struct xrt_device *
549get_xdev(volatile struct ipc_client_state *ics, uint32_t device_id)
550{
551 return ics->server->idevs[device_id].xdev;
552}
553
554/*!
555 * Get a idev with the given device_id.
556 */
557static inline struct ipc_device *
558get_idev(volatile struct ipc_client_state *ics, uint32_t device_id)
559{
560 return &ics->server->idevs[device_id];
561}
562
563/*!
564 * Get the data in the shared memory of the given client.
565 */
566static inline struct ipc_shared_memory *
567get_ism(volatile struct ipc_client_state *ics)
568{
569 return ics->server->isms[ics->server_thread_index];
570}
571
572/*!
573 * Get the handle for the shared memory of the given client.
574 */
575static inline xrt_shmem_handle_t
576get_ism_handle(volatile struct ipc_client_state *ics)
577{
578 return ics->ism_handle;
579}
580
581#ifdef __cplusplus
582}
583#endif