Home | History | Annotate | Line # | Download | only in isc
      1 /*	$NetBSD: counter.c,v 1.9 2025/05/21 14:48:04 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * SPDX-License-Identifier: MPL-2.0
      7  *
      8  * This Source Code Form is subject to the terms of the Mozilla Public
      9  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11  *
     12  * See the COPYRIGHT file distributed with this work for additional
     13  * information regarding copyright ownership.
     14  */
     15 
     16 /*! \file */
     17 
     18 #include <stdbool.h>
     19 #include <stddef.h>
     20 
     21 #include <isc/atomic.h>
     22 #include <isc/counter.h>
     23 #include <isc/magic.h>
     24 #include <isc/mem.h>
     25 #include <isc/refcount.h>
     26 #include <isc/util.h>
     27 
     28 #define COUNTER_MAGIC	 ISC_MAGIC('C', 'n', 't', 'r')
     29 #define VALID_COUNTER(r) ISC_MAGIC_VALID(r, COUNTER_MAGIC)
     30 
     31 struct isc_counter {
     32 	unsigned int magic;
     33 	isc_mem_t *mctx;
     34 	isc_refcount_t references;
     35 	atomic_uint_fast32_t limit;
     36 	atomic_uint_fast32_t used;
     37 };
     38 
     39 isc_result_t
     40 isc_counter_create(isc_mem_t *mctx, int limit, isc_counter_t **counterp) {
     41 	isc_counter_t *counter;
     42 
     43 	REQUIRE(counterp != NULL && *counterp == NULL);
     44 
     45 	counter = isc_mem_get(mctx, sizeof(*counter));
     46 
     47 	counter->mctx = NULL;
     48 	isc_mem_attach(mctx, &counter->mctx);
     49 
     50 	isc_refcount_init(&counter->references, 1);
     51 	atomic_init(&counter->limit, limit);
     52 	atomic_init(&counter->used, 0);
     53 
     54 	counter->magic = COUNTER_MAGIC;
     55 	*counterp = counter;
     56 	return ISC_R_SUCCESS;
     57 }
     58 
     59 isc_result_t
     60 isc_counter_increment(isc_counter_t *counter) {
     61 	uint32_t used = atomic_fetch_add_relaxed(&counter->used, 1) + 1;
     62 	uint32_t limit = atomic_load_acquire(&counter->limit);
     63 
     64 	if (limit != 0 && used >= limit) {
     65 		return ISC_R_QUOTA;
     66 	}
     67 
     68 	return ISC_R_SUCCESS;
     69 }
     70 
     71 unsigned int
     72 isc_counter_used(isc_counter_t *counter) {
     73 	REQUIRE(VALID_COUNTER(counter));
     74 
     75 	return atomic_load_acquire(&counter->used);
     76 }
     77 
     78 void
     79 isc_counter_setlimit(isc_counter_t *counter, int limit) {
     80 	REQUIRE(VALID_COUNTER(counter));
     81 
     82 	atomic_store(&counter->limit, limit);
     83 }
     84 
     85 unsigned int
     86 isc_counter_getlimit(isc_counter_t *counter) {
     87 	REQUIRE(VALID_COUNTER(counter));
     88 
     89 	return atomic_load_acquire(&counter->limit);
     90 }
     91 
     92 void
     93 isc_counter_attach(isc_counter_t *source, isc_counter_t **targetp) {
     94 	REQUIRE(VALID_COUNTER(source));
     95 	REQUIRE(targetp != NULL && *targetp == NULL);
     96 
     97 	isc_refcount_increment(&source->references);
     98 
     99 	*targetp = source;
    100 }
    101 
    102 static void
    103 destroy(isc_counter_t *counter) {
    104 	isc_refcount_destroy(&counter->references);
    105 	counter->magic = 0;
    106 	isc_mem_putanddetach(&counter->mctx, counter, sizeof(*counter));
    107 }
    108 
    109 void
    110 isc_counter_detach(isc_counter_t **counterp) {
    111 	isc_counter_t *counter;
    112 
    113 	REQUIRE(counterp != NULL && *counterp != NULL);
    114 	counter = *counterp;
    115 	*counterp = NULL;
    116 	REQUIRE(VALID_COUNTER(counter));
    117 
    118 	if (isc_refcount_decrement(&counter->references) == 1) {
    119 		destroy(counter);
    120 	}
    121 }
    122