mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2025-12-24 13:10:10 +01:00
util/timespec: Return overflow from timespec_add_[mn]sec()
To avoid altering any currently existing callers, we continue on with the calculation regardless of overflow. This also matches the behavior of GCC's __builtin_add_overflow(). Reviewed-By: Mike Blumenkrantz <michael.blumenkrantz@gmail.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/15651>
This commit is contained in:
parent
591899eedd
commit
0ba22b203d
2 changed files with 58 additions and 13 deletions
|
|
@ -27,6 +27,8 @@
|
|||
|
||||
#include "util/timespec.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
TEST(timespec_test, timespec_add)
|
||||
{
|
||||
struct timespec a, b, r;
|
||||
|
|
@ -114,41 +116,66 @@ TEST(timespec_test, millihz_to_nsec)
|
|||
EXPECT_EQ(millihz_to_nsec(60000), 16666666);
|
||||
}
|
||||
|
||||
TEST(timespec_test, time_t_max)
|
||||
{
|
||||
/* The TIME_T_MAX macro assumes it's no more than 64 bits */
|
||||
EXPECT_LE(sizeof(time_t), sizeof(uint64_t));
|
||||
|
||||
time_t t = TIME_T_MAX;
|
||||
EXPECT_EQ((uint64_t)t, (uint64_t)TIME_T_MAX);
|
||||
|
||||
/* Since the tests are C++ code, we have std::numeric_limits */
|
||||
EXPECT_EQ(std::numeric_limits<time_t>::max(), TIME_T_MAX);
|
||||
}
|
||||
|
||||
TEST(timespec_test, timespec_add_nsec)
|
||||
{
|
||||
struct timespec a, r;
|
||||
|
||||
a.tv_sec = 0;
|
||||
a.tv_nsec = NSEC_PER_SEC - 1;
|
||||
timespec_add_nsec(&r, &a, 1);
|
||||
EXPECT_FALSE(timespec_add_nsec(&r, &a, 1));
|
||||
EXPECT_EQ(1, r.tv_sec);
|
||||
EXPECT_EQ(0, r.tv_nsec);
|
||||
|
||||
timespec_add_nsec(&r, &a, 2);
|
||||
EXPECT_FALSE(timespec_add_nsec(&r, &a, 2));
|
||||
EXPECT_EQ(1, r.tv_sec);
|
||||
EXPECT_EQ(1, r.tv_nsec);
|
||||
|
||||
timespec_add_nsec(&r, &a, (NSEC_PER_SEC * 2ULL));
|
||||
EXPECT_FALSE(timespec_add_nsec(&r, &a, (NSEC_PER_SEC * 2ULL)));
|
||||
EXPECT_EQ(2, r.tv_sec);
|
||||
EXPECT_EQ(NSEC_PER_SEC - 1, r.tv_nsec);
|
||||
|
||||
timespec_add_nsec(&r, &a, (NSEC_PER_SEC * 2ULL) + 2);
|
||||
EXPECT_FALSE(timespec_add_nsec(&r, &a, (NSEC_PER_SEC * 2ULL) + 2));
|
||||
EXPECT_EQ(r.tv_sec, 3);
|
||||
EXPECT_EQ(r.tv_nsec, 1);
|
||||
|
||||
r.tv_sec = 4;
|
||||
r.tv_nsec = 0;
|
||||
timespec_add_nsec(&r, &r, NSEC_PER_SEC + 10ULL);
|
||||
EXPECT_FALSE(timespec_add_nsec(&r, &r, NSEC_PER_SEC + 10ULL));
|
||||
EXPECT_EQ(5, r.tv_sec);
|
||||
EXPECT_EQ(10, r.tv_nsec);
|
||||
|
||||
timespec_add_nsec(&r, &r, (NSEC_PER_SEC * 3ULL) - 9ULL);
|
||||
EXPECT_FALSE(timespec_add_nsec(&r, &r, (NSEC_PER_SEC * 3ULL) - 9ULL));
|
||||
EXPECT_EQ(8, r.tv_sec);
|
||||
EXPECT_EQ(1, r.tv_nsec);
|
||||
|
||||
timespec_add_nsec(&r, &r, (NSEC_PER_SEC * 7ULL) + (NSEC_PER_SEC - 1ULL));
|
||||
EXPECT_FALSE(timespec_add_nsec(&r, &r, (NSEC_PER_SEC * 7ULL) +
|
||||
(NSEC_PER_SEC - 1ULL)));
|
||||
EXPECT_EQ(16, r.tv_sec);
|
||||
EXPECT_EQ(0, r.tv_nsec);
|
||||
|
||||
a.tv_sec = TIME_T_MAX;
|
||||
a.tv_nsec = 0;
|
||||
EXPECT_TRUE(timespec_add_nsec(&r, &a, UINT64_MAX));
|
||||
|
||||
a.tv_sec = TIME_T_MAX;
|
||||
a.tv_nsec = 0;
|
||||
EXPECT_TRUE(timespec_add_nsec(&r, &a, NSEC_PER_SEC));
|
||||
|
||||
a.tv_sec = TIME_T_MAX;
|
||||
a.tv_nsec = NSEC_PER_SEC / 2;
|
||||
EXPECT_TRUE(timespec_add_nsec(&r, &a, NSEC_PER_SEC / 2));
|
||||
}
|
||||
|
||||
TEST(timespec_test, timespec_add_msec)
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@
|
|||
#include <time.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "macros.h"
|
||||
|
||||
#define NSEC_PER_SEC 1000000000
|
||||
|
||||
/**
|
||||
|
|
@ -77,26 +79,41 @@ timespec_sub(struct timespec *r,
|
|||
}
|
||||
}
|
||||
|
||||
#define TIME_T_MAX \
|
||||
((time_t)(((time_t)-1) > 0 ? u_uintN_max(sizeof(time_t) * 8) : \
|
||||
u_intN_max(sizeof(time_t) * 8)))
|
||||
|
||||
/**
|
||||
* Add a nanosecond value to a timespec
|
||||
*
|
||||
* \param r[out] result: a + b
|
||||
* \param a[in] base operand as timespec
|
||||
* \param b[in] operand in nanoseconds
|
||||
* \return true if the calculation overflowed
|
||||
*/
|
||||
static inline void
|
||||
static inline bool
|
||||
timespec_add_nsec(struct timespec *r, const struct timespec *a, uint64_t b)
|
||||
{
|
||||
r->tv_sec = a->tv_sec + (b / NSEC_PER_SEC);
|
||||
r->tv_nsec = a->tv_nsec + (b % NSEC_PER_SEC);
|
||||
uint64_t b_sec = b / NSEC_PER_SEC;
|
||||
long b_nsec = b % NSEC_PER_SEC;
|
||||
bool overflow = (b_sec > (uint64_t)TIME_T_MAX) ||
|
||||
((uint64_t)a->tv_sec > (uint64_t)TIME_T_MAX - b_sec);
|
||||
|
||||
r->tv_sec = (uint64_t)a->tv_sec + b_sec;
|
||||
r->tv_nsec = (uint64_t)a->tv_nsec + b_nsec;
|
||||
|
||||
if (r->tv_nsec >= NSEC_PER_SEC) {
|
||||
r->tv_sec++;
|
||||
if (r->tv_sec >= TIME_T_MAX)
|
||||
overflow = true;
|
||||
r->tv_sec = (uint64_t)r->tv_sec + 1ull;
|
||||
r->tv_nsec -= NSEC_PER_SEC;
|
||||
} else if (r->tv_nsec < 0) {
|
||||
assert(overflow);
|
||||
r->tv_sec--;
|
||||
r->tv_nsec += NSEC_PER_SEC;
|
||||
}
|
||||
|
||||
return overflow;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -105,11 +122,12 @@ timespec_add_nsec(struct timespec *r, const struct timespec *a, uint64_t b)
|
|||
* \param r[out] result: a + b
|
||||
* \param a[in] base operand as timespec
|
||||
* \param b[in] operand in milliseconds
|
||||
* \return true if the calculation overflowed
|
||||
*/
|
||||
static inline void
|
||||
static inline bool
|
||||
timespec_add_msec(struct timespec *r, const struct timespec *a, uint64_t b)
|
||||
{
|
||||
timespec_add_nsec(r, a, b * 1000000);
|
||||
return timespec_add_nsec(r, a, b * 1000000) || b > (UINT64_MAX / 1000000);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue