Something went wrong. Try again.
The open source OpenXR runtime
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196// Copyright 2026, Beyley Cardellio// Copyright 2026, NVIDIA CORPORATION.// SPDX-License-Identifier: BSL-1.0/*! * @file * @brief Base timing source implementation. * @author Beyley Cardellio <ep1cm1n10n123@gmail.com> * @ingroup base */
#include "util/u_misc.h"
#include "os/os_threading.h"
#include "b_timing_source.h"
#define B_TIMING_SOURCE_MAX_SINKS 4
struct b_timing_source{ struct t_timing_event_source source; struct t_timing_event_sink sink; struct xrt_frame_node node;
struct os_mutex mutex;
bool running;
struct t_timing_event_sink *sinks[B_TIMING_SOURCE_MAX_SINKS];};
/*! * @ref t_timing_event_source implementation */
static struct b_timing_source *from_source(struct t_timing_event_source *ttes){ return container_of(ttes, struct b_timing_source, source);}
static intb_timing_source_add_sink(struct t_timing_event_source *ttes, struct t_timing_event_sink *sink){ struct b_timing_source *bts = from_source(ttes);
int ret = -1;
os_mutex_lock(&bts->mutex); for (size_t i = 0; i < ARRAY_SIZE(bts->sinks); i++) { if (bts->sinks[i] == NULL) { bts->sinks[i] = sink;
// Success ret = 0; break; } } os_mutex_unlock(&bts->mutex);
return ret;}
static voidb_timing_source_remove_sink(struct t_timing_event_source *ttes, struct t_timing_event_sink *sink){ struct b_timing_source *bts = from_source(ttes);
os_mutex_lock(&bts->mutex); for (size_t i = 0; i < ARRAY_SIZE(bts->sinks); i++) { if (bts->sinks[i] == sink) { bts->sinks[i] = NULL; } } os_mutex_unlock(&bts->mutex);}
/*! * @ref t_timing_event_sink implementation */
static struct b_timing_source *from_sink(struct t_timing_event_sink *sink){ return container_of(sink, struct b_timing_source, sink);}
/*! * Fan out @p event to every registered downstream sink. * * The mutex must be held for the entire fan-out, including while calling each * downstream `push_timing_event`. Timing sinks unregister themselves on * destruction via `remove_sink`; without the lock held across the callbacks, a * sink can be destroyed and freed on another thread while we still hold (or * call through) its pointer. Clever alternatives like copying the sink list and * unlocking before dispatch do not help: the copied pointers can still become * use-after-free once the owning object is destroyed. * * Because the mutex is non-recursive, downstream callbacks must not call * `add_sink` / `remove_sink` on this same `b_timing_source` (that would * deadlock). Sink registration is expected at setup / teardown time, not from * inside event delivery. */static voidb_timing_source_push_timing_event(struct t_timing_event_sink *sink, const struct t_timing_event *event){ struct b_timing_source *bts = from_sink(sink);
// Held across dispatch so sinks cannot be destroyed under us; see above. os_mutex_lock(&bts->mutex); if (!bts->running) { os_mutex_unlock(&bts->mutex); return; }
for (size_t i = 0; i < ARRAY_SIZE(bts->sinks); i++) { struct t_timing_event_sink *downstream = bts->sinks[i];
if (downstream != NULL) { t_timing_event_sink_push_timing_event(downstream, event); } } os_mutex_unlock(&bts->mutex);}
/*! * @ref xrt_frame_node implementation */
static struct b_timing_source *from_node(struct xrt_frame_node *node){ return container_of(node, struct b_timing_source, node);}
static voidb_timing_source_break_apart(struct xrt_frame_node *node){ struct b_timing_source *bts = from_node(node);
os_mutex_lock(&bts->mutex); bts->running = false; os_mutex_unlock(&bts->mutex);}
static voidb_timing_source_destroy(struct xrt_frame_node *node){ struct b_timing_source *bts = from_node(node);
os_mutex_destroy(&bts->mutex);
free(bts);}
/* * Exported functions */
xrt_result_tb_timing_source_create(struct xrt_frame_context *xfctx, struct t_timing_event_sink **out_sink, struct t_timing_event_source **out_source){ struct b_timing_source *bts = U_TYPED_CALLOC(struct b_timing_source); if (bts == NULL) { return XRT_ERROR_ALLOCATION; }
if (os_mutex_init(&bts->mutex) < 0) { free(bts); return XRT_ERROR_SYNC_PRIMITIVE_CREATION_FAILED; }
// @ref t_timing_event_sink bts->sink.push_timing_event = b_timing_source_push_timing_event;
// @ref t_timing_event_source bts->source.add_sink = b_timing_source_add_sink; bts->source.remove_sink = b_timing_source_remove_sink;
// @ref xrt_frame_node bts->node.break_apart = b_timing_source_break_apart; bts->node.destroy = b_timing_source_destroy;
bts->running = true;
xrt_frame_context_add(xfctx, &bts->node);
*out_sink = &bts->sink; *out_source = &bts->source; return XRT_SUCCESS;}