libmicrohttpd2

HTTP server C library (MHD 2.x, alpha)
Log | Files | Refs | README | LICENSE

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 */