Skip to content

Commit 619666f

Browse files
mergify[bot]saikishorfujitatomoyaahcorde
authored
Add rcutils_raw_steady_time_now method for slew-free clock (backport ros2#507) (ros2#515)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com> Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com> Co-authored-by: Sai Kishor Kothakota <saisastra3@gmail.com> Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com> Co-authored-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
1 parent 1f961e2 commit 619666f

5 files changed

Lines changed: 115 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ The API is a combination of parts:
5555
- Portable implementations of "get system time" and "get steady time":
5656
- rcutils_system_time_now()
5757
- rcutils_steady_time_now()
58+
- rcutils_raw_steady_time_now()
5859
- rcutils/time.h
5960
- Some useful data structures:
6061
- A "string array" data structure (analogous to `std::vector<std::string>`):

include/rcutils/time.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,33 @@ RCUTILS_WARN_UNUSED
104104
rcutils_ret_t
105105
rcutils_steady_time_now(rcutils_time_point_value_t * now);
106106

107+
/// Retrieve the current time as a rcutils_time_point_value_t object.
108+
/**
109+
* This function returns the time from a monotonically increasing slew-free clock.
110+
*
111+
* The resolution (e.g. nanoseconds vs microseconds) is not guaranteed.
112+
*
113+
* The now argument must point to an allocated rcutils_time_point_value_t object,
114+
* as the result is copied into this variable.
115+
*
116+
* <hr>
117+
* Attribute | Adherence
118+
* ------------------ | -------------
119+
* Allocates Memory | No
120+
* Thread-Safe | Yes
121+
* Uses Atomics | No
122+
* Lock-Free | Yes
123+
*
124+
* \param[out] now a struct in which the current time is stored
125+
* \return #RCUTILS_RET_OK if the current time was successfully obtained, or
126+
* \return #RCUTILS_RET_INVALID_ARGUMENT if any arguments are invalid, or
127+
* \return #RCUTILS_RET_ERROR if an unspecified error occur.
128+
*/
129+
RCUTILS_PUBLIC
130+
RCUTILS_WARN_UNUSED
131+
rcutils_ret_t
132+
rcutils_raw_steady_time_now(rcutils_time_point_value_t * now);
133+
107134
/// Return a time point as nanoseconds in a string.
108135
/**
109136
* The number is always fixed width, with left padding zeros up to the maximum

src/time_unix.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@ extern "C"
2121
{
2222
#endif
2323

24+
#include "rcutils/logging_macros.h"
2425
#include "rcutils/time.h"
2526

2627
#if defined(__MACH__)
2728
#include <mach/clock.h>
2829
#include <mach/mach.h>
2930
#endif // defined(__MACH__)
31+
#include <errno.h>
3032
#include <math.h>
3133
#include <time.h>
3234
#include <unistd.h>
@@ -102,6 +104,33 @@ rcutils_steady_time_now(rcutils_time_point_value_t * now)
102104
return RCUTILS_RET_OK;
103105
}
104106

107+
rcutils_ret_t
108+
rcutils_raw_steady_time_now(rcutils_time_point_value_t * now)
109+
{
110+
RCUTILS_CHECK_ARGUMENT_FOR_NULL(now, RCUTILS_RET_INVALID_ARGUMENT);
111+
struct timespec timespec_now;
112+
113+
#if defined(CLOCK_MONOTONIC_RAW)
114+
clockid_t monotonic_raw_clock = CLOCK_MONOTONIC_RAW;
115+
#else
116+
clockid_t monotonic_raw_clock = CLOCK_MONOTONIC;
117+
RCUTILS_LOG_WARN_ONCE(
118+
"CLOCK_MONOTONIC_RAW is not supported by the platform, using CLOCK_MONOTONIC "
119+
"instead. This may not provide the desired raw steady time behavior.");
120+
#endif
121+
122+
if (clock_gettime(monotonic_raw_clock, &timespec_now) < 0) {
123+
RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING("Failed to get raw steady time: %d", errno);
124+
return RCUTILS_RET_ERROR;
125+
}
126+
if (__WOULD_BE_NEGATIVE(timespec_now.tv_sec, timespec_now.tv_nsec)) {
127+
RCUTILS_SET_ERROR_MSG("unexpected negative time");
128+
return RCUTILS_RET_ERROR;
129+
}
130+
*now = RCUTILS_S_TO_NS((int64_t)timespec_now.tv_sec) + timespec_now.tv_nsec;
131+
return RCUTILS_RET_OK;
132+
}
133+
105134
#ifdef __cplusplus
106135
}
107136
#endif

src/time_win32.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,15 @@ rcutils_steady_time_now(rcutils_time_point_value_t * now)
8484
return RCUTILS_RET_OK;
8585
}
8686

87+
rcutils_ret_t
88+
rcutils_raw_steady_time_now(rcutils_time_point_value_t * now)
89+
{
90+
// On Windows, there is no difference between steady and raw steady time.
91+
// The QueryPerformanceCounter function provides a high-resolution timer
92+
// that is not affected by system clock changes.
93+
return rcutils_steady_time_now(now);
94+
}
95+
8796
#ifdef __cplusplus
8897
}
8998
#endif

test/test_time.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,47 @@ TEST_F(TestTimeFixture, test_rcutils_steady_time_now) {
191191
llabs(steady_diff - sc_diff), RCUTILS_MS_TO_NS(k_tolerance_ms)) << "steady_clock differs";
192192
}
193193

194+
// Tests the rcutils_raw_steady_time_now() function.
195+
TEST_F(TestTimeFixture, test_rcutils_raw_steady_time_now) {
196+
rcutils_ret_t ret;
197+
// Check for invalid argument error condition (allowed to alloc).
198+
ret = rcutils_raw_steady_time_now(nullptr);
199+
EXPECT_EQ(ret, RCUTILS_RET_INVALID_ARGUMENT) << rcutils_get_error_string().str;
200+
rcutils_reset_error();
201+
// Check for normal operation (not allowed to alloc).
202+
rcutils_time_point_value_t now = 0;
203+
EXPECT_NO_MEMORY_OPERATIONS(
204+
{
205+
ret = rcutils_raw_steady_time_now(&now);
206+
});
207+
EXPECT_EQ(ret, RCUTILS_RET_OK) << rcutils_get_error_string().str;
208+
EXPECT_NE(0u, now);
209+
// Compare to std::chrono::steady_clock difference of two times (within a second).
210+
now = 0;
211+
EXPECT_NO_MEMORY_OPERATIONS(
212+
{
213+
ret = rcutils_raw_steady_time_now(&now);
214+
});
215+
std::chrono::steady_clock::time_point now_sc = std::chrono::steady_clock::now();
216+
EXPECT_EQ(ret, RCUTILS_RET_OK) << rcutils_get_error_string().str;
217+
// Wait for a little while.
218+
std::this_thread::sleep_for(std::chrono::milliseconds(100));
219+
// Then take a new timestamp with each and compare.
220+
rcutils_time_point_value_t later;
221+
EXPECT_NO_MEMORY_OPERATIONS(
222+
{
223+
ret = rcutils_raw_steady_time_now(&later);
224+
});
225+
std::chrono::steady_clock::time_point later_sc = std::chrono::steady_clock::now();
226+
EXPECT_EQ(ret, RCUTILS_RET_OK) << rcutils_get_error_string().str;
227+
int64_t steady_diff = later - now;
228+
int64_t sc_diff =
229+
std::chrono::duration_cast<std::chrono::nanoseconds>(later_sc - now_sc).count();
230+
const int k_tolerance_ms = 1;
231+
EXPECT_LE(
232+
llabs(steady_diff - sc_diff), RCUTILS_MS_TO_NS(k_tolerance_ms)) << "steady_clock differs";
233+
}
234+
194235
#if !defined(_WIN32)
195236

196237
// For mocking purposes
@@ -221,6 +262,10 @@ TEST_F(TestTimeFixture, test_rcutils_with_bad_system_clocks) {
221262
ret = rcutils_steady_time_now(&now);
222263
EXPECT_EQ(RCUTILS_RET_ERROR, ret);
223264
rcutils_reset_error();
265+
266+
ret = rcutils_raw_steady_time_now(&now);
267+
EXPECT_EQ(RCUTILS_RET_ERROR, ret);
268+
rcutils_reset_error();
224269
}
225270
#endif
226271
{
@@ -240,6 +285,10 @@ TEST_F(TestTimeFixture, test_rcutils_with_bad_system_clocks) {
240285
ret = rcutils_steady_time_now(&now);
241286
EXPECT_EQ(RCUTILS_RET_ERROR, ret);
242287
rcutils_reset_error();
288+
289+
ret = rcutils_raw_steady_time_now(&now);
290+
EXPECT_EQ(RCUTILS_RET_ERROR, ret);
291+
rcutils_reset_error();
243292
}
244293
}
245294

0 commit comments

Comments
 (0)