diff --git a/src/xrt/base/CMakeLists.txt b/src/xrt/base/CMakeLists.txt index e6dce4f44..80d205328 100644 --- a/src/xrt/base/CMakeLists.txt +++ b/src/xrt/base/CMakeLists.txt @@ -6,13 +6,15 @@ target_include_directories(base PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries( base - PRIVATE aux_util aux_os + PRIVATE aux_util aux_os aux_math PUBLIC xrt-interfaces ) target_sources( base PRIVATE + b_app_policy.c + b_app_policy.h b_documentation.h b_body_tracker.cpp b_body_tracker.h diff --git a/src/xrt/base/b_app_policy.c b/src/xrt/base/b_app_policy.c new file mode 100644 index 000000000..fe098d23f --- /dev/null +++ b/src/xrt/base/b_app_policy.c @@ -0,0 +1,222 @@ +// Copyright 2026, Beyley Cardellio. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Helper to implement @ref xrt_app_instance. + * @author Beyley Cardellio + * @ingroup base + */ + +#include "util/u_misc.h" +#include "util/u_logging.h" + +#include "math/m_api.h" + +#include "b_app_policy.h" + + +// Forward declares +static void +app_system_destroy(struct xrt_app_system *xasys); + +/* + * + * Helpers. + * + */ + +static inline struct b_app_instance * +b_app_instance(struct xrt_app_instance *xsys) +{ + return (struct b_app_instance *)xsys; +} + +static inline struct b_app_system * +b_app_system(struct xrt_app_system *xsys) +{ + return (struct b_app_system *)xsys; +} + +static struct b_app_system * +app_system_create(struct b_app_instance *bainst, struct xrt_system *xsys) +{ + struct b_app_system *basys = U_TYPED_CALLOC(struct b_app_system); + + // Use init function, then add the common destroy function. + if (!b_app_system_init(&bainst->base, basys, xsys, app_system_destroy)) { + free(basys); + return NULL; + } + + return basys; +} + +/* + * + * Member functions. + * + */ + +//! Destroys a single b_app_system, only used if it's using the b_app_instance infrastructure. +static void +app_system_destroy(struct xrt_app_system *xasys) +{ + struct b_app_system *basys = b_app_system(xasys); + struct b_app_instance *bainst = b_app_instance(basys->xainst); + + // Lock before destroying the object + os_mutex_lock(&bainst->app_systems.mutex); + + // Use shared fini function. + b_app_system_fini(basys); + free(basys); + + uint32_t i = 0; + for (; i < bainst->app_systems.count; i++) { + if (bainst->app_systems.storage[i] == xasys) { + break; + } + } + + if (i == bainst->app_systems.count) { + assert(!"b_app_system destroyed but not in parent b_app_instance?"); + os_mutex_unlock(&bainst->app_systems.mutex); + return; + } + + // Swap remove the last element + bainst->app_systems.storage[i] = bainst->app_systems.storage[bainst->app_systems.count - 1]; + // Mark the last item as removed + bainst->app_systems.count -= 1; + + os_mutex_unlock(&bainst->app_systems.mutex); +} + +static void +b_app_instance_destroy(struct xrt_app_instance *xainst) +{ + struct b_app_instance *bainst = b_app_instance(xainst); + + // Free the app systems that are still stored in the app instance, if any. + if (bainst->app_systems.storage != NULL) { + // Free all app systems that haven't been freed yet. + for (uint32_t i = 0; i < bainst->app_systems.count; i++) { + struct xrt_app_system *xasys = bainst->app_systems.storage[i]; + + b_app_system_fini(b_app_system(xasys)); + free(xasys); + } + + free(bainst->app_systems.storage); + + bainst->app_systems.storage = NULL; + bainst->app_systems.count = 0; + bainst->app_systems.capacity = 0; + } + + // Use shared fini function. + b_app_instance_fini(bainst); + + free(bainst); +} + +static xrt_result_t +b_app_instance_create_app_system(struct xrt_app_instance *xainst, + struct xrt_system *xsys, + struct xrt_app_system **out_xasys) +{ + struct b_app_instance *bainst = b_app_instance(xainst); + + xrt_result_t xret = XRT_SUCCESS; + + struct b_app_system *basys = app_system_create(bainst, xsys); + if (basys == NULL) { + return XRT_ERROR_ALLOCATION; + } + + os_mutex_lock(&bainst->app_systems.mutex); + + if (bainst->app_systems.count >= bainst->app_systems.capacity) { + uint32_t new_capacity = MAX(1, bainst->app_systems.capacity * 2); + U_ARRAY_REALLOC_OR_FREE(bainst->app_systems.storage, struct xrt_app_system *, new_capacity); + + if (bainst->app_systems.storage == NULL) { + xret = XRT_ERROR_ALLOCATION; + goto out; + } + + bainst->app_systems.capacity = new_capacity; + } + + bainst->app_systems.storage[bainst->app_systems.count++] = &basys->base; + + *out_xasys = &basys->base; + +out: + if (xret != XRT_SUCCESS) { + // Destroy the app system we just created since we won't be able to store it. + b_app_system_fini(basys); + free(basys); + } + + os_mutex_unlock(&bainst->app_systems.mutex); + return xret; +} + +/* + * + * 'Exported' functions. + * + */ + +struct b_app_instance * +b_app_instance_create(void) +{ + struct b_app_instance *bainst = U_TYPED_CALLOC(struct b_app_instance); + + // Use init function with the common destroy function. + if (!b_app_instance_init(bainst, b_app_instance_destroy)) { + free(bainst); + return NULL; + } + + return bainst; +} + +bool +b_app_instance_init(struct b_app_instance *bainst, void (*destroy_fn)(struct xrt_app_instance *)) +{ + // xrt_app_instance fields. + bainst->base.create_app_system = b_app_instance_create_app_system; + bainst->base.destroy = destroy_fn; + + os_mutex_init(&bainst->app_systems.mutex); + + return true; +} + +void +b_app_instance_fini(struct b_app_instance *bainst) +{ + os_mutex_destroy(&bainst->app_systems.mutex); +} + + +bool +b_app_system_init(struct xrt_app_instance *xainst, + struct b_app_system *basys, + struct xrt_system *xsys, + void (*destroy_fn)(struct xrt_app_system *)) +{ + // xrt_app_system fields. + basys->base.destroy = destroy_fn; + + basys->xainst = xainst; + basys->xsys = xsys; + + return true; +} + +void +b_app_system_fini(struct b_app_system *basys) +{} diff --git a/src/xrt/base/b_app_policy.h b/src/xrt/base/b_app_policy.h new file mode 100644 index 000000000..0846e22d6 --- /dev/null +++ b/src/xrt/base/b_app_policy.h @@ -0,0 +1,122 @@ +// Copyright 2026, Beyley Cardellio. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Helper to implement @ref xrt_app_instance and @ref xrt_app_system. + * @author Beyley Cardellio + * @ingroup base + */ + +#pragma once + +#include "xrt/xrt_app_policy.h" + +#include "os/os_threading.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +/*! + * @implements xrt_app_instance + */ +struct b_app_instance +{ + struct xrt_app_instance base; + + //! Data for stored app systems + struct + { + struct os_mutex mutex; + + //! Number of application systems stored + uint32_t count; + //! Capacity of the storage pointer + uint32_t capacity; + //! The storage pointer for all the application systems + struct xrt_app_system **storage; + } app_systems; +}; + +/*! + * @implements xrt_app_system + */ +struct b_app_system +{ + struct xrt_app_system base; + + //! The parent @ref xrt_app_instance + struct xrt_app_instance *xainst; + + //! The @ref xrt_system this is wrapping + struct xrt_system *xsys; +}; + + +/*! + * Create a @ref b_app_instance, a fully working @ref xrt_app_instance. Objects wishing to + * use @ref b_app_instance as a parent class should use @ref b_app_instance_init. + * + * @public @memberof b_app_instance + * @ingroup base + * @see b_app_instance_init + */ +struct b_app_instance * +b_app_instance_create(void); + +/*! + * Inits a @ref b_app_instance struct when used as a parent class, only to be used externally by the derived class. + * Not needed to be called if created by @ref b_app_instance_create. + * + * @protected @memberof b_app_instance + * @ingroup base + */ +bool +b_app_instance_init(struct b_app_instance *bainst, void (*destroy_fn)(struct xrt_app_instance *)); + +/*! + * Finalizes a @ref b_app_instance struct when used as a parent class, only to be used + * by the derived class. This will not free the @ref b_app_instance pointer itself but will + * free any resources created by the default implementation functions. Not + * needed to be called if created by @ref b_app_instance_create, instead use + * xrt_app_instance::destroy. + * + * @protected @memberof b_app_instance + * @ingroup base + */ +void +b_app_instance_fini(struct b_app_instance *bainst); + + +/*! + * Inits a @ref b_app_system struct when used as a parent class, only to be used + * by the derived class. Not needed to be called if created by @ref b_app_instance_create_app_system. + * + * @protected @memberof b_app_system + * @ingroup base + */ +bool +b_app_system_init(struct xrt_app_instance *xainst, + struct b_app_system *basys, + struct xrt_system *xsys, + void (*destroy_fn)(struct xrt_app_system *)); + +/*! + * Finalizes a @ref b_app_system struct when used as a parent class, only to be used + * by the derived class. This will not free the @ref b_app_system pointer itself but will + * free any resources created by the default implementation functions. Not + * needed to be called if created by @ref b_app_instance_create_app_system, instead use + * xrt_app_system::destroy. + * + * @protected @memberof b_app_system + * @ingroup base + */ +void +b_app_system_fini(struct b_app_system *basys); + + +#ifdef __cplusplus +} +#endif