back to topotato report
topotato coverage report
Current view: top level - lib - seqlock.c (source / functions) Hit Total Coverage
Test: test_bgp_ecmp_enhe.py::BGP_Unnumbered_ECMP Lines: 64 72 88.9 %
Date: 2023-11-16 17:19:14 Functions: 7 16 43.8 %

          Line data    Source code
       1             : // SPDX-License-Identifier: LGPL-2.1-or-later
       2             : /*
       3             :  * "Sequence" lock primitive
       4             :  *
       5             :  * Copyright (C) 2015  David Lamparter <equinox@diac24.net>
       6             :  */
       7             : 
       8             : #define _GNU_SOURCE
       9             : 
      10             : #ifdef HAVE_CONFIG_H
      11             : #include "config.h"
      12             : #endif
      13             : 
      14             : #include <string.h>
      15             : #include <unistd.h>
      16             : #include <limits.h>
      17             : #include <errno.h>
      18             : #include <sys/types.h>
      19             : #include <sys/time.h>
      20             : #include <pthread.h>
      21             : #include <assert.h>
      22             : 
      23             : #include "seqlock.h"
      24             : 
      25             : /****************************************
      26             :  * OS specific synchronization wrappers *
      27             :  ****************************************/
      28             : 
      29             : /*
      30             :  * Linux: sys_futex()
      31             :  */
      32             : #ifdef HAVE_SYNC_LINUX_FUTEX
      33             : #include <sys/syscall.h>
      34             : #include <linux/futex.h>
      35             : 
      36          46 : static long sys_futex(void *addr1, int op, int val1,
      37             :                       const struct timespec *timeout, void *addr2, int val3)
      38             : {
      39          46 :         return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3);
      40             : }
      41             : 
      42             : #define wait_once(sqlo, val)    \
      43             :         sys_futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, NULL, NULL, 0)
      44             : #define wait_time(sqlo, val, time, reltime)     \
      45             :         sys_futex((int *)&sqlo->pos, FUTEX_WAIT_BITSET, (int)val, time, \
      46             :                   NULL, ~0U)
      47             : #define wait_poke(sqlo)         \
      48             :         sys_futex((int *)&sqlo->pos, FUTEX_WAKE, INT_MAX, NULL, NULL, 0)
      49             : 
      50             : /*
      51             :  * OpenBSD: sys_futex(), almost the same as on Linux
      52             :  */
      53             : #elif defined(HAVE_SYNC_OPENBSD_FUTEX)
      54             : #include <sys/syscall.h>
      55             : #include <sys/futex.h>
      56             : 
      57             : #define TIME_RELATIVE           1
      58             : 
      59             : #define wait_once(sqlo, val)    \
      60             :         futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, NULL, NULL, 0)
      61             : #define wait_time(sqlo, val, time, reltime)     \
      62             :         futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, reltime, NULL, 0)
      63             : #define wait_poke(sqlo)         \
      64             :         futex((int *)&sqlo->pos, FUTEX_WAKE, INT_MAX, NULL, NULL, 0)
      65             : 
      66             : /*
      67             :  * FreeBSD: _umtx_op()
      68             :  */
      69             : #elif defined(HAVE_SYNC_UMTX_OP)
      70             : #include <sys/umtx.h>
      71             : 
      72             : #define wait_once(sqlo, val)    \
      73             :         _umtx_op((void *)&sqlo->pos, UMTX_OP_WAIT_UINT, val, NULL, NULL)
      74             : static int wait_time(struct seqlock *sqlo, uint32_t val,
      75             :                       const struct timespec *abstime,
      76             :                       const struct timespec *reltime)
      77             : {
      78             :         struct _umtx_time t;
      79             :         t._flags = UMTX_ABSTIME;
      80             :         t._clockid = CLOCK_MONOTONIC;
      81             :         memcpy(&t._timeout, abstime, sizeof(t._timeout));
      82             :         return _umtx_op((void *)&sqlo->pos, UMTX_OP_WAIT_UINT, val,
      83             :                  (void *)(uintptr_t) sizeof(t), &t);
      84             : }
      85             : #define wait_poke(sqlo)         \
      86             :         _umtx_op((void *)&sqlo->pos, UMTX_OP_WAKE, INT_MAX, NULL, NULL)
      87             : 
      88             : /*
      89             :  * generic version.  used on NetBSD, Solaris and OSX.  really shitty.
      90             :  */
      91             : #else
      92             : 
      93             : #define TIME_ABS_REALTIME       1
      94             : 
      95             : #define wait_init(sqlo)         do { \
      96             :                 pthread_mutex_init(&sqlo->lock, NULL); \
      97             :                 pthread_cond_init(&sqlo->wake, NULL); \
      98             :         } while (0)
      99             : #define wait_prep(sqlo)         pthread_mutex_lock(&sqlo->lock)
     100             : #define wait_once(sqlo, val)    pthread_cond_wait(&sqlo->wake, &sqlo->lock)
     101             : #define wait_time(sqlo, val, time, reltime) \
     102             :                                 pthread_cond_timedwait(&sqlo->wake, \
     103             :                                                        &sqlo->lock, time);
     104             : #define wait_done(sqlo)         pthread_mutex_unlock(&sqlo->lock)
     105             : #define wait_poke(sqlo)         do { \
     106             :                 pthread_mutex_lock(&sqlo->lock); \
     107             :                 pthread_cond_broadcast(&sqlo->wake); \
     108             :                 pthread_mutex_unlock(&sqlo->lock); \
     109             :         } while (0)
     110             : 
     111             : #endif
     112             : 
     113             : #ifndef wait_init
     114             : #define wait_init(sqlo)         /**/
     115             : #define wait_prep(sqlo)         /**/
     116             : #define wait_done(sqlo)         /**/
     117             : #endif /* wait_init */
     118             : 
     119             : 
     120          23 : void seqlock_wait(struct seqlock *sqlo, seqlock_val_t val)
     121             : {
     122          23 :         seqlock_val_t cur, cal;
     123             : 
     124          23 :         seqlock_assert_valid(val);
     125             : 
     126          23 :         wait_prep(sqlo);
     127          23 :         cur = atomic_load_explicit(&sqlo->pos, memory_order_relaxed);
     128             : 
     129          35 :         while (cur & SEQLOCK_HELD) {
     130          29 :                 cal = SEQLOCK_VAL(cur) - val - 1;
     131          29 :                 assert(cal < 0x40000000 || cal > 0xc0000000);
     132          29 :                 if (cal < 0x80000000)
     133             :                         break;
     134             : 
     135          12 :                 if ((cur & SEQLOCK_WAITERS)
     136          12 :                     || atomic_compare_exchange_weak_explicit(
     137             :                                 &sqlo->pos, &cur, cur | SEQLOCK_WAITERS,
     138             :                                 memory_order_relaxed, memory_order_relaxed)) {
     139          12 :                         wait_once(sqlo, cur | SEQLOCK_WAITERS);
     140          12 :                         cur = atomic_load_explicit(&sqlo->pos,
     141             :                                 memory_order_relaxed);
     142             :                 }
     143             :                 /* else: we failed to swap in cur because it just changed */
     144             :         }
     145          23 :         wait_done(sqlo);
     146          23 : }
     147             : 
     148          47 : bool seqlock_timedwait(struct seqlock *sqlo, seqlock_val_t val,
     149             :                        const struct timespec *abs_monotime_limit)
     150             : {
     151             : /*
     152             :  * ABS_REALTIME - used on NetBSD, Solaris and OSX
     153             :  */
     154             : #ifdef TIME_ABS_REALTIME
     155             : #define time_arg1 &abs_rt
     156             : #define time_arg2 NULL
     157             : #define time_prep
     158             :         struct timespec curmono, abs_rt;
     159             : 
     160             :         clock_gettime(CLOCK_MONOTONIC, &curmono);
     161             :         clock_gettime(CLOCK_REALTIME, &abs_rt);
     162             : 
     163             :         abs_rt.tv_nsec += abs_monotime_limit->tv_nsec - curmono.tv_nsec;
     164             :         if (abs_rt.tv_nsec < 0) {
     165             :                 abs_rt.tv_sec--;
     166             :                 abs_rt.tv_nsec += 1000000000;
     167             :         } else if (abs_rt.tv_nsec >= 1000000000) {
     168             :                 abs_rt.tv_sec++;
     169             :                 abs_rt.tv_nsec -= 1000000000;
     170             :         }
     171             :         abs_rt.tv_sec += abs_monotime_limit->tv_sec - curmono.tv_sec;
     172             : 
     173             : /*
     174             :  * RELATIVE - used on OpenBSD (might get a patch to get absolute monotime)
     175             :  */
     176             : #elif defined(TIME_RELATIVE)
     177             :         struct timespec reltime;
     178             : 
     179             : #define time_arg1 abs_monotime_limit
     180             : #define time_arg2 &reltime
     181             : #define time_prep \
     182             :         clock_gettime(CLOCK_MONOTONIC, &reltime);                              \
     183             :         reltime.tv_sec = abs_monotime_limit.tv_sec - reltime.tv_sec;           \
     184             :         reltime.tv_nsec = abs_monotime_limit.tv_nsec - reltime.tv_nsec;        \
     185             :         if (reltime.tv_nsec < 0) {                                             \
     186             :                 reltime.tv_sec--;                                              \
     187             :                 reltime.tv_nsec += 1000000000;                                 \
     188             :         }
     189             : /*
     190             :  * FreeBSD & Linux: absolute time re. CLOCK_MONOTONIC
     191             :  */
     192             : #else
     193             : #define time_arg1 abs_monotime_limit
     194             : #define time_arg2 NULL
     195             : #define time_prep
     196             : #endif
     197             : 
     198          47 :         bool ret = true;
     199          47 :         seqlock_val_t cur, cal;
     200             : 
     201          47 :         seqlock_assert_valid(val);
     202             : 
     203          47 :         wait_prep(sqlo);
     204          47 :         cur = atomic_load_explicit(&sqlo->pos, memory_order_relaxed);
     205             : 
     206          57 :         while (cur & SEQLOCK_HELD) {
     207          15 :                 cal = SEQLOCK_VAL(cur) - val - 1;
     208          15 :                 assert(cal < 0x40000000 || cal > 0xc0000000);
     209          15 :                 if (cal < 0x80000000)
     210             :                         break;
     211             : 
     212          11 :                 if ((cur & SEQLOCK_WAITERS)
     213          11 :                     || atomic_compare_exchange_weak_explicit(
     214             :                                 &sqlo->pos, &cur, cur | SEQLOCK_WAITERS,
     215             :                                 memory_order_relaxed, memory_order_relaxed)) {
     216          11 :                         int rv;
     217             : 
     218             :                         time_prep
     219             : 
     220          11 :                         rv = wait_time(sqlo, cur | SEQLOCK_WAITERS, time_arg1,
     221             :                                        time_arg2);
     222          11 :                         if (rv) {
     223             :                                 ret = false;
     224             :                                 break;
     225             :                         }
     226          10 :                         cur = atomic_load_explicit(&sqlo->pos,
     227             :                                 memory_order_relaxed);
     228             :                 }
     229             :         }
     230          47 :         wait_done(sqlo);
     231             : 
     232          47 :         return ret;
     233             : }
     234             : 
     235           0 : bool seqlock_check(struct seqlock *sqlo, seqlock_val_t val)
     236             : {
     237           0 :         seqlock_val_t cur;
     238             : 
     239           0 :         seqlock_assert_valid(val);
     240             : 
     241           0 :         cur = atomic_load_explicit(&sqlo->pos, memory_order_relaxed);
     242           0 :         if (!(cur & SEQLOCK_HELD))
     243             :                 return true;
     244           0 :         cur = SEQLOCK_VAL(cur) - val - 1;
     245           0 :         assert(cur < 0x40000000 || cur > 0xc0000000);
     246           0 :         return cur < 0x80000000;
     247             : }
     248             : 
     249       13759 : void seqlock_acquire_val(struct seqlock *sqlo, seqlock_val_t val)
     250             : {
     251       13759 :         seqlock_val_t prev;
     252             : 
     253       13759 :         seqlock_assert_valid(val);
     254             : 
     255       13759 :         prev = atomic_exchange_explicit(&sqlo->pos, val, memory_order_relaxed);
     256       13759 :         if (prev & SEQLOCK_WAITERS)
     257           3 :                 wait_poke(sqlo);
     258       13759 : }
     259             : 
     260        6882 : void seqlock_release(struct seqlock *sqlo)
     261             : {
     262        6882 :         seqlock_val_t prev;
     263             : 
     264        6882 :         prev = atomic_exchange_explicit(&sqlo->pos, 0, memory_order_relaxed);
     265        6882 :         if (prev & SEQLOCK_WAITERS)
     266           8 :                 wait_poke(sqlo);
     267        6882 : }
     268             : 
     269          22 : void seqlock_init(struct seqlock *sqlo)
     270             : {
     271          22 :         sqlo->pos = 0;
     272          22 :         wait_init(sqlo);
     273          22 : }
     274             : 
     275             : 
     276       27482 : seqlock_val_t seqlock_cur(struct seqlock *sqlo)
     277             : {
     278       27482 :         return SEQLOCK_VAL(atomic_load_explicit(&sqlo->pos,
     279             :                                                 memory_order_relaxed));
     280             : }
     281             : 
     282          18 : seqlock_val_t seqlock_bump(struct seqlock *sqlo)
     283             : {
     284          18 :         seqlock_val_t val, cur;
     285             : 
     286          18 :         cur = atomic_load_explicit(&sqlo->pos, memory_order_relaxed);
     287          18 :         seqlock_assert_valid(cur);
     288             : 
     289          18 :         do {
     290          18 :                 val = SEQLOCK_VAL(cur) + SEQLOCK_INCR;
     291          18 :         } while (!atomic_compare_exchange_weak_explicit(&sqlo->pos, &cur, val,
     292             :                         memory_order_relaxed, memory_order_relaxed));
     293             : 
     294          18 :         if (cur & SEQLOCK_WAITERS)
     295          12 :                 wait_poke(sqlo);
     296          18 :         return val;
     297             : }

Generated by: LCOV version v1.16-topotato