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