diff --git a/rcl/CMakeLists.txt b/rcl/CMakeLists.txt index 361815a..26a16df 100644 --- a/rcl/CMakeLists.txt +++ b/rcl/CMakeLists.txt @@ -17,12 +17,6 @@ if(NOT WIN32) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall -Wextra") endif() -if(WIN32) - set(time_impl_c src/rcl/time_win32.c) -else() - set(time_impl_c src/rcl/time_unix.c) -endif() - set(${PROJECT_NAME}_sources src/rcl/client.c src/rcl/common.c @@ -36,7 +30,6 @@ set(${PROJECT_NAME}_sources src/rcl/service.c src/rcl/subscription.c src/rcl/time.c - ${time_impl_c} src/rcl/timer.c src/rcl/validate_topic_name.c src/rcl/wait.c diff --git a/rcl/include/rcl/time.h b/rcl/include/rcl/time.h index 5dfbc30..f171913 100644 --- a/rcl/include/rcl/time.h +++ b/rcl/include/rcl/time.h @@ -23,25 +23,26 @@ extern "C" #include "rcl/macros.h" #include "rcl/types.h" #include "rcl/visibility_control.h" +#include "rcutils/time.h" /// Convenience macro to convert seconds to nanoseconds. -#define RCL_S_TO_NS(seconds) (seconds * (1000 * 1000 * 1000)) +#define RCL_S_TO_NS RCUTILS_S_TO_NS /// Convenience macro to convert milliseconds to nanoseconds. -#define RCL_MS_TO_NS(milliseconds) (milliseconds * (1000 * 1000)) +#define RCL_MS_TO_NS RCUTILS_MS_TO_NS /// Convenience macro to convert microseconds to nanoseconds. -#define RCL_US_TO_NS(microseconds) (microseconds * 1000) +#define RCL_US_TO_NS RCUTILS_US_TO_NS /// Convenience macro to convert nanoseconds to seconds. -#define RCL_NS_TO_S(nanoseconds) (nanoseconds / (1000 * 1000 * 1000)) +#define RCL_NS_TO_S RCUTILS_NS_TO_S /// Convenience macro to convert nanoseconds to milliseconds. -#define RCL_NS_TO_MS(nanoseconds) (nanoseconds / (1000 * 1000)) +#define RCL_NS_TO_MS RCUTILS_NS_TO_MS /// Convenience macro to convert nanoseconds to microseconds. -#define RCL_NS_TO_US(nanoseconds) (nanoseconds / 1000) +#define RCL_NS_TO_US RCUTILS_NS_TO_US /// A single point in time, measured in nanoseconds since the Unix epoch. -typedef uint64_t rcl_time_point_value_t; +typedef rcutils_time_point_value_t rcl_time_point_value_t; /// A duration of time, measured in nanoseconds. -typedef int64_t rcl_duration_value_t; +typedef rcutils_duration_value_t rcl_duration_value_t; /// Time source type, used to indicate the source of a time measurement. enum rcl_time_source_type_t @@ -487,68 +488,6 @@ rcl_ret_t rcl_set_ros_time_override(rcl_time_source_t * time_source, rcl_time_point_value_t time_value); -/// Retrieve the current time as a rcl_time_point_value_t. -/** - * This function returns the time from a system clock. - * The closest equivalent would be to std::chrono::system_clock::now(); - * - * The resolution (e.g. nanoseconds vs microseconds) is not guaranteed. - * - * The now argument must point to an allocated rcl_system_time_point_t struct, - * as the result is copied into this variable. - * - *
- * Attribute | Adherence - * ------------------ | ------------- - * Allocates Memory | No - * Thread-Safe | Yes - * Uses Atomics | No - * Lock-Free | Yes [1] - * [1] if `atomic_is_lock_free()` returns true for `atomic_int_least64_t` - * - * \todo TODO(tfoote): consider moving this to rmw for more reuse - * - * \param[out] now a datafield in which the current time is stored - * \return `RCL_RET_OK` if the current time was successfully obtained, or - * \return `RCL_RET_INVALID_ARGUMENT` if any arguments are invalid, or - * \return `RCL_RET_ERROR` an unspecified error occur. - */ -RCL_PUBLIC -RCL_WARN_UNUSED -rcl_ret_t -rcl_system_time_now(rcl_time_point_value_t * now); - -/// Retrieve the current time as a rcl_time_point_value_t object. -/** - * This function returns the time from a monotonically increasing clock. - * The closest equivalent would be to std::chrono::steady_clock::now(); - * - * The resolution (e.g. nanoseconds vs microseconds) is not guaranteed. - * - * The now argument must point to an allocated rcl_time_point_value_t object, - * as the result is copied into this variable. - * - *
- * Attribute | Adherence - * ------------------ | ------------- - * Allocates Memory | No - * Thread-Safe | Yes - * Uses Atomics | No - * Lock-Free | Yes [1] - * [1] if `atomic_is_lock_free()` returns true for `atomic_int_least64_t` - * - * \todo TODO(tfoote): consider moving this to rmw for more reuse - * - * \param[out] now a struct in which the current time is stored - * \return `RCL_RET_OK` if the current time was successfully obtained, or - * \return `RCL_RET_INVALID_ARGUMENT` if any arguments are invalid, or - * \return `RCL_RET_ERROR` an unspecified error occur. - */ -RCL_PUBLIC -RCL_WARN_UNUSED -rcl_ret_t -rcl_steady_time_now(rcl_time_point_value_t * now); - #if __cplusplus } #endif diff --git a/rcl/src/rcl/time.c b/rcl/src/rcl/time.c index 857287b..95e7785 100644 --- a/rcl/src/rcl/time.c +++ b/rcl/src/rcl/time.c @@ -21,6 +21,7 @@ #include "./stdatomic_helper.h" #include "rcl/allocator.h" #include "rcl/error_handling.h" +#include "rcutils/time.h" // Process default ROS time sources static rcl_time_source_t * rcl_default_ros_time_source; @@ -40,7 +41,7 @@ rcl_ret_t rcl_get_steady_time(void * data, rcl_time_point_value_t * current_time) { (void)data; // unused - return rcl_steady_time_now(current_time); + return rcutils_steady_time_now(current_time); } // Implementation only @@ -48,7 +49,7 @@ rcl_ret_t rcl_get_system_time(void * data, rcl_time_point_value_t * current_time) { (void)data; // unused - return rcl_system_time_now(current_time); + return rcutils_system_time_now(current_time); } // Internal method for zeroing values on init, assumes time_source is valid diff --git a/rcl/src/rcl/time_unix.c b/rcl/src/rcl/time_unix.c deleted file mode 100644 index 2d8a2ac..0000000 --- a/rcl/src/rcl/time_unix.c +++ /dev/null @@ -1,107 +0,0 @@ -// 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. - -#if defined(WIN32) -# error time_unix.c is not intended to be used with win32 based systems -#endif // defined(WIN32) - -#if __cplusplus -extern "C" -{ -#endif - -#include "rcl/time.h" - -#if defined(__MACH__) -#include -#include -#endif // defined(__MACH__) -#include -#include -#include - -#include "./common.h" -#include "rcl/allocator.h" -#include "rcl/error_handling.h" - -#if !defined(__MACH__) // Assume clock_get_time is available on OS X. -// This id an appropriate check for clock_gettime() according to: -// http://man7.org/linux/man-pages/man2/clock_gettime.2.html -# if !defined(_POSIX_TIMERS) || !_POSIX_TIMERS -# error no monotonic clock function available -# endif // !defined(_POSIX_TIMERS) || !_POSIX_TIMERS -#endif // !defined(__MACH__) - -#define __WOULD_BE_NEGATIVE(seconds, subseconds) (seconds < 0 || (subseconds < 0 && seconds == 0)) - -rcl_ret_t -rcl_system_time_now(rcl_time_point_value_t * now) -{ - RCL_CHECK_ARGUMENT_FOR_NULL(now, RCL_RET_INVALID_ARGUMENT, rcl_get_default_allocator()); - struct timespec timespec_now; -#if defined(__MACH__) - // On OS X use clock_get_time. - clock_serv_t cclock; - mach_timespec_t mts; - host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); - clock_get_time(cclock, &mts); - mach_port_deallocate(mach_task_self(), cclock); - timespec_now.tv_sec = mts.tv_sec; - timespec_now.tv_nsec = mts.tv_nsec; -#else // defined(__MACH__) - // Otherwise use clock_gettime. - clock_gettime(CLOCK_REALTIME, ×pec_now); -#endif // defined(__MACH__) - if (__WOULD_BE_NEGATIVE(timespec_now.tv_sec, timespec_now.tv_nsec)) { - RCL_SET_ERROR_MSG("unexpected negative time", rcl_get_default_allocator()); - return RCL_RET_ERROR; - } - *now = RCL_S_TO_NS((uint64_t)timespec_now.tv_sec) + timespec_now.tv_nsec; - return RCL_RET_OK; -} - -rcl_ret_t -rcl_steady_time_now(rcl_time_point_value_t * now) -{ - RCL_CHECK_ARGUMENT_FOR_NULL(now, RCL_RET_INVALID_ARGUMENT, rcl_get_default_allocator()); - // If clock_gettime is available or on OS X, use a timespec. - struct timespec timespec_now; -#if defined(__MACH__) - // On OS X use clock_get_time. - clock_serv_t cclock; - mach_timespec_t mts; - host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); - clock_get_time(cclock, &mts); - mach_port_deallocate(mach_task_self(), cclock); - timespec_now.tv_sec = mts.tv_sec; - timespec_now.tv_nsec = mts.tv_nsec; -#else // defined(__MACH__) - // Otherwise use clock_gettime. -#if defined(CLOCK_MONOTONIC_RAW) - clock_gettime(CLOCK_MONOTONIC_RAW, ×pec_now); -#else // defined(CLOCK_MONOTONIC_RAW) - clock_gettime(CLOCK_MONOTONIC, ×pec_now); -#endif // defined(CLOCK_MONOTONIC_RAW) -#endif // defined(__MACH__) - if (__WOULD_BE_NEGATIVE(timespec_now.tv_sec, timespec_now.tv_nsec)) { - RCL_SET_ERROR_MSG("unexpected negative time", rcl_get_default_allocator()); - return RCL_RET_ERROR; - } - *now = RCL_S_TO_NS((uint64_t)timespec_now.tv_sec) + timespec_now.tv_nsec; - return RCL_RET_OK; -} - -#if __cplusplus -} -#endif diff --git a/rcl/src/rcl/time_win32.c b/rcl/src/rcl/time_win32.c deleted file mode 100644 index 2dc3584..0000000 --- a/rcl/src/rcl/time_win32.c +++ /dev/null @@ -1,70 +0,0 @@ -// 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 WIN32 -# error time_win32.c is only intended to be used with win32 based systems -#endif - -#if __cplusplus -extern "C" -{ -#endif - -#include "rcl/time.h" - -#include - -#include "./common.h" -#include "./stdatomic_helper.h" -#include "rcl/allocator.h" -#include "rcl/error_handling.h" - -rcl_ret_t -rcl_system_time_now(rcl_time_point_value_t * now) -{ - RCL_CHECK_ARGUMENT_FOR_NULL(now, RCL_RET_INVALID_ARGUMENT, rcl_get_default_allocator()); - FILETIME ft; - GetSystemTimeAsFileTime(&ft); - ULARGE_INTEGER uli; - uli.LowPart = ft.dwLowDateTime; - uli.HighPart = ft.dwHighDateTime; - // Adjust for January 1st, 1970, see: - // https://support.microsoft.com/en-us/kb/167296 - uli.QuadPart -= 116444736000000000; - // Convert to nanoseconds from 100's of nanoseconds. - *now = uli.QuadPart * 100; - return RCL_RET_OK; -} - -rcl_ret_t -rcl_steady_time_now(rcl_time_point_value_t * now) -{ - RCL_CHECK_ARGUMENT_FOR_NULL(now, RCL_RET_INVALID_ARGUMENT, rcl_get_default_allocator()); - LARGE_INTEGER cpu_frequency, performance_count; - // These should not ever fail since XP is already end of life: - // From https://msdn.microsoft.com/en-us/library/windows/desktop/ms644905(v=vs.85).aspx and - // https://msdn.microsoft.com/en-us/library/windows/desktop/ms644904(v=vs.85).aspx: - // "On systems that run Windows XP or later, the function will always succeed and will - // thus never return zero." - QueryPerformanceFrequency(&cpu_frequency); - QueryPerformanceCounter(&performance_count); - // Convert to nanoseconds before converting from ticks to avoid precision loss. - rcl_time_point_value_t intermediate = RCL_S_TO_NS(performance_count.QuadPart); - *now = intermediate / cpu_frequency.QuadPart; - return RCL_RET_OK; -} - -#if __cplusplus -} -#endif diff --git a/rcl/src/rcl/timer.c b/rcl/src/rcl/timer.c index 89795b8..72f13ed 100644 --- a/rcl/src/rcl/timer.c +++ b/rcl/src/rcl/timer.c @@ -21,6 +21,7 @@ extern "C" #include "./common.h" #include "./stdatomic_helper.h" +#include "rcutils/time.h" typedef struct rcl_timer_impl_t { @@ -62,7 +63,7 @@ rcl_timer_init( return RCL_RET_ALREADY_INIT; } rcl_time_point_value_t now_steady; - rcl_ret_t now_ret = rcl_steady_time_now(&now_steady); + rcl_ret_t now_ret = rcutils_steady_time_now(&now_steady); if (now_ret != RCL_RET_OK) { return now_ret; // rcl error state should already be set. } @@ -105,7 +106,7 @@ rcl_timer_call(rcl_timer_t * timer) return RCL_RET_TIMER_CANCELED; } rcl_time_point_value_t now_steady; - rcl_ret_t now_ret = rcl_steady_time_now(&now_steady); + rcl_ret_t now_ret = rcutils_steady_time_now(&now_steady); if (now_ret != RCL_RET_OK) { return now_ret; // rcl error state should already be set. } @@ -149,7 +150,7 @@ rcl_timer_get_time_until_next_call(const rcl_timer_t * timer, int64_t * time_unt } RCL_CHECK_ARGUMENT_FOR_NULL(time_until_next_call, RCL_RET_INVALID_ARGUMENT, *allocator); rcl_time_point_value_t now; - rcl_ret_t ret = rcl_steady_time_now(&now); + rcl_ret_t ret = rcutils_steady_time_now(&now); if (ret != RCL_RET_OK) { return ret; // rcl error state should already be set. } @@ -171,7 +172,7 @@ rcl_timer_get_time_since_last_call( } RCL_CHECK_ARGUMENT_FOR_NULL(time_since_last_call, RCL_RET_INVALID_ARGUMENT, *allocator); rcl_time_point_value_t now; - rcl_ret_t ret = rcl_steady_time_now(&now); + rcl_ret_t ret = rcutils_steady_time_now(&now); if (ret != RCL_RET_OK) { return ret; // rcl error state should already be set. } @@ -255,7 +256,7 @@ rcl_timer_reset(rcl_timer_t * timer) RCL_CHECK_FOR_NULL_WITH_MSG( timer->impl, "timer is invalid", return RCL_RET_TIMER_INVALID, rcl_get_default_allocator()); rcl_time_point_value_t now; - rcl_ret_t now_ret = rcl_steady_time_now(&now); + rcl_ret_t now_ret = rcutils_steady_time_now(&now); if (now_ret != RCL_RET_OK) { return now_ret; // rcl error state should already be set. } diff --git a/rcl/test/rcl/test_time.cpp b/rcl/test/rcl/test_time.cpp index edf96d4..1615699 100644 --- a/rcl/test/rcl/test_time.cpp +++ b/rcl/test/rcl/test_time.cpp @@ -54,76 +54,6 @@ public: } }; -// Tests the rcl_system_time_now() function. -TEST_F(CLASSNAME(TestTimeFixture, RMW_IMPLEMENTATION), test_rcl_system_time_now) { - assert_no_realloc_begin(); - rcl_ret_t ret; - // Check for invalid argument error condition (allowed to alloc). - ret = rcl_system_time_now(nullptr); - EXPECT_EQ(ret, RCL_RET_INVALID_ARGUMENT) << rcl_get_error_string_safe(); - rcl_reset_error(); - assert_no_malloc_begin(); - assert_no_free_begin(); - // Check for normal operation (not allowed to alloc). - rcl_time_point_value_t now = 0; - ret = rcl_system_time_now(&now); - assert_no_malloc_end(); - assert_no_realloc_end(); - assert_no_free_end(); - stop_memory_checking(); - EXPECT_EQ(ret, RCL_RET_OK) << rcl_get_error_string_safe(); - EXPECT_NE(now, 0u); - // Compare to std::chrono::system_clock time (within a second). - now = 0; - ret = rcl_system_time_now(&now); - { - std::chrono::system_clock::time_point now_sc = std::chrono::system_clock::now(); - auto now_ns = std::chrono::duration_cast(now_sc.time_since_epoch()); - int64_t now_ns_int = now_ns.count(); - int64_t now_diff = now - now_ns_int; - const int k_tolerance_ms = 1000; - EXPECT_LE(llabs(now_diff), RCL_MS_TO_NS(k_tolerance_ms)) << "system_clock differs"; - } -} - -// Tests the rcl_steady_time_now() function. -TEST_F(CLASSNAME(TestTimeFixture, RMW_IMPLEMENTATION), test_rcl_steady_time_now) { - assert_no_realloc_begin(); - rcl_ret_t ret; - // Check for invalid argument error condition (allowed to alloc). - ret = rcl_steady_time_now(nullptr); - EXPECT_EQ(ret, RCL_RET_INVALID_ARGUMENT) << rcl_get_error_string_safe(); - rcl_reset_error(); - assert_no_malloc_begin(); - assert_no_free_begin(); - // Check for normal operation (not allowed to alloc). - rcl_time_point_value_t now = 0; - ret = rcl_steady_time_now(&now); - assert_no_malloc_end(); - assert_no_realloc_end(); - assert_no_free_end(); - stop_memory_checking(); - EXPECT_EQ(ret, RCL_RET_OK) << rcl_get_error_string_safe(); - EXPECT_NE(now, 0u); - // Compare to std::chrono::steady_clock difference of two times (within a second). - now = 0; - ret = rcl_steady_time_now(&now); - std::chrono::steady_clock::time_point now_sc = std::chrono::steady_clock::now(); - EXPECT_EQ(ret, RCL_RET_OK) << rcl_get_error_string_safe(); - // Wait for a little while. - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - // Then take a new timestamp with each and compare. - rcl_time_point_value_t later; - ret = rcl_steady_time_now(&later); - std::chrono::steady_clock::time_point later_sc = std::chrono::steady_clock::now(); - EXPECT_EQ(ret, RCL_RET_OK) << rcl_get_error_string_safe(); - int64_t steady_diff = later - now; - int64_t sc_diff = - std::chrono::duration_cast(later_sc - now_sc).count(); - const int k_tolerance_ms = 1; - EXPECT_LE(llabs(steady_diff - sc_diff), RCL_MS_TO_NS(k_tolerance_ms)) << "steady_clock differs"; -} - // Tests the rcl_set_ros_time_override() function. TEST_F(CLASSNAME(TestTimeFixture, RMW_IMPLEMENTATION), test_rcl_ros_time_set_override) { rcl_time_source_t * ros_time_source = rcl_get_default_ros_time_source(); @@ -256,7 +186,7 @@ TEST_F(CLASSNAME(TestTimeFixture, RMW_IMPLEMENTATION), test_rcl_init_for_time_so // EXPECT_NE(now.nanoseconds, 0u); // // Compare to std::chrono::system_clock time (within a second). // now = {0}; - // ret = rcl_system_time_now(&now); + // ret = rcutils_system_time_now(&now); // { // std::chrono::system_clock::time_point now_sc = std::chrono::system_clock::now(); // auto now_ns = std::chrono::duration_cast(