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). */
|
||||
#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 {
|
||||
uint64_t ns;
|
||||
#ifdef JEMALLOC_DEBUG
|
||||
uint32_t magic; /* Tracks if initialized. */
|
||||
#endif
|
||||
} nstime_t;
|
||||
|
||||
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->npending_purge = 0;
|
||||
nstime_init_zero(&shard->last_purge);
|
||||
|
||||
shard->stats.npurge_passes = 0;
|
||||
shard->stats.npurges = 0;
|
||||
|
60
src/nstime.c
60
src/nstime.c
@ -8,93 +8,153 @@
|
||||
#define BILLION UINT64_C(1000000000)
|
||||
#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
|
||||
nstime_init(nstime_t *time, uint64_t ns) {
|
||||
nstime_set_initialized(time);
|
||||
time->ns = ns;
|
||||
}
|
||||
|
||||
void
|
||||
nstime_init2(nstime_t *time, uint64_t sec, uint64_t nsec) {
|
||||
nstime_set_initialized(time);
|
||||
time->ns = sec * BILLION + nsec;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
nstime_ns(const nstime_t *time) {
|
||||
nstime_assert_initialized(time);
|
||||
return time->ns;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
nstime_msec(const nstime_t *time) {
|
||||
nstime_assert_initialized(time);
|
||||
return time->ns / MILLION;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
nstime_sec(const nstime_t *time) {
|
||||
nstime_assert_initialized(time);
|
||||
return time->ns / BILLION;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
nstime_nsec(const nstime_t *time) {
|
||||
nstime_assert_initialized(time);
|
||||
return time->ns % BILLION;
|
||||
}
|
||||
|
||||
void
|
||||
nstime_copy(nstime_t *time, const nstime_t *source) {
|
||||
/* Source is required to be initialized. */
|
||||
nstime_assert_initialized(source);
|
||||
*time = *source;
|
||||
nstime_assert_initialized(time);
|
||||
}
|
||||
|
||||
int
|
||||
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);
|
||||
}
|
||||
|
||||
void
|
||||
nstime_add(nstime_t *time, const nstime_t *addend) {
|
||||
nstime_pair_assert_initialized(time, addend);
|
||||
assert(UINT64_MAX - time->ns >= addend->ns);
|
||||
|
||||
nstime_initialize_operand(time);
|
||||
time->ns += addend->ns;
|
||||
}
|
||||
|
||||
void
|
||||
nstime_iadd(nstime_t *time, uint64_t addend) {
|
||||
nstime_assert_initialized(time);
|
||||
assert(UINT64_MAX - time->ns >= addend);
|
||||
|
||||
nstime_initialize_operand(time);
|
||||
time->ns += addend;
|
||||
}
|
||||
|
||||
void
|
||||
nstime_subtract(nstime_t *time, const nstime_t *subtrahend) {
|
||||
nstime_pair_assert_initialized(time, subtrahend);
|
||||
assert(nstime_compare(time, subtrahend) >= 0);
|
||||
|
||||
/* No initialize operand -- subtraction must be initialized. */
|
||||
time->ns -= subtrahend->ns;
|
||||
}
|
||||
|
||||
void
|
||||
nstime_isubtract(nstime_t *time, uint64_t subtrahend) {
|
||||
nstime_assert_initialized(time);
|
||||
assert(time->ns >= subtrahend);
|
||||
|
||||
/* No initialize operand -- subtraction must be initialized. */
|
||||
time->ns -= subtrahend;
|
||||
}
|
||||
|
||||
void
|
||||
nstime_imultiply(nstime_t *time, uint64_t multiplier) {
|
||||
nstime_assert_initialized(time);
|
||||
assert((((time->ns | multiplier) & (UINT64_MAX << (sizeof(uint64_t) <<
|
||||
2))) == 0) || ((time->ns * multiplier) / multiplier == time->ns));
|
||||
|
||||
nstime_initialize_operand(time);
|
||||
time->ns *= multiplier;
|
||||
}
|
||||
|
||||
void
|
||||
nstime_idivide(nstime_t *time, uint64_t divisor) {
|
||||
nstime_assert_initialized(time);
|
||||
assert(divisor != 0);
|
||||
|
||||
nstime_initialize_operand(time);
|
||||
time->ns /= divisor;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
nstime_divide(const nstime_t *time, const nstime_t *divisor) {
|
||||
nstime_pair_assert_initialized(time, divisor);
|
||||
assert(divisor->ns != 0);
|
||||
|
||||
/* No initialize operand -- *time itself remains unchanged. */
|
||||
return time->ns / divisor->ns;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user