diff options
Diffstat (limited to 'pw_rpc/public/pw_rpc/internal/config.h')
-rw-r--r-- | pw_rpc/public/pw_rpc/internal/config.h | 213 |
1 files changed, 162 insertions, 51 deletions
diff --git a/pw_rpc/public/pw_rpc/internal/config.h b/pw_rpc/public/pw_rpc/internal/config.h index 42e4ebb80..24a227159 100644 --- a/pw_rpc/public/pw_rpc/internal/config.h +++ b/pw_rpc/public/pw_rpc/internal/config.h @@ -18,76 +18,186 @@ #include <cstddef> #include <type_traits> -// In client and bidirectional RPCs, pw_rpc clients may signal that they have -// finished sending requests with a CLIENT_STREAM_END packet. While this can be -// useful in some circumstances, it is often not necessary. -// -// This option controls whether or not include a callback that is called when -// the client stream ends. The callback is included in all ServerReader/Writer -// objects as a pw::Function, so may have a significant cost. +/// In client and bidirectional RPCs, pw_rpc clients may signal that they have +/// finished sending requests with a `CLIENT_STREAM_END` packet. While this can +/// be useful in some circumstances, it is often not necessary. +/// +/// This option controls whether or not include a callback that is called when +/// the client stream ends. The callback is included in all ServerReader/Writer +/// objects as a @cpp_type{pw::Function}, so may have a significant cost. +/// +/// This is disabled by default. #ifndef PW_RPC_CLIENT_STREAM_END_CALLBACK #define PW_RPC_CLIENT_STREAM_END_CALLBACK 0 #endif // PW_RPC_CLIENT_STREAM_END_CALLBACK -// The Nanopb-based pw_rpc implementation allocates memory to use for Nanopb -// structs for the request and response protobufs. The template function that -// allocates these structs rounds struct sizes up to this value so that -// different structs can be allocated with the same function. Structs with sizes -// larger than this value cause an extra function to be created, which slightly -// increases code size. -// -// Ideally, this value will be set to the size of the largest Nanopb struct used -// as an RPC request or response. The buffer can be stack or globally allocated -// (see PW_RPC_NANOPB_STRUCT_BUFFER_STACK_ALLOCATE). +/// The Nanopb-based pw_rpc implementation allocates memory to use for Nanopb +/// structs for the request and response protobufs. The template function that +/// allocates these structs rounds struct sizes up to this value so that +/// different structs can be allocated with the same function. Structs with +/// sizes larger than this value cause an extra function to be created, which +/// slightly increases code size. +/// +/// Ideally, this value will be set to the size of the largest Nanopb struct +/// used as an RPC request or response. The buffer can be stack or globally +/// allocated (see @c_macro{PW_RPC_NANOPB_STRUCT_BUFFER_STACK_ALLOCATE}). +/// +/// This defaults to 64 bytes. #ifndef PW_RPC_NANOPB_STRUCT_MIN_BUFFER_SIZE #define PW_RPC_NANOPB_STRUCT_MIN_BUFFER_SIZE 64 #endif // PW_RPC_NANOPB_STRUCT_MIN_BUFFER_SIZE -// Enable global synchronization for RPC calls. If this is set, a backend must -// be configured for pw_sync:mutex. +/// Enable global synchronization for RPC calls. If this is set, a backend must +/// be configured for pw_sync:mutex. +/// +/// This is enabled by default. #ifndef PW_RPC_USE_GLOBAL_MUTEX -#define PW_RPC_USE_GLOBAL_MUTEX 0 +#define PW_RPC_USE_GLOBAL_MUTEX 1 #endif // PW_RPC_USE_GLOBAL_MUTEX -// Whether pw_rpc should use dynamic memory allocation internally. If enabled, -// pw_rpc dynamically allocates channels and its encoding buffers. RPC users may -// use dynamic allocation independently of this option (e.g. to allocate pw_rpc -// call objects). -// -// The semantics for allocating and initializing channels change depending on -// this option. If dynamic allocation is disabled, pw_rpc endpoints (servers or -// clients) use an externally-allocated, fixed-size array of channels. -// That array must include unassigned channels or existing channels must be -// closed to add new channels. -// -// If dynamic allocation is enabled, an span of channels may be passed to the -// endpoint at construction, but these channels are only used to initialize its -// internal std::vector of channels. External channel objects are NOT used by -// the endpoint cannot be updated if dynamic allocation is enabled. No -// unassigned channels should be passed to the endpoint; they will be ignored. -// Any number of channels may be added to the endpoint, without closing existing -// channels, but adding channels will use more memory. +/// pw_rpc must yield the current thread when waiting for a callback to complete +/// in a different thread. PW_RPC_YIELD_MODE determines how to yield. There are +/// three supported settings: +/// +/// - @c_macro{PW_RPC_YIELD_MODE_BUSY_LOOP} - Do nothing. Release and +/// reacquire the RPC lock in a busy loop. @c_macro{PW_RPC_USE_GLOBAL_MUTEX} +/// must be 0. +/// - @c_macro{PW_RPC_YIELD_MODE_SLEEP} - Yield with 1-tick calls to +/// @cpp_func{pw::this_thread::sleep_for()}. A backend must be configured +/// for pw_thread:sleep. +/// - @c_macro{PW_RPC_YIELD_MODE_YIELD} - Yield with +/// @cpp_func{pw::this_thread::yield()}. A backend must be configured for +/// pw_thread:yield. IMPORTANT: On some platforms, +/// @cpp_func{pw::this_thread::yield()} does not yield to lower priority +/// tasks and should not be used here. +/// +#ifndef PW_RPC_YIELD_MODE +#if PW_RPC_USE_GLOBAL_MUTEX == 0 +#define PW_RPC_YIELD_MODE PW_RPC_YIELD_MODE_BUSY_LOOP +#else +#define PW_RPC_YIELD_MODE PW_RPC_YIELD_MODE_SLEEP +#endif // PW_RPC_USE_GLOBAL_MUTEX == 0 +#endif // PW_RPC_YIELD_MODE + +/// @def PW_RPC_YIELD_MODE_BUSY_LOOP +/// @def PW_RPC_YIELD_MODE_SLEEP +/// @def PW_RPC_YIELD_MODE_YIELD +/// +/// Supported configuration values for @c_macro{PW_RPC_YIELD_MODE}. +#define PW_RPC_YIELD_MODE_BUSY_LOOP 100 +#define PW_RPC_YIELD_MODE_SLEEP 101 +#define PW_RPC_YIELD_MODE_YIELD 102 + +/// If `PW_RPC_YIELD_MODE == PW_RPC_YIELD_MODE_SLEEP`, +/// `PW_RPC_YIELD_SLEEP_DURATION` sets how long to sleep during each iteration +/// of the yield loop. The value must be a constant expression that converts to +/// a @cpp_type{pw::chrono::SystemClock::duration}. +#ifndef PW_RPC_YIELD_SLEEP_DURATION + +// When building for a desktop operating system, use a 1ms sleep by default. +// 1-tick duration sleeps can result in spurious timeouts. +#if defined(_WIN32) || defined(__APPLE__) || defined(__linux__) +#define PW_RPC_YIELD_SLEEP_DURATION std::chrono::milliseconds(1) +#else +#define PW_RPC_YIELD_SLEEP_DURATION pw::chrono::SystemClock::duration(1) +#endif // defined(_WIN32) || defined(__APPLE__) || defined(__linux__) + +#endif // PW_RPC_YIELD_SLEEP_DURATION + +// PW_RPC_YIELD_SLEEP_DURATION is not needed for non-sleep yield modes. +#if PW_RPC_YIELD_MODE != PW_RPC_YIELD_MODE_SLEEP +#undef PW_RPC_YIELD_SLEEP_DURATION +#endif // PW_RPC_YIELD_MODE != PW_RPC_YIELD_MODE_SLEEP + +/// pw_rpc call objects wait for their callbacks to complete before they are +/// moved or destoyed. Deadlocks occur if a callback: +/// +/// - attempts to destroy its call object, +/// - attempts to move its call object while the call is still active, or +/// - never returns. +/// +/// If `PW_RPC_CALLBACK_TIMEOUT_TICKS` is greater than 0, then `PW_CRASH` is +/// invoked if a thread waits for an RPC callback to complete for more than the +/// specified tick count. +/// +/// A "tick" in this context is one iteration of a loop that yields releases the +/// RPC lock and yields the thread according to @c_macro{PW_RPC_YIELD_MODE}. By +/// default, the thread yields with a 1-tick call to +/// @cpp_func{pw::this_thread::sleep_for()}. +#ifndef PW_RPC_CALLBACK_TIMEOUT_TICKS +#define PW_RPC_CALLBACK_TIMEOUT_TICKS 10000 +#endif // PW_RPC_CALLBACK_TIMEOUT_TICKS + +/// Whether pw_rpc should use dynamic memory allocation internally. If enabled, +/// pw_rpc dynamically allocates channels and its encoding buffer. RPC users may +/// use dynamic allocation independently of this option (e.g. to allocate pw_rpc +/// call objects). +/// +/// The semantics for allocating and initializing channels change depending on +/// this option. If dynamic allocation is disabled, pw_rpc endpoints (servers or +/// clients) use an externally-allocated, fixed-size array of channels. That +/// array must include unassigned channels or existing channels must be closed +/// to add new channels. +/// +/// If dynamic allocation is enabled, an span of channels may be passed to the +/// endpoint at construction, but these channels are only used to initialize its +/// internal channels container. External channel objects are NOT used by the +/// endpoint and cannot be updated if dynamic allocation is enabled. No +/// unassigned channels should be passed to the endpoint; they will be ignored. +/// Any number of channels may be added to the endpoint, without closing +/// existing channels, but adding channels will use more memory. #ifndef PW_RPC_DYNAMIC_ALLOCATION #define PW_RPC_DYNAMIC_ALLOCATION 0 #endif // PW_RPC_DYNAMIC_ALLOCATION -#if PW_RPC_DYNAMIC_ALLOCATION && defined(PW_RPC_ENCODING_BUFFER_SIZE_BYTES) -static_assert(false, - "PW_RPC_ENCODING_BUFFER_SIZE_BYTES cannot be set if " - "PW_RPC_DYNAMIC_ALLOCATION is enabled"); -#endif // PW_RPC_DYNAMIC_ALLOCATION && PW_RPC_ENCODING_BUFFER_SIZE_BYTES - -// Size of the global RPC packet encoding buffer in bytes. +#if defined(PW_RPC_DYNAMIC_CONTAINER) || \ + defined(PW_RPC_DYNAMIC_CONTAINER_INCLUDE) +static_assert( + PW_RPC_DYNAMIC_ALLOCATION == 1, + "PW_RPC_DYNAMIC_ALLOCATION is disabled, so PW_RPC_DYNAMIC_CONTAINER and " + "PW_RPC_DYNAMIC_CONTAINER_INCLUDE have no effect and should not be set."); +#endif // PW_RPC_DYNAMIC_CONTAINER || PW_RPC_DYNAMIC_CONTAINER_INCLUDE + +/// If @c_macro{PW_RPC_DYNAMIC_ALLOCATION} is enabled, this macro must expand to +/// a container capable of storing objects of the provided type. This container +/// will be used internally by pw_rpc to allocate the channels list and encoding +/// buffer. Defaults to `std::vector<type>`, but may be set to any type that +/// supports the following `std::vector` operations: +/// +/// - Default construction +/// - `emplace_back()` +/// - `pop_back()` +/// - `back()` +/// - `resize()` +/// - `clear()` +/// - Range-based for loop iteration (`begin()`, `end()`) +/// +#ifndef PW_RPC_DYNAMIC_CONTAINER +#define PW_RPC_DYNAMIC_CONTAINER(type) std::vector<type> +#endif // PW_RPC_DYNAMIC_CONTAINER + +/// If @c_macro{PW_RPC_DYNAMIC_ALLOCATION} is enabled, this header file is +/// included in files that use @c_macro{PW_RPC_DYNAMIC_CONTAINER}. Defaults to +/// `<vector>`, but may be set in conjunction with +/// @c_macro{PW_RPC_DYNAMIC_CONTAINER} to use a different container type for +/// dynamic allocations in pw_rpc. +#ifndef PW_RPC_DYNAMIC_CONTAINER_INCLUDE +#define PW_RPC_DYNAMIC_CONTAINER_INCLUDE <vector> +#endif // PW_RPC_DYNAMIC_CONTAINER_INCLUDE + +/// Size of the global RPC packet encoding buffer in bytes. If dynamic +/// allocation is enabled, this value is only used for test helpers that +/// allocate RPC encoding buffers. #ifndef PW_RPC_ENCODING_BUFFER_SIZE_BYTES #define PW_RPC_ENCODING_BUFFER_SIZE_BYTES 512 #endif // PW_RPC_ENCODING_BUFFER_SIZE_BYTES -// The log level to use for this module. Logs below this level are omitted. +/// The log level to use for this module. Logs below this level are omitted. #ifndef PW_RPC_CONFIG_LOG_LEVEL #define PW_RPC_CONFIG_LOG_LEVEL PW_LOG_LEVEL_INFO #endif // PW_RPC_CONFIG_LOG_LEVEL -// The log module name to use for this module. +/// The log module name to use for this module. #ifndef PW_RPC_CONFIG_LOG_MODULE_NAME #define PW_RPC_CONFIG_LOG_MODULE_NAME "PW_RPC" #endif // PW_RPC_CONFIG_LOG_MODULE_NAME @@ -113,14 +223,15 @@ inline constexpr size_t kEncodingBufferSizeBytes = } // namespace pw::rpc::cfg -// This option determines whether to allocate the Nanopb structs on the stack or -// in a global variable. Globally allocated structs are NOT thread safe, but -// work fine when the RPC server's ProcessPacket function is only called from -// one thread. +/// This option determines whether to allocate the Nanopb structs on the stack +/// or in a global variable. Globally allocated structs are NOT thread safe, but +/// work fine when the RPC server's ProcessPacket function is only called from +/// one thread. #ifndef PW_RPC_NANOPB_STRUCT_BUFFER_STACK_ALLOCATE #define PW_RPC_NANOPB_STRUCT_BUFFER_STACK_ALLOCATE 1 #endif // PW_RPC_NANOPB_STRUCT_BUFFER_STACK_ALLOCATE +/// @private Internal macro for declaring the Nanopb struct; do not use. #if PW_RPC_NANOPB_STRUCT_BUFFER_STACK_ALLOCATE #define _PW_RPC_NANOPB_STRUCT_STORAGE_CLASS #else |