Track the initialized state of nstime_t on debug build.
Some nstime_t operations require and assume the input nstime is initialized (e.g. nstime_update) -- uninitialized input may cause silent failures which is difficult to reproduce / debug. Add an explicit flag to track the state (limited to debug build only). Also fixed an use case in hpa (time of last_purge).
This commit is contained in:
parent
400c59895a
commit
cdabe908d0
@ -3,10 +3,19 @@
|
|||||||
|
|
||||||
/* Maximum supported number of seconds (~584 years). */
|
/* Maximum supported number of seconds (~584 years). */
|
||||||
#define NSTIME_SEC_MAX KQU(18446744072)
|
#define NSTIME_SEC_MAX KQU(18446744072)
|
||||||
#define NSTIME_ZERO_INITIALIZER {0}
|
|
||||||
|
#define NSTIME_MAGIC ((uint32_t)0xb8a9ce37)
|
||||||
|
#ifdef JEMALLOC_DEBUG
|
||||||
|
# define NSTIME_ZERO_INITIALIZER {0, NSTIME_MAGIC}
|
||||||
|
#else
|
||||||
|
# define NSTIME_ZERO_INITIALIZER {0}
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint64_t ns;
|
uint64_t ns;
|
||||||
|
#ifdef JEMALLOC_DEBUG
|
||||||
|
uint32_t magic; /* Tracks if initialized. */
|
||||||
|
#endif
|
||||||
} nstime_t;
|
} nstime_t;
|
||||||
|
|
||||||
static const nstime_t zero = NSTIME_ZERO_INITIALIZER;
|
static const nstime_t zero = NSTIME_ZERO_INITIALIZER;
|
||||||
|
@ -203,6 +203,7 @@ hpa_shard_init(hpa_shard_t *shard, hpa_central_t *central, emap_t *emap,
|
|||||||
shard->opts = *opts;
|
shard->opts = *opts;
|
||||||
|
|
||||||
shard->npending_purge = 0;
|
shard->npending_purge = 0;
|
||||||
|
nstime_init_zero(&shard->last_purge);
|
||||||
|
|
||||||
shard->stats.npurge_passes = 0;
|
shard->stats.npurge_passes = 0;
|
||||||
shard->stats.npurges = 0;
|
shard->stats.npurges = 0;
|
||||||
|
62
src/nstime.c
62
src/nstime.c
@ -8,93 +8,153 @@
|
|||||||
#define BILLION UINT64_C(1000000000)
|
#define BILLION UINT64_C(1000000000)
|
||||||
#define MILLION UINT64_C(1000000)
|
#define MILLION UINT64_C(1000000)
|
||||||
|
|
||||||
|
static void
|
||||||
|
nstime_set_initialized(nstime_t *time) {
|
||||||
|
#ifdef JEMALLOC_DEBUG
|
||||||
|
time->magic = NSTIME_MAGIC;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nstime_assert_initialized(const nstime_t *time) {
|
||||||
|
#ifdef JEMALLOC_DEBUG
|
||||||
|
/*
|
||||||
|
* Some parts (e.g. stats) rely on memset to zero initialize. Treat
|
||||||
|
* these as valid initialization.
|
||||||
|
*/
|
||||||
|
assert(time->magic == NSTIME_MAGIC ||
|
||||||
|
(time->magic == 0 && time->ns == 0));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nstime_pair_assert_initialized(const nstime_t *t1, const nstime_t *t2) {
|
||||||
|
nstime_assert_initialized(t1);
|
||||||
|
nstime_assert_initialized(t2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nstime_initialize_operand(nstime_t *time) {
|
||||||
|
/*
|
||||||
|
* Operations like nstime_add may have the initial operand being zero
|
||||||
|
* initialized (covered by the assert below). Full-initialize needed
|
||||||
|
* before changing it to non-zero.
|
||||||
|
*/
|
||||||
|
nstime_assert_initialized(time);
|
||||||
|
nstime_set_initialized(time);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nstime_init(nstime_t *time, uint64_t ns) {
|
nstime_init(nstime_t *time, uint64_t ns) {
|
||||||
|
nstime_set_initialized(time);
|
||||||
time->ns = ns;
|
time->ns = ns;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nstime_init2(nstime_t *time, uint64_t sec, uint64_t nsec) {
|
nstime_init2(nstime_t *time, uint64_t sec, uint64_t nsec) {
|
||||||
|
nstime_set_initialized(time);
|
||||||
time->ns = sec * BILLION + nsec;
|
time->ns = sec * BILLION + nsec;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t
|
uint64_t
|
||||||
nstime_ns(const nstime_t *time) {
|
nstime_ns(const nstime_t *time) {
|
||||||
|
nstime_assert_initialized(time);
|
||||||
return time->ns;
|
return time->ns;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t
|
uint64_t
|
||||||
nstime_msec(const nstime_t *time) {
|
nstime_msec(const nstime_t *time) {
|
||||||
|
nstime_assert_initialized(time);
|
||||||
return time->ns / MILLION;
|
return time->ns / MILLION;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t
|
uint64_t
|
||||||
nstime_sec(const nstime_t *time) {
|
nstime_sec(const nstime_t *time) {
|
||||||
|
nstime_assert_initialized(time);
|
||||||
return time->ns / BILLION;
|
return time->ns / BILLION;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t
|
uint64_t
|
||||||
nstime_nsec(const nstime_t *time) {
|
nstime_nsec(const nstime_t *time) {
|
||||||
|
nstime_assert_initialized(time);
|
||||||
return time->ns % BILLION;
|
return time->ns % BILLION;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nstime_copy(nstime_t *time, const nstime_t *source) {
|
nstime_copy(nstime_t *time, const nstime_t *source) {
|
||||||
|
/* Source is required to be initialized. */
|
||||||
|
nstime_assert_initialized(source);
|
||||||
*time = *source;
|
*time = *source;
|
||||||
|
nstime_assert_initialized(time);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
nstime_compare(const nstime_t *a, const nstime_t *b) {
|
nstime_compare(const nstime_t *a, const nstime_t *b) {
|
||||||
|
nstime_pair_assert_initialized(a, b);
|
||||||
return (a->ns > b->ns) - (a->ns < b->ns);
|
return (a->ns > b->ns) - (a->ns < b->ns);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nstime_add(nstime_t *time, const nstime_t *addend) {
|
nstime_add(nstime_t *time, const nstime_t *addend) {
|
||||||
|
nstime_pair_assert_initialized(time, addend);
|
||||||
assert(UINT64_MAX - time->ns >= addend->ns);
|
assert(UINT64_MAX - time->ns >= addend->ns);
|
||||||
|
|
||||||
|
nstime_initialize_operand(time);
|
||||||
time->ns += addend->ns;
|
time->ns += addend->ns;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nstime_iadd(nstime_t *time, uint64_t addend) {
|
nstime_iadd(nstime_t *time, uint64_t addend) {
|
||||||
|
nstime_assert_initialized(time);
|
||||||
assert(UINT64_MAX - time->ns >= addend);
|
assert(UINT64_MAX - time->ns >= addend);
|
||||||
|
|
||||||
|
nstime_initialize_operand(time);
|
||||||
time->ns += addend;
|
time->ns += addend;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nstime_subtract(nstime_t *time, const nstime_t *subtrahend) {
|
nstime_subtract(nstime_t *time, const nstime_t *subtrahend) {
|
||||||
|
nstime_pair_assert_initialized(time, subtrahend);
|
||||||
assert(nstime_compare(time, subtrahend) >= 0);
|
assert(nstime_compare(time, subtrahend) >= 0);
|
||||||
|
|
||||||
|
/* No initialize operand -- subtraction must be initialized. */
|
||||||
time->ns -= subtrahend->ns;
|
time->ns -= subtrahend->ns;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nstime_isubtract(nstime_t *time, uint64_t subtrahend) {
|
nstime_isubtract(nstime_t *time, uint64_t subtrahend) {
|
||||||
|
nstime_assert_initialized(time);
|
||||||
assert(time->ns >= subtrahend);
|
assert(time->ns >= subtrahend);
|
||||||
|
|
||||||
|
/* No initialize operand -- subtraction must be initialized. */
|
||||||
time->ns -= subtrahend;
|
time->ns -= subtrahend;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nstime_imultiply(nstime_t *time, uint64_t multiplier) {
|
nstime_imultiply(nstime_t *time, uint64_t multiplier) {
|
||||||
|
nstime_assert_initialized(time);
|
||||||
assert((((time->ns | multiplier) & (UINT64_MAX << (sizeof(uint64_t) <<
|
assert((((time->ns | multiplier) & (UINT64_MAX << (sizeof(uint64_t) <<
|
||||||
2))) == 0) || ((time->ns * multiplier) / multiplier == time->ns));
|
2))) == 0) || ((time->ns * multiplier) / multiplier == time->ns));
|
||||||
|
|
||||||
|
nstime_initialize_operand(time);
|
||||||
time->ns *= multiplier;
|
time->ns *= multiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nstime_idivide(nstime_t *time, uint64_t divisor) {
|
nstime_idivide(nstime_t *time, uint64_t divisor) {
|
||||||
|
nstime_assert_initialized(time);
|
||||||
assert(divisor != 0);
|
assert(divisor != 0);
|
||||||
|
|
||||||
|
nstime_initialize_operand(time);
|
||||||
time->ns /= divisor;
|
time->ns /= divisor;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t
|
uint64_t
|
||||||
nstime_divide(const nstime_t *time, const nstime_t *divisor) {
|
nstime_divide(const nstime_t *time, const nstime_t *divisor) {
|
||||||
|
nstime_pair_assert_initialized(time, divisor);
|
||||||
assert(divisor->ns != 0);
|
assert(divisor->ns != 0);
|
||||||
|
|
||||||
|
/* No initialize operand -- *time itself remains unchanged. */
|
||||||
return time->ns / divisor->ns;
|
return time->ns / divisor->ns;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,7 +252,7 @@ nstime_update_impl(nstime_t *time) {
|
|||||||
nstime_t old_time;
|
nstime_t old_time;
|
||||||
|
|
||||||
nstime_copy(&old_time, time);
|
nstime_copy(&old_time, time);
|
||||||
nstime_get(time);
|
nstime_get(time);
|
||||||
|
|
||||||
/* Handle non-monotonic clocks. */
|
/* Handle non-monotonic clocks. */
|
||||||
if (unlikely(nstime_compare(&old_time, time) > 0)) {
|
if (unlikely(nstime_compare(&old_time, time) > 0)) {
|
||||||
|
Loading…
Reference in New Issue
Block a user