rcl/rcl/include/rcl/node.h
jwang11 ddc172a7e6 expand rcl_node_is_valid with allocator to standarize the node check … (#173)
* expand rcl_node_is_valid with allocator to standarize the node check across RCL

Current RCL use very different way for node check in different components. Actually,
node.c already provide a standard way - rcl_node_is_valid. However, it just use
internal allocator, but some context has external allocator. The commit expand
rcl_node_is_valid with one more parameter for allocator. It can be used in all cases.

Signed-off-by: jwang <jing.j.wang@intel.com>

* Add allocator check in rcl_node_is_valid

* Separate argument checks from the concept of what makes a node invalid

* clarify allocator only used for error messages; default if NULL

* shorten parameter description
2017-11-15 18:42:07 -08:00

459 lines
15 KiB
C

// Copyright 2015 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__NODE_H_
#define RCL__NODE_H_
#if __cplusplus
extern "C"
{
#endif
#include <stdint.h>
#include "rcl/allocator.h"
#include "rcl/macros.h"
#include "rcl/types.h"
#include "rcl/visibility_control.h"
/// Constant which indicates that the default domain id should be used.
#define RCL_NODE_OPTIONS_DEFAULT_DOMAIN_ID SIZE_MAX
struct rcl_guard_condition_t;
struct rcl_node_impl_t;
/// Structure which encapsulates a ROS Node.
typedef struct rcl_node_t
{
/// Private implementation pointer.
struct rcl_node_impl_t * impl;
} rcl_node_t;
/// Structure which encapsulates the options for creating a rcl_node_t.
typedef struct rcl_node_options_t
{
// bool anonymous_name;
// rmw_qos_profile_t parameter_qos;
/// If true, no parameter infrastructure will be setup.
// bool no_parameters;
/// If set, then this value overrides the ROS_DOMAIN_ID environment variable.
/**
* It defaults to RCL_NODE_OPTIONS_DEFAULT_DOMAIN_ID, which will cause the
* node to use the ROS domain ID set in the ROS_DOMAIN_ID environment
* variable, or on some systems 0 if the environment variable is not set.
*
* \todo TODO(wjwwood):
* Should we put a limit on the ROS_DOMAIN_ID value, that way we can have
* a safe value for the default RCL_NODE_OPTIONS_DEFAULT_DOMAIN_ID?
* (currently max size_t)
*/
size_t domain_id;
/// Custom allocator used for internal allocations.
rcl_allocator_t allocator;
} rcl_node_options_t;
/// Return a rcl_node_t struct with members initialized to `NULL`.
RCL_PUBLIC
RCL_WARN_UNUSED
rcl_node_t
rcl_get_zero_initialized_node(void);
/// Initialize a ROS node.
/**
* Calling this on a rcl_node_t makes it a valid node handle until rcl_shutdown
* is called or until rcl_node_fini is called on it.
*
* After calling, the ROS node object can be used to create other middleware
* primitives like publishers, services, parameters, etc.
*
* The name of the node must not be NULL and adhere to naming restrictions,
* see the rmw_validate_node_name() function for rules.
*
* \todo TODO(wjwwood): node name uniqueness is no yet enforced
*
* The name of the node cannot coincide with another node of the same name.
* If a node of the same name is already in the domain, it will be shutdown.
*
* The namespace of the node should not be NULL and should also pass the
* rmw_validate_namespace() function's rules.
*
* Additionally this function allows namespaces which lack a leading forward
* slash.
* Because there is no notion of a relative namespace, there is no difference
* between a namespace which lacks a forward and the same namespace with a
* leasing forward slash.
* Therefore, a namespace like ``"foo/bar"`` is automatically changed to
* ``"/foo/bar"`` by this function.
* Similarly, the namespace ``""`` will implicitly become ``"/"`` which is a
* valid namespace.
*
* \todo TODO(wjwwood):
* Parameter infrastructure is currently initialized in the language specific
* client library, e.g. rclcpp for C++, but will be initialized here in the
* future. When that happens there will be an option to avoid parameter
* infrastructure with an option in the rcl_node_options_t struct.
*
* A node contains infrastructure for ROS parameters, which include advertising
* publishers and service servers.
* This function will create those external parameter interfaces even if
* parameters are not used later.
*
* The rcl_node_t given must be allocated and zero initialized.
* Passing an rcl_node_t which has already had this function called on it, more
* recently than rcl_node_fini, will fail.
* An allocated rcl_node_t with uninitialized memory is undefined behavior.
*
* Expected usage:
*
* ```c
* rcl_node_t node = rcl_get_zero_initialized_node();
* rcl_node_options_t node_ops = rcl_node_get_default_options();
* // ... node options customization
* rcl_ret_t ret = rcl_node_init(&node, "node_name", "/node_ns", &node_ops);
* // ... error handling and then use the node, but eventually deinitialize it:
* ret = rcl_node_fini(&node);
* // ... error handling for rcl_node_fini()
* ```
*
* <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>
*
* \pre the node handle must be allocated, zero initialized, and invalid
* \post the node handle is valid and can be used in other `rcl_*` functions
*
* \param[inout] node a preallocated rcl_node_t
* \param[in] name the name of the node, must be a valid c-string
* \param[in] namespace_ the namespace of the node, must be a valid c-string
* \param[in] options the node options
* \return `RCL_RET_OK` if the node was initialized successfully, or
* \return `RCL_RET_ALREADY_INIT` if the node has already be initialized, or
* \return `RCL_RET_INVALID_ARGUMENT` if any arguments are invalid, or
* \return `RCL_RET_BAD_ALLOC` if allocating memory failed, or
* \return `RCL_RET_NODE_INVALID_NAME` if the name is invalid, or
* \return `RCL_RET_NODE_INVALID_NAMESPACE` if the namespace_ is invalid, or
* \return `RCL_RET_ERROR` if an unspecified error occurs.
*/
RCL_PUBLIC
RCL_WARN_UNUSED
rcl_ret_t
rcl_node_init(
rcl_node_t * node,
const char * name,
const char * namespace_,
const rcl_node_options_t * options);
/// Finalized a rcl_node_t.
/**
* Destroys any automatically created infrastructure and deallocates memory.
* After calling, the rcl_node_t can be safely deallocated.
*
* Any middleware primitives created by the user, e.g. publishers, services, etc.,
* are invalid after deinitialization.
*
* <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>
*
* \param[in] node rcl_node_t to be finalized
* \return `RCL_RET_OK` if node was finalized successfully, or
* \return `RCL_RET_INVALID_ARGUMENT` if any arguments are invalid, or
* \return `RCL_RET_ERROR` if an unspecified error occurs.
*/
RCL_PUBLIC
RCL_WARN_UNUSED
rcl_ret_t
rcl_node_fini(rcl_node_t * node);
/// Return the default node options in a rcl_node_options_t.
/**
* The default values are:
*
* - domain_id = RCL_NODE_OPTIONS_DEFAULT_DOMAIN_ID
* - allocator = rcl_get_default_allocator()
*/
RCL_PUBLIC
rcl_node_options_t
rcl_node_get_default_options(void);
/// Return `true` if the node is valid, else `false`.
/**
* Also return `false` if the node pointer is `NULL` or the allocator is invalid.
*
* The allocator needs to either be a valid allocator or `NULL`, in which case
* the default allocator will be used.
* The allocator is used when allocation is needed for an error message.
*
* A node is invalid if:
* - the implementation is `NULL` (rcl_node_init not called or failed)
* - rcl_shutdown has been called since the node has been initialized
* - the node has been finalized with rcl_node_fini
*
* There is a possible validity race condition.
*
* Consider:
*
* ```c
* assert(rcl_node_is_valid(node, NULL)); // <-- thread 1
* rcl_shutdown(); // <-- thread 2
* // use node as if valid // <-- thread 1
* ```
*
* In the third line the node is now invalid, even though on the previous line
* of thread 1 it was checked to be valid.
* This is why this function is considered not thread-safe.
*
* <hr>
* Attribute | Adherence
* ------------------ | -------------
* Allocates Memory | No
* 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>
*
* \param[in] node rcl_node_t to be validated
* \param[in] allocator a valid allocator or `NULL`
* \return `true` if the node and allocator are valid, otherwise `false`.
*/
RCL_PUBLIC
bool
rcl_node_is_valid(const rcl_node_t * node, const rcl_allocator_t * allocator);
/// Return the name of the node.
/**
* This function returns the node's internal name string.
* This function can fail, and therefore return `NULL`, if:
* - node is `NULL`
* - node has not been initialized (the implementation is invalid)
*
* The returned string is only valid as long as the given rcl_node_t is valid.
* The value of the string may change if the value in the rcl_node_t changes,
* and therefore copying the string is recommended if this is a concern.
*
* <hr>
* Attribute | Adherence
* ------------------ | -------------
* Allocates Memory | No
* Thread-Safe | No
* Uses Atomics | No
* Lock-Free | Yes
*
* \param[in] node pointer to the node
* \return name string if successful, otherwise `NULL`
*/
RCL_PUBLIC
RCL_WARN_UNUSED
const char *
rcl_node_get_name(const rcl_node_t * node);
/// Return the namespace of the node.
/**
* This function returns the node's internal namespace string.
* This function can fail, and therefore return `NULL`, if:
* - node is `NULL`
* - node has not been initialized (the implementation is invalid)
*
* The returned string is only valid as long as the given rcl_node_t is valid.
* The value of the string may change if the value in the rcl_node_t changes,
* and therefore copying the string is recommended if this is a concern.
*
* <hr>
* Attribute | Adherence
* ------------------ | -------------
* Allocates Memory | No
* Thread-Safe | No
* Uses Atomics | No
* Lock-Free | Yes
*
* \param[in] node pointer to the node
* \return name string if successful, otherwise `NULL`
*/
RCL_PUBLIC
RCL_WARN_UNUSED
const char *
rcl_node_get_namespace(const rcl_node_t * node);
/// Return the rcl node options.
/**
* This function returns the node's internal options struct.
* This function can fail, and therefore return `NULL`, if:
* - node is `NULL`
* - node has not been initialized (the implementation is invalid)
*
* The returned struct is only valid as long as the given rcl_node_t is valid.
* The values in the struct may change if the options of the rcl_node_t changes,
* and therefore copying the struct is recommended if this is a concern.
*
* <hr>
* Attribute | Adherence
* ------------------ | -------------
* Allocates Memory | No
* Thread-Safe | No
* Uses Atomics | No
* Lock-Free | Yes
*
* \param[in] node pointer to the node
* \return options struct if successful, otherwise `NULL`
*/
RCL_PUBLIC
RCL_WARN_UNUSED
const rcl_node_options_t *
rcl_node_get_options(const rcl_node_t * node);
/// Return the ROS domain ID that the node is using.
/**
* This function returns the ROS domain ID that the node is in.
*
* This function should be used to determine what `domain_id` was used rather
* than checking the domin_id field in the node options, because if
* `RCL_NODE_OPTIONS_DEFAULT_DOMAIN_ID` is used when creating the node then
* it is not changed after creation, but this function will return the actual
* `domain_id` used.
*
* The `domain_id` field must point to an allocated `size_t` object to which
* the ROS domain ID will be written.
*
* <hr>
* Attribute | Adherence
* ------------------ | -------------
* Allocates Memory | No
* Thread-Safe | No
* Uses Atomics | No
* Lock-Free | Yes
*
* \param[in] node the handle to the node being queried
* \param[out] domain_id storage for the domain id
* \return `RCL_RET_OK` if node the domain ID was retrieved successfully, or
* \return `RCL_RET_NODE_INVALID` if the node is invalid, or
* \return `RCL_RET_INVALID_ARGUMENT` if any arguments are invalid, or
* \return `RCL_RET_ERROR` if an unspecified error occurs.
*/
RCL_PUBLIC
RCL_WARN_UNUSED
rcl_ret_t
rcl_node_get_domain_id(const rcl_node_t * node, size_t * domain_id);
/// Return the rmw node handle.
/**
* The handle returned is a pointer to the internally held rmw handle.
* This function can fail, and therefore return `NULL`, if:
* - node is `NULL`
* - node has not been initialized (the implementation is invalid)
*
* The returned handle is made invalid if the node is finalized or if
* rcl_shutdown() is called.
* The returned handle is not guaranteed to be valid for the life time of the
* node as it may be finalized and recreated itself.
* Therefore it is recommended to get the handle from the node using
* this function each time it is needed and avoid use of the handle
* concurrently with functions that might change it.
*
* <hr>
* Attribute | Adherence
* ------------------ | -------------
* Allocates Memory | No
* Thread-Safe | No
* Uses Atomics | No
* Lock-Free | Yes
*
* \param[in] node pointer to the rcl node
* \return rmw node handle if successful, otherwise `NULL`
*/
RCL_PUBLIC
RCL_WARN_UNUSED
rmw_node_t *
rcl_node_get_rmw_handle(const rcl_node_t * node);
/// Return the associated rcl instance id.
/**
* This id is stored when rcl_node_init is called and can be compared with the
* value returned by rcl_get_instance_id() to check if this node was created in
* the current rcl context (since the latest call to rcl_init().
*
* This function can fail, and therefore return `0`, if:
* - node is `NULL`
* - node has not been initialized (the implementation is invalid)
*
* This function will succeed even if rcl_shutdown() has been called
* since the node was created.
*
* <hr>
* Attribute | Adherence
* ------------------ | -------------
* Allocates Memory | No
* Thread-Safe | No
* Uses Atomics | No
* Lock-Free | Yes
*
* \param[in] node pointer to the rcl node
* \return rcl instance id captured during node init or `0` on error
*/
RCL_PUBLIC
RCL_WARN_UNUSED
uint64_t
rcl_node_get_rcl_instance_id(const rcl_node_t * node);
/// Return a guard condition which is triggered when the ROS graph changes.
/**
* The handle returned is a pointer to an internally held rcl guard condition.
* This function can fail, and therefore return `NULL`, if:
* - node is `NULL`
* - node is invalid
*
* The returned handle is made invalid if the node is finialized or if
* rcl_shutdown() is called.
*
* The guard condition will be triggered anytime a change to the ROS graph occurs.
* A ROS graph change includes things like (but not limited to) a new publisher
* advertises, a new subscription is created, a new service becomes available,
* a subscription is canceled, etc.
*
* \todo TODO(wjwwood): link to exhaustive list of graph events
*
* <hr>
* Attribute | Adherence
* ------------------ | -------------
* Allocates Memory | No
* Thread-Safe | No
* Uses Atomics | No
* Lock-Free | Yes
*
* \param[in] node pointer to the rcl node
* \return rcl guard condition handle if successful, otherwise `NULL`
*/
RCL_PUBLIC
RCL_WARN_UNUSED
const struct rcl_guard_condition_t *
rcl_node_get_graph_guard_condition(const rcl_node_t * node);
#if __cplusplus
}
#endif
#endif // RCL__NODE_H_