Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ The API is a combination of parts:
- Portable implementations of "get system time" and "get steady time":
- rcutils_system_time_now()
- rcutils_steady_time_now()
- rcutils_raw_steady_time_now()
- rcutils/time.h
- Some useful data structures:
- A "string array" data structure (analogous to `std::vector<std::string>`):
Expand Down
27 changes: 27 additions & 0 deletions include/rcutils/time.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,33 @@ RCUTILS_WARN_UNUSED
rcutils_ret_t
rcutils_steady_time_now(rcutils_time_point_value_t * now);

/// Retrieve the current time as a rcutils_time_point_value_t object.
/**
* This function returns the time from a monotonically increasing slew-free clock.
*
* The resolution (e.g. nanoseconds vs microseconds) is not guaranteed.
*
* The now argument must point to an allocated rcutils_time_point_value_t object,
* as the result is copied into this variable.
*
* <hr>
* Attribute | Adherence
* ------------------ | -------------
* Allocates Memory | No
* Thread-Safe | Yes
* Uses Atomics | No
* Lock-Free | Yes
*
* \param[out] now a struct in which the current time is stored
* \return #RCUTILS_RET_OK if the current time was successfully obtained, or
* \return #RCUTILS_RET_INVALID_ARGUMENT if any arguments are invalid, or
* \return #RCUTILS_RET_ERROR if an unspecified error occur.
*/
RCUTILS_PUBLIC
RCUTILS_WARN_UNUSED
rcutils_ret_t
rcutils_raw_steady_time_now(rcutils_time_point_value_t * now);

/// Return a time point as nanoseconds in a string.
/**
* The number is always fixed width, with left padding zeros up to the maximum
Expand Down
24 changes: 24 additions & 0 deletions src/time_unix.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,27 @@ rcutils_steady_time_now(rcutils_time_point_value_t * now)
*now = RCUTILS_S_TO_NS((int64_t)timespec_now.tv_sec) + timespec_now.tv_nsec;
return RCUTILS_RET_OK;
}

rcutils_ret_t
rcutils_raw_steady_time_now(rcutils_time_point_value_t * now)
{
RCUTILS_CHECK_ARGUMENT_FOR_NULL(now, RCUTILS_RET_INVALID_ARGUMENT);
struct timespec timespec_now;

#if defined(CLOCK_MONOTONIC_RAW)
clockid_t monotonic_raw_clock = CLOCK_MONOTONIC_RAW;
#else
clockid_t monotonic_raw_clock = CLOCK_MONOTONIC;
#endif

if (clock_gettime(monotonic_raw_clock, &timespec_now) < 0) {
RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING("Failed to get raw steady time: %d", errno);
return RCUTILS_RET_ERROR;
}
if (would_be_negative(&timespec_now)) {
RCUTILS_SET_ERROR_MSG("unexpected negative time");
return RCUTILS_RET_ERROR;
}
*now = RCUTILS_S_TO_NS((int64_t)timespec_now.tv_sec) + timespec_now.tv_nsec;
return RCUTILS_RET_OK;
}
9 changes: 9 additions & 0 deletions src/time_win32.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ rcutils_steady_time_now(rcutils_time_point_value_t * now)
return RCUTILS_RET_OK;
}

rcutils_ret_t
rcutils_raw_steady_time_now(rcutils_time_point_value_t * now)
{
// On Windows, there is no difference between steady and raw steady time.
// The QueryPerformanceCounter function provides a high-resolution timer
// that is not affected by system clock changes.
return rcutils_steady_time_now(now);
}

#ifdef __cplusplus
}
#endif
49 changes: 49 additions & 0 deletions test/test_time.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,47 @@ TEST_F(TestTimeFixture, test_rcutils_steady_time_now) {
llabs(steady_diff - sc_diff), RCUTILS_MS_TO_NS(k_tolerance_ms)) << "steady_clock differs";
}

// Tests the rcutils_raw_steady_time_now() function.
TEST_F(TestTimeFixture, test_rcutils_raw_steady_time_now) {
rcutils_ret_t ret;
// Check for invalid argument error condition (allowed to alloc).
ret = rcutils_raw_steady_time_now(nullptr);
EXPECT_EQ(ret, RCUTILS_RET_INVALID_ARGUMENT) << rcutils_get_error_string().str;
rcutils_reset_error();
// Check for normal operation (not allowed to alloc).
rcutils_time_point_value_t now = 0;
EXPECT_NO_MEMORY_OPERATIONS(
{
ret = rcutils_raw_steady_time_now(&now);
});
EXPECT_EQ(ret, RCUTILS_RET_OK) << rcutils_get_error_string().str;
EXPECT_NE(0u, now);
// Compare to std::chrono::steady_clock difference of two times (within a second).
now = 0;
EXPECT_NO_MEMORY_OPERATIONS(
{
ret = rcutils_raw_steady_time_now(&now);
});
std::chrono::steady_clock::time_point now_sc = std::chrono::steady_clock::now();
EXPECT_EQ(ret, RCUTILS_RET_OK) << rcutils_get_error_string().str;
// Wait for a little while.
std::this_thread::sleep_for(std::chrono::milliseconds(100));
// Then take a new timestamp with each and compare.
rcutils_time_point_value_t later;
EXPECT_NO_MEMORY_OPERATIONS(
{
ret = rcutils_raw_steady_time_now(&later);
});
std::chrono::steady_clock::time_point later_sc = std::chrono::steady_clock::now();
EXPECT_EQ(ret, RCUTILS_RET_OK) << rcutils_get_error_string().str;
int64_t steady_diff = later - now;
int64_t sc_diff =
std::chrono::duration_cast<std::chrono::nanoseconds>(later_sc - now_sc).count();
const int k_tolerance_ms = 1;
EXPECT_LE(
llabs(steady_diff - sc_diff), RCUTILS_MS_TO_NS(k_tolerance_ms)) << "steady_clock differs";
}

#if !defined(_WIN32)

TEST_F(TestTimeFixture, test_rcutils_with_bad_system_clocks) {
Expand All @@ -211,6 +252,10 @@ TEST_F(TestTimeFixture, test_rcutils_with_bad_system_clocks) {
ret = rcutils_steady_time_now(&now);
EXPECT_EQ(RCUTILS_RET_ERROR, ret);
rcutils_reset_error();

ret = rcutils_raw_steady_time_now(&now);
EXPECT_EQ(RCUTILS_RET_ERROR, ret);
rcutils_reset_error();
}
{
auto mock = mocking_utils::patch(
Expand All @@ -229,6 +274,10 @@ TEST_F(TestTimeFixture, test_rcutils_with_bad_system_clocks) {
ret = rcutils_steady_time_now(&now);
EXPECT_EQ(RCUTILS_RET_ERROR, ret);
rcutils_reset_error();

ret = rcutils_raw_steady_time_now(&now);
EXPECT_EQ(RCUTILS_RET_ERROR, ret);
rcutils_reset_error();
}
}

Expand Down