mhd_atomic_counter.h (14022B)
1 /* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */ 2 /* 3 This file is part of GNU libmicrohttpd. 4 Copyright (C) 2024-2025 Evgeny Grin (Karlson2k) 5 6 GNU libmicrohttpd is free software; you can redistribute it and/or 7 modify it under the terms of the GNU Lesser General Public 8 License as published by the Free Software Foundation; either 9 version 2.1 of the License, or (at your option) any later version. 10 11 GNU libmicrohttpd is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Lesser General Public License for more details. 15 16 Alternatively, you can redistribute GNU libmicrohttpd and/or 17 modify it under the terms of the GNU General Public License as 18 published by the Free Software Foundation; either version 2 of 19 the License, or (at your option) any later version, together 20 with the eCos exception, as follows: 21 22 As a special exception, if other files instantiate templates or 23 use macros or inline functions from this file, or you compile this 24 file and link it with other works to produce a work based on this 25 file, this file does not by itself cause the resulting work to be 26 covered by the GNU General Public License. However the source code 27 for this file must still be made available in accordance with 28 section (3) of the GNU General Public License v2. 29 30 This exception does not invalidate any other reasons why a work 31 based on this file might be covered by the GNU General Public 32 License. 33 34 You should have received copies of the GNU Lesser General Public 35 License and the GNU General Public License along with this library; 36 if not, see <https://www.gnu.org/licenses/>. 37 */ 38 39 /** 40 * @file src/mhd2/mhd_atomic_counter.h 41 * @brief The definition of the atomic counter type and related functions 42 * declarations 43 * @author Karlson2k (Evgeny Grin) 44 */ 45 46 #ifndef MHD_ATOMIC_COUNTER_H 47 #define MHD_ATOMIC_COUNTER_H 1 48 49 #include "mhd_sys_options.h" 50 51 #include "mhd_assert.h" 52 53 #include "sys_sizet_type.h" 54 55 /* Use 'size_t' to make sure it would never overflow when used for 56 * MHD needs. */ 57 58 /** 59 * The type used to contain the counter value. 60 * Always unsigned. 61 */ 62 #define mhd_ATOMIC_COUNTER_TYPE size_t 63 /** 64 * The maximum counter value 65 */ 66 #define mhd_ATOMIC_COUNTER_MAX \ 67 ((mhd_ATOMIC_COUNTER_TYPE) (~((mhd_ATOMIC_COUNTER_TYPE) 0))) 68 69 #ifdef MHD_SUPPORT_THREADS 70 71 # if defined(MHD_SUPPORT_ATOMIC_COUNTERS) && ! defined(__STDC_NO_ATOMICS__) 72 73 /** 74 * Atomic operations are based native compiler support for atomics 75 */ 76 # define mhd_ATOMIC_NATIVE 1 77 # else 78 /** 79 * Atomic operations are based on locks 80 */ 81 # define mhd_ATOMIC_BY_LOCKS 1 82 # endif 83 84 #else /* ! MHD_SUPPORT_THREADS */ 85 86 /** 87 * Atomic because single thread environment is used 88 */ 89 # define mhd_ATOMIC_SINGLE_THREAD 1 90 #endif /* ! MHD_SUPPORT_THREADS */ 91 92 #if defined(mhd_ATOMIC_NATIVE) 93 # include <stdatomic.h> 94 95 /** 96 * The atomic counter 97 */ 98 struct mhd_AtomicCounter 99 { 100 /** 101 * Counter value. 102 */ 103 volatile _Atomic mhd_ATOMIC_COUNTER_TYPE count; 104 }; 105 106 #elif defined(mhd_ATOMIC_BY_LOCKS) 107 # include "mhd_locks.h" 108 # include "sys_bool_type.h" 109 110 /** 111 * The atomic counter 112 */ 113 struct mhd_AtomicCounter 114 { 115 /** 116 * Counter value. 117 * Must be read or written only with @a lock held. 118 */ 119 volatile mhd_ATOMIC_COUNTER_TYPE count; 120 /** 121 * The mutex. 122 */ 123 mhd_mutex lock; 124 }; 125 126 #elif defined(mhd_ATOMIC_SINGLE_THREAD) 127 128 /** 129 * The atomic counter 130 */ 131 struct mhd_AtomicCounter 132 { 133 /** 134 * Counter value. 135 */ 136 volatile mhd_ATOMIC_COUNTER_TYPE count; 137 }; 138 139 #endif /* mhd_ATOMIC_SINGLE_THREAD */ 140 141 142 #if defined(mhd_ATOMIC_NATIVE) 143 144 /** 145 * Initialise the counter to specified value. 146 * @param pcnt the pointer to the counter to initialise 147 * @param initial_value the initial value for the counter 148 * @return 'true' if succeed, "false' if failed 149 * @warning Must not be called for the counters that has been initialised 150 * already. 151 */ 152 # define mhd_atomic_counter_init(pcnt,initial_value) \ 153 (atomic_init (&((pcnt)->count), (initial_value)), (! 0)) 154 155 /** 156 * Deinitialise the counter. 157 * @param pcnt the pointer to the counter to deinitialise 158 * @warning Must be called only for the counters that has been initialised. 159 */ 160 # define mhd_atomic_counter_deinit(pcnt) ((void) 0) 161 162 /** 163 * Get the value of the counter and atomically increment the counter. 164 * The value may overflow and wrap back to zero. 165 * @param pcnt the pointer to the counter to increment 166 * @return the counter value before the increment 167 */ 168 # define mhd_atomic_counter_get_inc_wrap(pcnt) \ 169 atomic_fetch_add_explicit (&((pcnt)->count), \ 170 1, memory_order_relaxed) 171 172 # ifdef NDEBUG 173 /** 174 * Atomically increment the value of the counter. 175 * Counter overflow is detected in debug builds. 176 * @param pcnt the pointer to the counter to increment 177 */ 178 # define mhd_atomic_counter_inc(pcnt) \ 179 do { (void) \ 180 atomic_fetch_add_explicit (&((pcnt)->count), 1, \ 181 memory_order_relaxed); } while (0) 182 183 /** 184 * Get the value of the counter and atomically increment the counter. 185 * Counter overflow is detected in debug builds. 186 * @param pcnt the pointer to the counter to increment 187 * @return the counter value before the increment 188 */ 189 # define mhd_atomic_counter_get_inc(pcnt) \ 190 mhd_atomic_counter_get_inc_wrap ((pcnt)) 191 192 /** 193 * Get the value of the counter and atomically decrement the counter. 194 * Counter underflow is detected in debug builds. 195 * @param pcnt the pointer to the counter to decrement 196 * @return the counter value before the decrement 197 */ 198 # define mhd_atomic_counter_get_dec(pcnt) \ 199 atomic_fetch_sub_explicit (&((pcnt)->count), \ 200 1, memory_order_release) 201 # else /* _DEBUG */ 202 /** 203 * Atomically increment the value of the counter. 204 * Counter overflow is detected in debug builds. 205 * @param pcnt the pointer to the counter to increment 206 */ 207 # define mhd_atomic_counter_inc(pcnt) \ 208 do { mhd_ATOMIC_COUNTER_TYPE old_val = \ 209 atomic_fetch_add_explicit (&((pcnt)->count), 1, \ 210 memory_order_relaxed); \ 211 mhd_assert (mhd_ATOMIC_COUNTER_MAX != old_val); } while (0) 212 213 /** 214 * Get the value of the counter and atomically increment the counter. 215 * Counter overflow is detected in debug builds. 216 * @param pcnt the pointer to the counter to increment 217 * @return the counter value before the increment 218 */ 219 mhd_static_inline mhd_ATOMIC_COUNTER_TYPE 220 mhd_atomic_counter_get_inc (struct mhd_AtomicCounter *pcnt) 221 { 222 mhd_ATOMIC_COUNTER_TYPE ret; 223 224 ret = mhd_atomic_counter_get_inc_wrap (pcnt); 225 226 mhd_assert (mhd_ATOMIC_COUNTER_MAX != ret); 227 228 return ret; 229 } 230 231 232 /** 233 * Get the value of the counter and atomically decrement the counter. 234 * Counter underflow is detected in debug builds. 235 * @param pcnt the pointer to the counter to decrement 236 * @return the counter value before the decrement 237 */ 238 mhd_static_inline mhd_ATOMIC_COUNTER_TYPE 239 mhd_atomic_counter_get_dec (struct mhd_AtomicCounter *pcnt) 240 { 241 mhd_ATOMIC_COUNTER_TYPE ret; 242 243 ret = atomic_fetch_sub_explicit (&((pcnt)->count),1, memory_order_relaxed); 244 245 mhd_assert (0 != ret); 246 247 return ret; 248 } 249 250 251 # endif /* _DEBUG */ 252 253 /** 254 * Atomically get the value of the counter. 255 * @param pcnt the pointer to the counter to get 256 * @return the counter value 257 */ 258 # define mhd_atomic_counter_get(pcnt) \ 259 (atomic_load_explicit (&((pcnt)->count), memory_order_relaxed)) 260 261 #elif defined(mhd_ATOMIC_BY_LOCKS) 262 263 /** 264 * Initialise the counter to specified value. 265 * @param pcnt the pointer to the counter to initialise 266 * @param initial_value the initial value for the counter 267 * @return 'true' if succeed, "false' if failed 268 * @warning Must not be called for the counters that has been initialised 269 * already. 270 */ 271 # define mhd_atomic_counter_init(pcnt,initial_value) \ 272 ((pcnt)->count = (initial_value), \ 273 mhd_mutex_init_short (&((pcnt)->lock))) 274 275 /** 276 * Deinitialise the counter. 277 * @param pcnt the pointer to the counter to deinitialise 278 * @warning Must be called only for the counters that has been initialised. 279 */ 280 # define mhd_atomic_counter_deinit(pcnt) \ 281 mhd_mutex_destroy_chk (&((pcnt)->lock)) 282 /** 283 * Get the value of the counter and atomically increment the counter. 284 * The value may overflow and wrap back to zero. 285 * @param pcnt the pointer to the counter to increment 286 * @return the counter value before the increment 287 */ 288 MHD_INTERNAL mhd_ATOMIC_COUNTER_TYPE 289 mhd_atomic_counter_get_inc_wrap (struct mhd_AtomicCounter *pcnt); 290 291 #ifdef NDEBUG 292 293 /** 294 * Atomically increment the value of the counter. 295 * Counter overflow is detected in debug builds. 296 * @param pcnt the pointer to the counter to increment 297 */ 298 # define mhd_atomic_counter_inc(pcnt) do { \ 299 mhd_mutex_lock_chk (&((pcnt)->lock)); \ 300 ++((pcnt)->count); \ 301 mhd_mutex_unlock_chk (&((pcnt)->lock)); } while (0) 302 303 /** 304 * Get the value of the counter and atomically increment the counter. 305 * Counter overflow is detected in debug builds. 306 * @param pcnt the pointer to the counter to increment 307 * @return the counter value before the increment 308 */ 309 #define mhd_atomic_counter_get_inc(pcnt) mhd_atomic_counter_get_inc_wrap (pcnt) 310 311 #else 312 313 /** 314 * Atomically increment the value of the counter. 315 * Counter overflow is detected in debug builds. 316 * @param pcnt the pointer to the counter to increment 317 */ 318 # define mhd_atomic_counter_inc(pcnt) do { \ 319 mhd_ATOMIC_COUNTER_TYPE old_val; \ 320 mhd_mutex_lock_chk (&((pcnt)->lock)); \ 321 old_val = (pcnt)->count++; \ 322 mhd_mutex_unlock_chk (&((pcnt)->lock)); \ 323 mhd_assert (mhd_ATOMIC_COUNTER_MAX != old_val); } while (0) 324 325 /** 326 * Get the value of the counter and atomically increment the counter. 327 * Counter overflow is detected in debug builds. 328 * @param pcnt the pointer to the counter to increment 329 * @return the counter value before the increment 330 */ 331 MHD_INTERNAL mhd_ATOMIC_COUNTER_TYPE 332 mhd_atomic_counter_get_inc (struct mhd_AtomicCounter *pcnt); 333 334 #endif 335 336 /** 337 * Get the value of the counter and atomically decrement the counter. 338 * Counter underflow is detected in debug builds. 339 * @param pcnt the pointer to the counter to decrement 340 * @return the counter value before the decrement 341 */ 342 MHD_INTERNAL mhd_ATOMIC_COUNTER_TYPE 343 mhd_atomic_counter_get_dec (struct mhd_AtomicCounter *pcnt); 344 345 /** 346 * Atomically get the value of the counter. 347 * @param pcnt the pointer to the counter to get 348 * @return the counter value 349 */ 350 MHD_INTERNAL mhd_ATOMIC_COUNTER_TYPE 351 mhd_atomic_counter_get (struct mhd_AtomicCounter *pcnt); 352 353 #elif defined(mhd_ATOMIC_SINGLE_THREAD) 354 355 /** 356 * Initialise the counter to specified value. 357 * @param pcnt the pointer to the counter to initialise 358 * @param initial_value the initial value for the counter 359 * @return 'true' if succeed, "false' if failed 360 * @warning Must not be called for the counters that has been initialised 361 * already. 362 */ 363 # define mhd_atomic_counter_init(pcnt,initial_value) \ 364 ((pcnt)->count = (initial_value), (! 0)) 365 366 /** 367 * Deinitialise the counter. 368 * @param pcnt the pointer to the counter to deinitialise 369 * @warning Must be called only for the counters that has been initialised. 370 */ 371 # define mhd_atomic_counter_deinit(pcnt) ((void) 0) 372 373 /** 374 * Get the value of the counter and atomically increment the counter. 375 * The value may overflow and wrap back to zero. 376 * @param pcnt the pointer to the counter to increment 377 * @return the counter value before the increment 378 */ 379 # define mhd_atomic_counter_get_inc_wrap(pcnt) ((pcnt)->count++) 380 381 /** 382 * Atomically increment the value of the counter. 383 * Counter overflow is detected in debug builds. 384 * @param pcnt the pointer to the counter to increment 385 */ 386 # define mhd_atomic_counter_inc(pcnt) \ 387 do { mhd_assert (mhd_ATOMIC_COUNTER_MAX != ((pcnt)->count)); \ 388 ++((pcnt)->count); } while (0) 389 390 # ifdef NDEBUG 391 /** 392 * Get the value of the counter and atomically increment the counter. 393 * Counter overflow is detected in debug builds. 394 * @param pcnt the pointer to the counter to increment 395 * @return the counter value before the increment 396 */ 397 # define mhd_atomic_counter_get_inc(pcnt) \ 398 mhd_atomic_counter_get_inc_wrap ((pcnt)) 399 400 /** 401 * Get the value of the counter and atomically decrement the counter. 402 * Counter underflow is detected in debug builds. 403 * @param pcnt the pointer to the counter to decrement 404 * @return the counter value before the decrement 405 */ 406 # define mhd_atomic_counter_get_dec(pcnt) ((pcnt)->count--) 407 408 # else /* _DEBUG */ 409 /** 410 * Get the value of the counter and atomically increment the counter. 411 * Counter overflow is detected in debug builds. 412 * @param pcnt the pointer to the counter to increment 413 * @return the counter value before the increment 414 */ 415 mhd_static_inline mhd_ATOMIC_COUNTER_TYPE 416 mhd_atomic_counter_get_inc (struct mhd_AtomicCounter *pcnt) 417 { 418 mhd_assert (mhd_ATOMIC_COUNTER_MAX != (pcnt->count)); 419 420 return mhd_atomic_counter_get_inc_wrap (pcnt); 421 } 422 423 424 /** 425 * Get the value of the counter and atomically decrement the counter. 426 * Counter underflow is detected in debug builds. 427 * @param pcnt the pointer to the counter to decrement 428 * @return the counter value before the decrement 429 */ 430 mhd_static_inline mhd_ATOMIC_COUNTER_TYPE 431 mhd_atomic_counter_get_dec (struct mhd_AtomicCounter *pcnt) 432 { 433 mhd_assert (0 != ((pcnt)->count)); 434 435 return ((pcnt)->count--); 436 } 437 438 439 # endif /* _DEBUG */ 440 441 /** 442 * Atomically get the value of the counter. 443 * @param pcnt the pointer to the counter to get 444 * @return the counter value 445 */ 446 # define mhd_atomic_counter_get(pcnt) ((pcnt)->count) 447 448 #endif /* mhd_ATOMIC_SINGLE_THREAD */ 449 450 #endif /* ! MHD_ATOMIC_COUNTER_H */