|
| 1 | +// Copyright 2018 Open Source Robotics Foundation, Inc. |
| 2 | +// |
| 3 | +// Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +// you may not use this file except in compliance with the License. |
| 5 | +// You may obtain a copy of the License at |
| 6 | +// |
| 7 | +// http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +// |
| 9 | +// Unless required by applicable law or agreed to in writing, software |
| 10 | +// distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +// See the License for the specific language governing permissions and |
| 13 | +// limitations under the License. |
| 14 | + |
| 15 | +#ifndef RCL__CONTEXT_H_ |
| 16 | +#define RCL__CONTEXT_H_ |
| 17 | + |
| 18 | +#ifdef __cplusplus |
| 19 | +extern "C" |
| 20 | +{ |
| 21 | +#endif |
| 22 | + |
| 23 | +#include "rmw/init.h" |
| 24 | + |
| 25 | +#include "rcl/allocator.h" |
| 26 | +#include "rcl/arguments.h" |
| 27 | +#include "rcl/init_options.h" |
| 28 | +#include "rcl/macros.h" |
| 29 | +#include "rcl/types.h" |
| 30 | +#include "rcl/visibility_control.h" |
| 31 | + |
| 32 | +typedef uint64_t rcl_context_instance_id_t; |
| 33 | + |
| 34 | +struct rcl_context_impl_t; |
| 35 | + |
| 36 | +/// Encapsulates the non-global state of an init/shutdown cycle. |
| 37 | +/** |
| 38 | + * The context is used in the creation of top level entities like nodes and |
| 39 | + * guard conditions, as well as to shutdown a specific instance of init. |
| 40 | + * |
| 41 | + * Here is a diagram of a typical context's lifecycle: |
| 42 | + * |
| 43 | + * ``` |
| 44 | + * +---------------+ |
| 45 | + * | | |
| 46 | + * +--> uninitialized +---> rcl_get_zero_initialized_context() + |
| 47 | + * | | | | |
| 48 | + * | +---------------+ | |
| 49 | + * | | |
| 50 | + * | +-----------------------------------------------+ |
| 51 | + * | | |
| 52 | + * | +--------v---------+ +-----------------------+ |
| 53 | + * | | | | | |
| 54 | + * | | zero-initialized +-> rcl_init() +-> initialized and valid +-> rcl_shutdown() + |
| 55 | + * | | | | | | |
| 56 | + * | +------------------+ +-----------------------+ | |
| 57 | + * | | |
| 58 | + * | +-----------------------------------------------------------------+ |
| 59 | + * | | |
| 60 | + * | +------------v------------+ |
| 61 | + * | | | |
| 62 | + * | | initialized but invalid +---> finalize all entities, then rcl_context_fini() + |
| 63 | + * | | | | |
| 64 | + * | +-------------------------+ | |
| 65 | + * | | |
| 66 | + * +---------------------------------------------------------------------------------+ |
| 67 | + * ``` |
| 68 | + * |
| 69 | + * A declared but not defined `rcl_context_t` instance is considered to be |
| 70 | + * "uninitialized", and passing an uninitialized context to any functions will |
| 71 | + * result in undefined behavior. |
| 72 | + * Some functions, like `rcl_init()` require the context instance to be |
| 73 | + * zero initialized (all members set to "zero" state) before use. |
| 74 | + * |
| 75 | + * Zero initialization of an `rcl_context_t` should be done with |
| 76 | + * `rcl_get_zero_initialized_context()`, which ensures the context is in a safe |
| 77 | + * state for initialization with `rcl_init()`. |
| 78 | + * |
| 79 | + * Initialization of an `rcl_context_t` should be done with `rcl_init()`, after |
| 80 | + * which the context is considered both initialized and valid. |
| 81 | + * After initialization it can be used in the creation of other entities like |
| 82 | + * nodes and guard conditions. |
| 83 | + * |
| 84 | + * At any time the context can be invalidated by calling `rcl_shutdown()` on |
| 85 | + * the `rcl_context_t`, after which the context is still initialized but now |
| 86 | + * invalid. |
| 87 | + * |
| 88 | + * Invalidation indicates to other entities that the context was shutdown, but |
| 89 | + * is still accessible for use during cleanup of themselves. |
| 90 | + * |
| 91 | + * After being invalidated, and after all of the entities which used it have |
| 92 | + * been finalized, the context should be finalized with `rcl_context_fini()`. |
| 93 | + * |
| 94 | + * Finalizing the context while entities which have copies of it have not yet |
| 95 | + * been finalized is undefined behavior. |
| 96 | + * Therefore, the context's lifetime (between calls to `rcl_init()` and |
| 97 | + * `rcl_context_fini()`) should exceed the lifetime of all entities which use |
| 98 | + * it directly (e.g. nodes and guard conditions) or indirectly (e.g. |
| 99 | + * subscriptions and topics). |
| 100 | + */ |
| 101 | +typedef struct rcl_context_t |
| 102 | +{ |
| 103 | + /// Global arguments for all nodes which share this context. |
| 104 | + /** Typically generated by the parsing of argc/argv in `rcl_init()`. */ |
| 105 | + rcl_arguments_t global_arguments; |
| 106 | + |
| 107 | + /// Implementation specific pointer. |
| 108 | + struct rcl_context_impl_t * impl; |
| 109 | + |
| 110 | + // The assumption that this is big enough for an atomic_uint_least64_t is |
| 111 | + // ensured with a static_assert in the context.c file. |
| 112 | + // In most cases it should just be a plain uint64_t. |
| 113 | +#if !defined(RCL_CONTEXT_ATOMIC_INSTANCE_ID_STORAGE_SIZE) |
| 114 | +#define RCL_CONTEXT_ATOMIC_INSTANCE_ID_STORAGE_SIZE sizeof(uint_least64_t) |
| 115 | +#endif |
| 116 | + /// Private storage for instance ID atomic. |
| 117 | + /** |
| 118 | + * Accessing the instance id should be done using the function |
| 119 | + * `rcl_context_get_instance_id()` because the instance id's type is an |
| 120 | + * atomic and needs to be accessed properly to ensure safety. |
| 121 | + * |
| 122 | + * The instance id should not be changed manually - doing so is undefined |
| 123 | + * behavior. |
| 124 | + * |
| 125 | + * The instance id cannot be protected within the `impl` pointer's type |
| 126 | + * because it needs to be accessible even when the context is zero |
| 127 | + * initialized and therefore `impl` is `NULL`. |
| 128 | + * Specifically, storing the instance id in the `impl` would introduce a |
| 129 | + * race condition between accessing it and finalizing the context. |
| 130 | + * Additionally, C11 atomics (i.e. "stdatomic.h") cannot be used directly |
| 131 | + * here in the case that this header is included into a C++ program. |
| 132 | + * See this paper for an effort to make this possible in the future: |
| 133 | + * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0943r1.html |
| 134 | + */ |
| 135 | + uint8_t instance_id_storage[RCL_CONTEXT_ATOMIC_INSTANCE_ID_STORAGE_SIZE]; |
| 136 | +} rcl_context_t; |
| 137 | + |
| 138 | +/// Return a zero initialization context object. |
| 139 | +RCL_PUBLIC |
| 140 | +RCL_WARN_UNUSED |
| 141 | +rcl_context_t |
| 142 | +rcl_get_zero_initialized_context(void); |
| 143 | + |
| 144 | +// See `rcl_init()` for initialization of the context. |
| 145 | + |
| 146 | +/// Finalize a context. |
| 147 | +/** |
| 148 | + * The context to be finalized must have been previously initialized with |
| 149 | + * `rcl_init()`, and then later invalidated with `rcl_shutdown()`. |
| 150 | + * If context is `NULL`, then `RCL_RET_INVALID_ARGUMENT` is returned. |
| 151 | + * If context is zero-initialized, then `RCL_RET_INVALID_ARGUMENT` is returned. |
| 152 | + * If context is initialized and valid (`rcl_shutdown()` was not called on it), |
| 153 | + * then `RCL_RET_INVALID_ARGUMENT` is returned. |
| 154 | + * |
| 155 | + * <hr> |
| 156 | + * Attribute | Adherence |
| 157 | + * ------------------ | ------------- |
| 158 | + * Allocates Memory | Yes |
| 159 | + * Thread-Safe | No |
| 160 | + * Uses Atomics | Yes |
| 161 | + * Lock-Free | Yes [1] |
| 162 | + * <i>[1] if `atomic_is_lock_free()` returns true for `atomic_uint_least64_t`</i> |
| 163 | + * |
| 164 | + * \return `RCL_RET_OK` if the shutdown was completed successfully, or |
| 165 | + * \return `RCL_RET_INVALID_ARGUMENT` if any arguments are invalid, or |
| 166 | + * \return `RCL_RET_ERROR` if an unspecified error occur. |
| 167 | + */ |
| 168 | +RCL_PUBLIC |
| 169 | +RCL_WARN_UNUSED |
| 170 | +rcl_ret_t |
| 171 | +rcl_context_fini(rcl_context_t * context); |
| 172 | + |
| 173 | +// See `rcl_shutdown()` for invalidation of the context. |
| 174 | + |
| 175 | +/// Return the init options used during initialization for this context. |
| 176 | +/** |
| 177 | + * This function can fail and return `NULL` if: |
| 178 | + * - context is NULL |
| 179 | + * - context is zero-initialized, e.g. context->impl is `NULL` |
| 180 | + * |
| 181 | + * If context is uninitialized then that is undefined behavior. |
| 182 | + * |
| 183 | + * If `NULL` is returned an error message will have been set. |
| 184 | + * |
| 185 | + * The options are for reference only, and therefore the returned pointer is |
| 186 | + * const. |
| 187 | + * Changing the values in the options is undefined behavior but will likely |
| 188 | + * have no effect. |
| 189 | + * |
| 190 | + * <hr> |
| 191 | + * Attribute | Adherence |
| 192 | + * ------------------ | ------------- |
| 193 | + * Allocates Memory | No |
| 194 | + * Thread-Safe | Yes |
| 195 | + * Uses Atomics | Yes |
| 196 | + * Lock-Free | Yes |
| 197 | + * |
| 198 | + * \param[in] context object from which the init options should be retrieved |
| 199 | + * \return pointer to the the init options, or |
| 200 | + * \return `NULL` if there was an error |
| 201 | + */ |
| 202 | +RCL_PUBLIC |
| 203 | +RCL_WARN_UNUSED |
| 204 | +const rcl_init_options_t * |
| 205 | +rcl_context_get_init_options(rcl_context_t * context); |
| 206 | + |
| 207 | +/// Returns an unsigned integer that is unique to the given context, or `0` if invalid. |
| 208 | +/** |
| 209 | + * The given context must be non-`NULL`, but does not need to be initialized or valid. |
| 210 | + * If context is `NULL`, then `0` will be returned. |
| 211 | + * If context is uninitialized, then it is undefined behavior. |
| 212 | + * |
| 213 | + * The instance ID may be `0` if the context is zero-initialized or if the |
| 214 | + * context has been invalidated by `rcl_shutdown()`. |
| 215 | + * |
| 216 | + * <hr> |
| 217 | + * Attribute | Adherence |
| 218 | + * ------------------ | ------------- |
| 219 | + * Allocates Memory | No |
| 220 | + * Thread-Safe | Yes |
| 221 | + * Uses Atomics | Yes |
| 222 | + * Lock-Free | Yes [1] |
| 223 | + * <i>[1] if `atomic_is_lock_free()` returns true for `atomic_uint_least64_t`</i> |
| 224 | + * |
| 225 | + * \param[in] context object from which the instance id should be retrieved |
| 226 | + * \return a unique id specific to this context instance, or |
| 227 | + * \return `0` if invalid, or |
| 228 | + * \return `0` if context is `NULL` |
| 229 | + */ |
| 230 | +RCL_PUBLIC |
| 231 | +RCL_WARN_UNUSED |
| 232 | +rcl_context_instance_id_t |
| 233 | +rcl_context_get_instance_id(rcl_context_t * context); |
| 234 | + |
| 235 | +/// Return `true` if the given context is currently valid, otherwise `false`. |
| 236 | +/** |
| 237 | + * If context is `NULL`, then `false` is returned. |
| 238 | + * If context is zero-initialized, then `false` is returned. |
| 239 | + * If context is uninitialized, then it is undefined behavior. |
| 240 | + * |
| 241 | + * Attribute | Adherence |
| 242 | + * ------------------ | ------------- |
| 243 | + * Allocates Memory | No |
| 244 | + * Thread-Safe | Yes |
| 245 | + * Uses Atomics | Yes |
| 246 | + * Lock-Free | Yes [1] |
| 247 | + * <i>[1] if `atomic_is_lock_free()` returns true for `atomic_uint_least64_t`</i> |
| 248 | + * |
| 249 | + * \param[in] context object which should be checked for validity |
| 250 | + * \return `true` if valid, otherwise `false` |
| 251 | + */ |
| 252 | +RCL_PUBLIC |
| 253 | +RCL_WARN_UNUSED |
| 254 | +bool |
| 255 | +rcl_context_is_valid(rcl_context_t * context); |
| 256 | + |
| 257 | +#ifdef __cplusplus |
| 258 | +} |
| 259 | +#endif |
| 260 | + |
| 261 | +#endif // RCL__CONTEXT_H_ |
0 commit comments