counter.c revision 1.1 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