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