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:
Qi Wang 2021-11-16 14:51:07 -08:00 committed by Qi Wang
parent 400c59895a
commit cdabe908d0
3 changed files with 72 additions and 2 deletions

View File

@ -3,10 +3,19 @@
/* Maximum supported number of seconds (~584 years). */
#define NSTIME_SEC_MAX KQU(18446744072)
#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;

View File

@ -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;

View File

@ -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;
}