mhd_atomic_counter.h (14027B)
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 109 /** 110 * The atomic counter 111 */ 112 struct mhd_AtomicCounter 113 { 114 /** 115 * Counter value. 116 * Must be read or written only with @a lock held. 117 */ 118 volatile mhd_ATOMIC_COUNTER_TYPE count; 119 /** 120 * The mutex. 121 */ 122 mhd_mutex lock; 123 }; 124 125 #elif defined(mhd_ATOMIC_SINGLE_THREAD) 126 127 /** 128 * The atomic counter 129 */ 130 struct mhd_AtomicCounter 131 { 132 /** 133 * Counter value. 134 */ 135 volatile mhd_ATOMIC_COUNTER_TYPE count; 136 }; 137 138 #endif /* mhd_ATOMIC_SINGLE_THREAD */ 139 140 #if defined(_MSC_FULL_VER) 141 # pragma warning(push) 142 /* Disable C4505 "unreferenced local function has been removed" */ 143 # pragma warning(disable:4505) 144 #endif /* _MSC_FULL_VER */ 145 146 #if defined(mhd_ATOMIC_NATIVE) 147 148 /** 149 * Initialise the counter to specified value. 150 * @param pcnt the pointer to the counter to initialise 151 * @param initial_value the initial value for the counter 152 * @return 'true' if succeed, "false' if failed 153 * @warning Must not be called for the counters that has been initialised 154 * already. 155 */ 156 # define mhd_atomic_counter_init(pcnt, initial_value) \ 157 (atomic_init (&((pcnt)->count), (initial_value)), (! 0)) 158 159 /** 160 * Deinitialise the counter. 161 * @param pcnt the pointer to the counter to deinitialise 162 * @warning Must be called only for the counters that has been initialised. 163 */ 164 # define mhd_atomic_counter_deinit(pcnt) ((void) 0) 165 166 /** 167 * Get the value of the counter and atomically increment the counter. 168 * The value may overflow and wrap back to zero. 169 * @param pcnt the pointer to the counter to increment 170 * @return the counter value before the increment 171 */ 172 # define mhd_atomic_counter_get_inc_wrap(pcnt) \ 173 atomic_fetch_add_explicit (&((pcnt)->count), \ 174 1u, memory_order_relaxed) 175 176 /** 177 * Atomically increment the value of the counter. 178 * Counter overflow is detected in debug builds. 179 * @param pcnt the pointer to the counter to increment 180 */ 181 # ifdef NDEBUG 182 # define mhd_atomic_counter_inc(pcnt) \ 183 do { (void) \ 184 atomic_fetch_add_explicit (&((pcnt)->count), 1u, \ 185 memory_order_relaxed); } while (0) 186 # else /* _DEBUG */ 187 # define mhd_atomic_counter_inc(pcnt) \ 188 do { mhd_ATOMIC_COUNTER_TYPE old_val = \ 189 atomic_fetch_add_explicit (&((pcnt)->count), 1u, \ 190 memory_order_relaxed); \ 191 mhd_assert (mhd_ATOMIC_COUNTER_MAX != old_val); } while (0) 192 # endif /* _DEBUG */ 193 194 /** 195 * Get the value of the counter and atomically increment the counter. 196 * Counter overflow is detected in debug builds. 197 * @param pcnt the pointer to the counter to increment 198 * @return the counter value before the increment 199 */ 200 # ifdef NDEBUG 201 # define mhd_atomic_counter_get_inc(pcnt) \ 202 mhd_atomic_counter_get_inc_wrap ((pcnt)) 203 # else /* _DEBUG */ 204 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ mhd_ATOMIC_COUNTER_TYPE 205 mhd_atomic_counter_get_inc (struct mhd_AtomicCounter *pcnt) 206 { 207 mhd_ATOMIC_COUNTER_TYPE ret; 208 209 ret = mhd_atomic_counter_get_inc_wrap (pcnt); 210 211 mhd_assert (mhd_ATOMIC_COUNTER_MAX != ret); 212 213 return ret; 214 } 215 216 217 # endif /* _DEBUG */ 218 219 /** 220 * Get the value of the counter and atomically decrement the counter. 221 * Counter underflow is detected in debug builds. 222 * @param pcnt the pointer to the counter to decrement 223 * @return the counter value before the decrement 224 */ 225 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ mhd_ATOMIC_COUNTER_TYPE 226 mhd_atomic_counter_get_dec (struct mhd_AtomicCounter *pcnt) 227 { 228 mhd_ATOMIC_COUNTER_TYPE ret; 229 230 ret = atomic_fetch_sub_explicit (&((pcnt)->count), 231 1u, 232 memory_order_release); 233 if (1u == ret) /* Sync with preceding releases when the last reference is dropped */ 234 atomic_thread_fence (memory_order_acquire); 235 236 mhd_assert (0u != ret); 237 238 return ret; 239 } 240 241 242 /** 243 * Atomically get the value of the counter. 244 * @param pcnt the pointer to the counter to get 245 * @return the counter value 246 */ 247 # define mhd_atomic_counter_get(pcnt) \ 248 (atomic_load_explicit (&((pcnt)->count), memory_order_relaxed)) 249 250 #elif defined(mhd_ATOMIC_BY_LOCKS) 251 252 /** 253 * Initialise the counter to specified value. 254 * @param pcnt the pointer to the counter to initialise 255 * @param initial_value the initial value for the counter 256 * @return 'true' if succeed, "false' if failed 257 * @warning Must not be called for the counters that has been initialised 258 * already. 259 */ 260 # define mhd_atomic_counter_init(pcnt, initial_value) \ 261 ((pcnt)->count = (initial_value), \ 262 mhd_mutex_init_short (&((pcnt)->lock))) 263 264 /** 265 * Deinitialise the counter. 266 * @param pcnt the pointer to the counter to deinitialise 267 * @warning Must be called only for the counters that has been initialised. 268 */ 269 # define mhd_atomic_counter_deinit(pcnt) \ 270 mhd_mutex_destroy_chk (&((pcnt)->lock)) 271 /** 272 * Get the value of the counter and atomically increment the counter. 273 * The value may overflow and wrap back to zero. 274 * @param pcnt the pointer to the counter to increment 275 * @return the counter value before the increment 276 */ 277 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ mhd_ATOMIC_COUNTER_TYPE 278 mhd_atomic_counter_get_inc_wrap (struct mhd_AtomicCounter *pcnt) 279 { 280 mhd_ATOMIC_COUNTER_TYPE ret; 281 282 mhd_mutex_lock_chk (&(pcnt->lock)); 283 ret = pcnt->count++; 284 mhd_mutex_unlock_chk (&(pcnt->lock)); 285 286 return ret; 287 } 288 289 290 /** 291 * Atomically increment the value of the counter. 292 * Counter overflow is detected in debug builds. 293 * @param pcnt the pointer to the counter to increment 294 */ 295 # ifdef NDEBUG 296 # define mhd_atomic_counter_inc(pcnt) do { \ 297 mhd_mutex_lock_chk (&((pcnt)->lock)); \ 298 ++((pcnt)->count); \ 299 mhd_mutex_unlock_chk (&((pcnt)->lock)); } while (0) 300 # else /* _DEBUG */ 301 # define mhd_atomic_counter_inc(pcnt) do {\ 302 mhd_ATOMIC_COUNTER_TYPE old_val; \ 303 mhd_mutex_lock_chk (&((pcnt)->lock)); \ 304 old_val = (pcnt)->count++; \ 305 mhd_mutex_unlock_chk (&((pcnt)->lock)); \ 306 mhd_assert (mhd_ATOMIC_COUNTER_MAX != old_val); } while (0) 307 # endif /* _DEBUG */ 308 309 /** 310 * Get the value of the counter and atomically increment the counter. 311 * Counter overflow is detected in debug builds. 312 * @param pcnt the pointer to the counter to increment 313 * @return the counter value before the increment 314 */ 315 # ifdef NDEBUG 316 # define mhd_atomic_counter_get_inc(pcnt) \ 317 mhd_atomic_counter_get_inc_wrap ((pcnt)) 318 319 # else /* _DEBUG */ 320 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ mhd_ATOMIC_COUNTER_TYPE 321 mhd_atomic_counter_get_inc (struct mhd_AtomicCounter *pcnt) 322 { 323 const mhd_ATOMIC_COUNTER_TYPE ret = mhd_atomic_counter_get_inc_wrap (pcnt); 324 325 mhd_assert (mhd_ATOMIC_COUNTER_MAX != ret); /* check for overflow */ 326 327 return ret; 328 } 329 330 331 # endif /* _DEBUG */ 332 333 /** 334 * Get the value of the counter and atomically decrement the counter. 335 * Counter underflow is detected in debug builds. 336 * @param pcnt the pointer to the counter to decrement 337 * @return the counter value before the decrement 338 */ 339 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ mhd_ATOMIC_COUNTER_TYPE 340 mhd_atomic_counter_get_dec (struct mhd_AtomicCounter *pcnt) 341 { 342 mhd_ATOMIC_COUNTER_TYPE ret; 343 344 mhd_mutex_lock_chk (&(pcnt->lock)); 345 ret = pcnt->count--; 346 mhd_mutex_unlock_chk (&(pcnt->lock)); 347 348 mhd_assert (0u != ret); /* check for underflow */ 349 350 return ret; 351 } 352 353 354 /** 355 * Atomically get the value of the counter. 356 * @param pcnt the pointer to the counter to get 357 * @return the counter value 358 */ 359 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ mhd_ATOMIC_COUNTER_TYPE 360 mhd_atomic_counter_get (struct mhd_AtomicCounter *pcnt) 361 { 362 mhd_ATOMIC_COUNTER_TYPE ret; 363 364 mhd_mutex_lock_chk (&(pcnt->lock)); 365 ret = pcnt->count; 366 mhd_mutex_unlock_chk (&(pcnt->lock)); 367 368 return ret; 369 } 370 371 372 #elif defined(mhd_ATOMIC_SINGLE_THREAD) 373 374 /** 375 * Initialise the counter to specified value. 376 * @param pcnt the pointer to the counter to initialise 377 * @param initial_value the initial value for the counter 378 * @return 'true' if succeed, "false' if failed 379 * @warning Must not be called for the counters that has been initialised 380 * already. 381 */ 382 # define mhd_atomic_counter_init(pcnt, initial_value) \ 383 ((pcnt)->count = (initial_value), (! 0)) 384 385 /** 386 * Deinitialise the counter. 387 * @param pcnt the pointer to the counter to deinitialise 388 * @warning Must be called only for the counters that has been initialised. 389 */ 390 # define mhd_atomic_counter_deinit(pcnt) ((void) 0) 391 392 /** 393 * Get the value of the counter and atomically increment the counter. 394 * The value may overflow and wrap back to zero. 395 * @param pcnt the pointer to the counter to increment 396 * @return the counter value before the increment 397 */ 398 # define mhd_atomic_counter_get_inc_wrap(pcnt) ((pcnt)->count++) 399 400 /** 401 * Atomically increment the value of the counter. 402 * Counter overflow is detected in debug builds. 403 * @param pcnt the pointer to the counter to increment 404 */ 405 # define mhd_atomic_counter_inc(pcnt) \ 406 do { mhd_assert (mhd_ATOMIC_COUNTER_MAX != ((pcnt)->count)); \ 407 ++((pcnt)->count); } while (0) 408 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 # ifdef NDEBUG 416 # define mhd_atomic_counter_get_inc(pcnt) \ 417 mhd_atomic_counter_get_inc_wrap ((pcnt)) 418 # else /* _DEBUG */ 419 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ mhd_ATOMIC_COUNTER_TYPE 420 mhd_atomic_counter_get_inc (struct mhd_AtomicCounter *pcnt) 421 { 422 mhd_assert (mhd_ATOMIC_COUNTER_MAX != (pcnt->count)); 423 424 return mhd_atomic_counter_get_inc_wrap (pcnt); 425 } 426 427 428 # endif /* _DEBUG */ 429 430 /** 431 * Get the value of the counter and atomically decrement the counter. 432 * Counter underflow is detected in debug builds. 433 * @param pcnt the pointer to the counter to decrement 434 * @return the counter value before the decrement 435 */ 436 # ifdef NDEBUG 437 # define mhd_atomic_counter_get_dec(pcnt) ((pcnt)->count--) 438 # else /* _DEBUG */ 439 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ mhd_ATOMIC_COUNTER_TYPE 440 mhd_atomic_counter_get_dec (struct mhd_AtomicCounter *pcnt) 441 { 442 mhd_assert (0u != ((pcnt)->count)); 443 444 return ((pcnt)->count--); 445 } 446 447 448 # endif /* _DEBUG */ 449 450 /** 451 * Atomically get the value of the counter. 452 * @param pcnt the pointer to the counter to get 453 * @return the counter value 454 */ 455 # define mhd_atomic_counter_get(pcnt) ((pcnt)->count) 456 457 #endif /* mhd_ATOMIC_SINGLE_THREAD */ 458 459 460 #if defined(_MSC_FULL_VER) 461 /* Restore warnings */ 462 # pragma warning(pop) 463 #endif /* _MSC_FULL_VER */ 464 465 #endif /* ! MHD_ATOMIC_COUNTER_H */