typedef void (*isc_statsmulti_dumper_t)(isc_statscounter_t, uint64_t, void *);
void
-isc_statsmulti_create(isc_mem_t *mctx, isc_statsmulti_t **statsp, int ncounters);
+isc_statsmulti_create(isc_mem_t *mctx, isc_statsmulti_t **statsp, int n_additive, int n_max);
/*%<
- * Create a statistics counter structure of general type. It counts a general
- * set of counters indexed by an ID between 0 and ncounters -1.
+ * Create a statistics counter structure with both additive and highwater counters.
+ * Counters [0, n_additive) are additive (sum across threads).
+ * Counters [n_additive, n_additive + n_max) are highwater (maximum across threads).
*
* Requires:
*\li 'mctx' must be a valid memory context.
*
* Requires:
*\li 'stats' is a valid isc_statsmulti_t.
+ */
+
+void
+isc_statsmulti_update_if_greater(isc_statsmulti_t *stats, isc_statscounter_t counter, isc_statscounter_t value);
+/*%<
+ * Update a highwater counter if the provided value is greater than the current per-thread value.
+ * Only works on counters in the range [n_additive, n_additive + n_max).
+ *
+ * Requires:
+ *\li 'stats' is a valid isc_statsmulti_t.
+ *
+ *\li counter is in the highwater range for the stats specified on creation.
+ */
+
+isc_statscounter_t
+isc_statsmulti_get_highwater(isc_statsmulti_t *stats, isc_statscounter_t counter);
+/*%<
+ * Returns the maximum value across all threads for a highwater counter.
+ * Only works on counters in the range [n_additive, n_additive + n_max).
+ *
+ * Requires:
+ *\li 'stats' is a valid isc_statsmulti_t.
+ *
+ *\li counter is in the highwater range for the stats specified on creation.
*/
\ No newline at end of file
isc_mem_t *mctx;
isc_refcount_t references;
int ncounters;
+ int n_additive;
+ int n_max;
int per_thread_capacity;
int num_threads;
isc_atomic_statscounter_t *counters;
};
void
-isc_statsmulti_create(isc_mem_t *mctx, isc_statsmulti_t **statsp, int ncounters) {
+isc_statsmulti_create(isc_mem_t *mctx, isc_statsmulti_t **statsp, int n_additive, int n_max) {
REQUIRE(statsp != NULL && *statsp == NULL);
+ int ncounters = n_additive + n_max;
isc_statsmulti_t *stats = isc_mem_get(mctx, sizeof(*stats));
size_t counters_size = sizeof(isc_atomic_statscounter_t) * ncounters;
stats->mctx = NULL;
isc_mem_attach(mctx, &stats->mctx);
stats->ncounters = ncounters;
+ stats->n_additive = n_additive;
+ stats->n_max = n_max;
stats->per_thread_capacity = per_thread_capacity;
stats->num_threads = num_threads;
stats->magic = ISC_STATSMULTI_MAGIC;
isc_statscounter_t
isc_statsmulti_increment(isc_statsmulti_t *stats, isc_statscounter_t counter) {
REQUIRE(ISC_STATSMULTI_VALID(stats));
- REQUIRE(counter < stats->ncounters);
+ REQUIRE(counter < stats->n_additive);
isc_tid_t tid = isc_tid();
int thread_id = ISC_MAX(tid, 0);
void
isc_statsmulti_decrement(isc_statsmulti_t *stats, isc_statscounter_t counter) {
REQUIRE(ISC_STATSMULTI_VALID(stats));
- REQUIRE(counter < stats->ncounters);
+ REQUIRE(counter < stats->n_additive);
isc_tid_t tid = isc_tid();
int thread_id = ISC_MAX(tid, 0);
isc_statscounter_t
isc_statsmulti_get_counter(isc_statsmulti_t *stats, isc_statscounter_t counter) {
REQUIRE(ISC_STATSMULTI_VALID(stats));
- REQUIRE(counter < stats->ncounters);
+ REQUIRE(counter < stats->n_additive);
isc_statscounter_t total = 0;
/* Accumulate across all threads */
atomic_store_relaxed(&stats->counters[i], 0);
}
}
+
+void
+isc_statsmulti_update_if_greater(isc_statsmulti_t *stats, isc_statscounter_t counter, isc_statscounter_t value) {
+ REQUIRE(ISC_STATSMULTI_VALID(stats));
+ REQUIRE(counter >= stats->n_additive);
+ REQUIRE(counter < stats->ncounters);
+
+ isc_tid_t tid = isc_tid();
+ int thread_id = ISC_MAX(tid, 0);
+ if (thread_id >= stats->num_threads) {
+ thread_id = 0;
+ }
+ int index = thread_id * stats->per_thread_capacity + counter;
+
+ /* Atomically update if the new value is greater than current */
+ isc_statscounter_t current = atomic_load_relaxed(&stats->counters[index]);
+ while (value > current) {
+ if (atomic_compare_exchange_weak_relaxed(&stats->counters[index], ¤t, value)) {
+ break;
+ }
+ /* current was updated by the failed compare_exchange, try again */
+ }
+}
+
+isc_statscounter_t
+isc_statsmulti_get_highwater(isc_statsmulti_t *stats, isc_statscounter_t counter) {
+ REQUIRE(ISC_STATSMULTI_VALID(stats));
+ REQUIRE(counter >= stats->n_additive);
+ REQUIRE(counter < stats->ncounters);
+
+ isc_statscounter_t max_value = 0;
+ /* Find maximum value across all threads */
+ for (int thread = 0; thread < stats->num_threads; thread++) {
+ int index = thread * stats->per_thread_capacity + counter;
+ isc_statscounter_t value = atomic_load_acquire(&stats->counters[index]);
+ if (value > max_value) {
+ max_value = value;
+ }
+ }
+ return max_value;
+}