-
Notifications
You must be signed in to change notification settings - Fork 163
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor init to not be global (#336)
* refactor init to not be global Signed-off-by: William Woodall <[email protected]> * style changes Signed-off-by: William Woodall <[email protected]> * refactor to hide use of C11 atomics in implementation Signed-off-by: William Woodall <[email protected]> * fix new action tests Signed-off-by: William Woodall <[email protected]> * use alternative atomic init for Windows support * updates after rebase Signed-off-by: William Woodall <[email protected]> * cleanup rmw_init_options before copying Signed-off-by: William Woodall <[email protected]> * fix two bugs in new init code * relax validity checks in a few places to facilitate post shutdown cleanup Signed-off-by: William Woodall <[email protected]> * fixing tests for new API behavior Signed-off-by: William Woodall <[email protected]> * to allocator -> to allocate * acutally call rmw_shutdown() and address review comments Signed-off-by: William Woodall <[email protected]>
- Loading branch information
Showing
55 changed files
with
1,950 additions
and
628 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,261 @@ | ||
// Copyright 2018 Open Source Robotics Foundation, Inc. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
#ifndef RCL__CONTEXT_H_ | ||
#define RCL__CONTEXT_H_ | ||
|
||
#ifdef __cplusplus | ||
extern "C" | ||
{ | ||
#endif | ||
|
||
#include "rmw/init.h" | ||
|
||
#include "rcl/allocator.h" | ||
#include "rcl/arguments.h" | ||
#include "rcl/init_options.h" | ||
#include "rcl/macros.h" | ||
#include "rcl/types.h" | ||
#include "rcl/visibility_control.h" | ||
|
||
typedef uint64_t rcl_context_instance_id_t; | ||
|
||
struct rcl_context_impl_t; | ||
|
||
/// Encapsulates the non-global state of an init/shutdown cycle. | ||
/** | ||
* The context is used in the creation of top level entities like nodes and | ||
* guard conditions, as well as to shutdown a specific instance of init. | ||
* | ||
* Here is a diagram of a typical context's lifecycle: | ||
* | ||
* ``` | ||
* +---------------+ | ||
* | | | ||
* +--> uninitialized +---> rcl_get_zero_initialized_context() + | ||
* | | | | | ||
* | +---------------+ | | ||
* | | | ||
* | +-----------------------------------------------+ | ||
* | | | ||
* | +--------v---------+ +-----------------------+ | ||
* | | | | | | ||
* | | zero-initialized +-> rcl_init() +-> initialized and valid +-> rcl_shutdown() + | ||
* | | | | | | | ||
* | +------------------+ +-----------------------+ | | ||
* | | | ||
* | +-----------------------------------------------------------------+ | ||
* | | | ||
* | +------------v------------+ | ||
* | | | | ||
* | | initialized but invalid +---> finalize all entities, then rcl_context_fini() + | ||
* | | | | | ||
* | +-------------------------+ | | ||
* | | | ||
* +---------------------------------------------------------------------------------+ | ||
* ``` | ||
* | ||
* A declared but not defined `rcl_context_t` instance is considered to be | ||
* "uninitialized", and passing an uninitialized context to any functions will | ||
* result in undefined behavior. | ||
* Some functions, like `rcl_init()` require the context instance to be | ||
* zero initialized (all members set to "zero" state) before use. | ||
* | ||
* Zero initialization of an `rcl_context_t` should be done with | ||
* `rcl_get_zero_initialized_context()`, which ensures the context is in a safe | ||
* state for initialization with `rcl_init()`. | ||
* | ||
* Initialization of an `rcl_context_t` should be done with `rcl_init()`, after | ||
* which the context is considered both initialized and valid. | ||
* After initialization it can be used in the creation of other entities like | ||
* nodes and guard conditions. | ||
* | ||
* At any time the context can be invalidated by calling `rcl_shutdown()` on | ||
* the `rcl_context_t`, after which the context is still initialized but now | ||
* invalid. | ||
* | ||
* Invalidation indicates to other entities that the context was shutdown, but | ||
* is still accessible for use during cleanup of themselves. | ||
* | ||
* After being invalidated, and after all of the entities which used it have | ||
* been finalized, the context should be finalized with `rcl_context_fini()`. | ||
* | ||
* Finalizing the context while entities which have copies of it have not yet | ||
* been finalized is undefined behavior. | ||
* Therefore, the context's lifetime (between calls to `rcl_init()` and | ||
* `rcl_context_fini()`) should exceed the lifetime of all entities which use | ||
* it directly (e.g. nodes and guard conditions) or indirectly (e.g. | ||
* subscriptions and topics). | ||
*/ | ||
typedef struct rcl_context_t | ||
{ | ||
/// Global arguments for all nodes which share this context. | ||
/** Typically generated by the parsing of argc/argv in `rcl_init()`. */ | ||
rcl_arguments_t global_arguments; | ||
|
||
/// Implementation specific pointer. | ||
struct rcl_context_impl_t * impl; | ||
|
||
// The assumption that this is big enough for an atomic_uint_least64_t is | ||
// ensured with a static_assert in the context.c file. | ||
// In most cases it should just be a plain uint64_t. | ||
#if !defined(RCL_CONTEXT_ATOMIC_INSTANCE_ID_STORAGE_SIZE) | ||
#define RCL_CONTEXT_ATOMIC_INSTANCE_ID_STORAGE_SIZE sizeof(uint_least64_t) | ||
#endif | ||
/// Private storage for instance ID atomic. | ||
/** | ||
* Accessing the instance id should be done using the function | ||
* `rcl_context_get_instance_id()` because the instance id's type is an | ||
* atomic and needs to be accessed properly to ensure safety. | ||
* | ||
* The instance id should not be changed manually - doing so is undefined | ||
* behavior. | ||
* | ||
* The instance id cannot be protected within the `impl` pointer's type | ||
* because it needs to be accessible even when the context is zero | ||
* initialized and therefore `impl` is `NULL`. | ||
* Specifically, storing the instance id in the `impl` would introduce a | ||
* race condition between accessing it and finalizing the context. | ||
* Additionally, C11 atomics (i.e. "stdatomic.h") cannot be used directly | ||
* here in the case that this header is included into a C++ program. | ||
* See this paper for an effort to make this possible in the future: | ||
* http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0943r1.html | ||
*/ | ||
uint8_t instance_id_storage[RCL_CONTEXT_ATOMIC_INSTANCE_ID_STORAGE_SIZE]; | ||
} rcl_context_t; | ||
|
||
/// Return a zero initialization context object. | ||
RCL_PUBLIC | ||
RCL_WARN_UNUSED | ||
rcl_context_t | ||
rcl_get_zero_initialized_context(void); | ||
|
||
// See `rcl_init()` for initialization of the context. | ||
|
||
/// Finalize a context. | ||
/** | ||
* The context to be finalized must have been previously initialized with | ||
* `rcl_init()`, and then later invalidated with `rcl_shutdown()`. | ||
* If context is `NULL`, then `RCL_RET_INVALID_ARGUMENT` is returned. | ||
* If context is zero-initialized, then `RCL_RET_INVALID_ARGUMENT` is returned. | ||
* If context is initialized and valid (`rcl_shutdown()` was not called on it), | ||
* then `RCL_RET_INVALID_ARGUMENT` is returned. | ||
* | ||
* <hr> | ||
* Attribute | Adherence | ||
* ------------------ | ------------- | ||
* Allocates Memory | Yes | ||
* Thread-Safe | No | ||
* Uses Atomics | Yes | ||
* Lock-Free | Yes [1] | ||
* <i>[1] if `atomic_is_lock_free()` returns true for `atomic_uint_least64_t`</i> | ||
* | ||
* \return `RCL_RET_OK` if the shutdown was completed successfully, or | ||
* \return `RCL_RET_INVALID_ARGUMENT` if any arguments are invalid, or | ||
* \return `RCL_RET_ERROR` if an unspecified error occur. | ||
*/ | ||
RCL_PUBLIC | ||
RCL_WARN_UNUSED | ||
rcl_ret_t | ||
rcl_context_fini(rcl_context_t * context); | ||
|
||
// See `rcl_shutdown()` for invalidation of the context. | ||
|
||
/// Return the init options used during initialization for this context. | ||
/** | ||
* This function can fail and return `NULL` if: | ||
* - context is NULL | ||
* - context is zero-initialized, e.g. context->impl is `NULL` | ||
* | ||
* If context is uninitialized then that is undefined behavior. | ||
* | ||
* If `NULL` is returned an error message will have been set. | ||
* | ||
* The options are for reference only, and therefore the returned pointer is | ||
* const. | ||
* Changing the values in the options is undefined behavior but will likely | ||
* have no effect. | ||
* | ||
* <hr> | ||
* Attribute | Adherence | ||
* ------------------ | ------------- | ||
* Allocates Memory | No | ||
* Thread-Safe | Yes | ||
* Uses Atomics | Yes | ||
* Lock-Free | Yes | ||
* | ||
* \param[in] context object from which the init options should be retrieved | ||
* \return pointer to the the init options, or | ||
* \return `NULL` if there was an error | ||
*/ | ||
RCL_PUBLIC | ||
RCL_WARN_UNUSED | ||
const rcl_init_options_t * | ||
rcl_context_get_init_options(rcl_context_t * context); | ||
|
||
/// Returns an unsigned integer that is unique to the given context, or `0` if invalid. | ||
/** | ||
* The given context must be non-`NULL`, but does not need to be initialized or valid. | ||
* If context is `NULL`, then `0` will be returned. | ||
* If context is uninitialized, then it is undefined behavior. | ||
* | ||
* The instance ID may be `0` if the context is zero-initialized or if the | ||
* context has been invalidated by `rcl_shutdown()`. | ||
* | ||
* <hr> | ||
* Attribute | Adherence | ||
* ------------------ | ------------- | ||
* Allocates Memory | No | ||
* Thread-Safe | Yes | ||
* Uses Atomics | Yes | ||
* Lock-Free | Yes [1] | ||
* <i>[1] if `atomic_is_lock_free()` returns true for `atomic_uint_least64_t`</i> | ||
* | ||
* \param[in] context object from which the instance id should be retrieved | ||
* \return a unique id specific to this context instance, or | ||
* \return `0` if invalid, or | ||
* \return `0` if context is `NULL` | ||
*/ | ||
RCL_PUBLIC | ||
RCL_WARN_UNUSED | ||
rcl_context_instance_id_t | ||
rcl_context_get_instance_id(rcl_context_t * context); | ||
|
||
/// Return `true` if the given context is currently valid, otherwise `false`. | ||
/** | ||
* If context is `NULL`, then `false` is returned. | ||
* If context is zero-initialized, then `false` is returned. | ||
* If context is uninitialized, then it is undefined behavior. | ||
* | ||
* Attribute | Adherence | ||
* ------------------ | ------------- | ||
* Allocates Memory | No | ||
* Thread-Safe | Yes | ||
* Uses Atomics | Yes | ||
* Lock-Free | Yes [1] | ||
* <i>[1] if `atomic_is_lock_free()` returns true for `atomic_uint_least64_t`</i> | ||
* | ||
* \param[in] context object which should be checked for validity | ||
* \return `true` if valid, otherwise `false` | ||
*/ | ||
RCL_PUBLIC | ||
RCL_WARN_UNUSED | ||
bool | ||
rcl_context_is_valid(rcl_context_t * context); | ||
|
||
#ifdef __cplusplus | ||
} | ||
#endif | ||
|
||
#endif // RCL__CONTEXT_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.