back to topotato report
topotato coverage report
Current view: top level - lib - atomlist.c (source / functions) Hit Total Coverage
Test: test_ospf6_vlink.py::VirtualLinkBasic Lines: 61 141 43.3 %
Date: 2023-02-16 02:06:43 Functions: 4 9 44.4 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2016-2018  David Lamparter, for NetDEF, Inc.
       3             :  *
       4             :  * Permission to use, copy, modify, and distribute this software for any
       5             :  * purpose with or without fee is hereby granted, provided that the above
       6             :  * copyright notice and this permission notice appear in all copies.
       7             :  *
       8             :  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
       9             :  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
      10             :  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
      11             :  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
      12             :  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
      13             :  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
      14             :  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
      15             :  */
      16             : 
      17             : #ifdef HAVE_CONFIG_H
      18             : #include "config.h"
      19             : #endif
      20             : 
      21             : #include <assert.h>
      22             : 
      23             : #include "atomlist.h"
      24             : 
      25           0 : void atomlist_add_head(struct atomlist_head *h, struct atomlist_item *item)
      26             : {
      27           0 :         atomptr_t prevval;
      28           0 :         atomptr_t i = atomptr_i(item);
      29             : 
      30           0 :         atomic_fetch_add_explicit(&h->count, 1, memory_order_relaxed);
      31             : 
      32             :         /* updating ->last is possible here, but makes the code considerably
      33             :          * more complicated... let's not.
      34             :          */
      35           0 :         prevval = ATOMPTR_NULL;
      36           0 :         item->next = ATOMPTR_NULL;
      37             : 
      38             :         /* head-insert atomically
      39             :          * release barrier: item + item->next writes must be completed
      40             :          */
      41           0 :         while (!atomic_compare_exchange_weak_explicit(&h->first, &prevval, i,
      42             :                                 memory_order_release, memory_order_relaxed))
      43           0 :                 atomic_store_explicit(&item->next, prevval,
      44             :                                 memory_order_relaxed);
      45           0 : }
      46             : 
      47         232 : void atomlist_add_tail(struct atomlist_head *h, struct atomlist_item *item)
      48             : {
      49         232 :         atomptr_t prevval = ATOMPTR_NULL;
      50         232 :         atomptr_t i = atomptr_i(item);
      51         232 :         atomptr_t hint;
      52         232 :         struct atomlist_item *prevptr;
      53         232 :         _Atomic atomptr_t *prev;
      54             : 
      55         232 :         item->next = ATOMPTR_NULL;
      56             : 
      57         232 :         atomic_fetch_add_explicit(&h->count, 1, memory_order_relaxed);
      58             : 
      59             :         /* place new item into ->last
      60             :          * release: item writes completed;  acquire: DD barrier on hint
      61             :          */
      62         232 :         hint = atomic_exchange_explicit(&h->last, i, memory_order_acq_rel);
      63             : 
      64         232 :         while (1) {
      65         232 :                 if (atomptr_p(hint) == NULL)
      66          40 :                         prev = &h->first;
      67             :                 else
      68         192 :                         prev = &atomlist_itemp(hint)->next;
      69             : 
      70         232 :                 do {
      71         232 :                         prevval = atomic_load_explicit(prev,
      72             :                                         memory_order_consume);
      73         232 :                         prevptr = atomlist_itemp(prevval);
      74         232 :                         if (prevptr == NULL)
      75             :                                 break;
      76             : 
      77           0 :                         prev = &prevptr->next;
      78           0 :                 } while (prevptr);
      79             : 
      80             :                 /* last item is being deleted - start over */
      81         232 :                 if (atomptr_l(prevval)) {
      82           0 :                         hint = ATOMPTR_NULL;
      83           0 :                         continue;
      84             :                 }
      85             : 
      86             :                 /* no barrier - item->next is NULL and was so in xchg above */
      87         232 :                 if (!atomic_compare_exchange_strong_explicit(prev, &prevval, i,
      88             :                                         memory_order_consume,
      89             :                                         memory_order_consume)) {
      90           0 :                         hint = prevval;
      91           0 :                         continue;
      92             :                 }
      93         232 :                 break;
      94             :         }
      95         232 : }
      96             : 
      97         168 : static void atomlist_del_core(struct atomlist_head *h,
      98             :                               struct atomlist_item *item,
      99             :                               _Atomic atomptr_t *hint,
     100             :                               atomptr_t next)
     101             : {
     102         168 :         _Atomic atomptr_t *prev = hint ? hint : &h->first, *upd;
     103         168 :         atomptr_t prevval, updval;
     104         168 :         struct atomlist_item *prevptr;
     105             : 
     106             :         /* drop us off "last" if needed.  no r/w to barrier. */
     107         168 :         prevval = atomptr_i(item);
     108         168 :         atomic_compare_exchange_strong_explicit(&h->last, &prevval,
     109             :                         ATOMPTR_NULL,
     110             :                         memory_order_relaxed, memory_order_relaxed);
     111             : 
     112         168 :         atomic_fetch_sub_explicit(&h->count, 1, memory_order_relaxed);
     113             : 
     114             :         /* the following code should be identical (except sort<>list) to
     115             :          * atomsort_del_hint()
     116             :          */
     117         168 :         while (1) {
     118         168 :                 upd = NULL;
     119         168 :                 updval = ATOMPTR_LOCK;
     120             : 
     121         216 :                 do {
     122         216 :                         prevval = atomic_load_explicit(prev,
     123             :                                         memory_order_consume);
     124             : 
     125             :                         /* track the beginning of a chain of deleted items
     126             :                          * this is necessary to make this lock-free; we can
     127             :                          * complete deletions started by other threads.
     128             :                          */
     129         216 :                         if (!atomptr_l(prevval)) {
     130         216 :                                 updval = prevval;
     131         216 :                                 upd = prev;
     132             :                         }
     133             : 
     134         216 :                         prevptr = atomlist_itemp(prevval);
     135         216 :                         if (prevptr == item)
     136             :                                 break;
     137             : 
     138          48 :                         prev = &prevptr->next;
     139          48 :                 } while (prevptr);
     140             : 
     141         168 :                 if (prevptr != item)
     142             :                         /* another thread completed our deletion */
     143         168 :                         return;
     144             : 
     145         168 :                 if (!upd || atomptr_l(updval)) {
     146             :                         /* failed to find non-deleted predecessor...
     147             :                          * have to try again
     148             :                          */
     149           0 :                         prev = &h->first;
     150           0 :                         continue;
     151             :                 }
     152             : 
     153         168 :                 if (!atomic_compare_exchange_strong_explicit(upd, &updval,
     154             :                                         next, memory_order_consume,
     155             :                                         memory_order_consume)) {
     156             :                         /* prev doesn't point to item anymore, something
     157             :                          * was inserted.  continue at same position forward.
     158             :                          */
     159           0 :                         continue;
     160             :                 }
     161             :                 break;
     162             :         }
     163             : }
     164             : 
     165          56 : void atomlist_del_hint(struct atomlist_head *h, struct atomlist_item *item,
     166             :                 _Atomic atomptr_t *hint)
     167             : {
     168          56 :         atomptr_t next;
     169             : 
     170             :         /* mark ourselves in-delete - full barrier */
     171          56 :         next = atomic_fetch_or_explicit(&item->next, ATOMPTR_LOCK,
     172             :                                 memory_order_acquire);
     173          56 :         assert(!atomptr_l(next));       /* delete race on same item */
     174             : 
     175          56 :         atomlist_del_core(h, item, hint, next);
     176          56 : }
     177             : 
     178         120 : struct atomlist_item *atomlist_pop(struct atomlist_head *h)
     179             : {
     180         120 :         struct atomlist_item *item;
     181         120 :         atomptr_t next;
     182             : 
     183             :         /* grab head of the list - and remember it in replval for the
     184             :          * actual delete below.  No matter what, the head of the list is
     185             :          * where we start deleting because either it's our item, or it's
     186             :          * some delete-marked items and then our item.
     187             :          */
     188         120 :         next = atomic_load_explicit(&h->first, memory_order_consume);
     189             : 
     190         120 :         do {
     191         120 :                 item = atomlist_itemp(next);
     192         120 :                 if (!item)
     193             :                         return NULL;
     194             : 
     195             :                 /* try to mark deletion */
     196         112 :                 next = atomic_fetch_or_explicit(&item->next, ATOMPTR_LOCK,
     197             :                                         memory_order_acquire);
     198             : 
     199         112 :         } while (atomptr_l(next));
     200             :         /* if loop is taken: delete race on same item (another pop or del)
     201             :          * => proceed to next item
     202             :          * if loop exited here: we have our item selected and marked
     203             :          */
     204         112 :         atomlist_del_core(h, item, &h->first, next);
     205         112 :         return item;
     206             : }
     207             : 
     208           0 : struct atomsort_item *atomsort_add(struct atomsort_head *h,
     209             :                 struct atomsort_item *item, int (*cmpfn)(
     210             :                         const struct atomsort_item *,
     211             :                         const struct atomsort_item *))
     212             : {
     213           0 :         _Atomic atomptr_t *prev;
     214           0 :         atomptr_t prevval;
     215           0 :         atomptr_t i = atomptr_i(item);
     216           0 :         struct atomsort_item *previtem;
     217           0 :         int cmpval;
     218             : 
     219           0 :         do {
     220           0 :                 prev = &h->first;
     221             : 
     222           0 :                 do {
     223           0 :                         prevval = atomic_load_explicit(prev,
     224             :                                         memory_order_acquire);
     225           0 :                         previtem = atomptr_p(prevval);
     226             : 
     227           0 :                         if (!previtem || (cmpval = cmpfn(previtem, item)) > 0)
     228             :                                 break;
     229           0 :                         if (cmpval == 0)
     230           0 :                                 return previtem;
     231             : 
     232           0 :                         prev = &previtem->next;
     233             :                 } while (1);
     234             : 
     235           0 :                 if (atomptr_l(prevval))
     236           0 :                         continue;
     237             : 
     238           0 :                 item->next = prevval;
     239           0 :                 if (atomic_compare_exchange_strong_explicit(prev, &prevval, i,
     240             :                                 memory_order_release, memory_order_relaxed))
     241             :                         break;
     242             :         } while (1);
     243             : 
     244           0 :         atomic_fetch_add_explicit(&h->count, 1, memory_order_relaxed);
     245           0 :         return NULL;
     246             : }
     247             : 
     248           0 : static void atomsort_del_core(struct atomsort_head *h,
     249             :                 struct atomsort_item *item, _Atomic atomptr_t *hint,
     250             :                 atomptr_t next)
     251             : {
     252           0 :         _Atomic atomptr_t *prev = hint ? hint : &h->first, *upd;
     253           0 :         atomptr_t prevval, updval;
     254           0 :         struct atomsort_item *prevptr;
     255             : 
     256           0 :         atomic_fetch_sub_explicit(&h->count, 1, memory_order_relaxed);
     257             : 
     258             :         /* the following code should be identical (except sort<>list) to
     259             :          * atomlist_del_core()
     260             :          */
     261           0 :         while (1) {
     262           0 :                 upd = NULL;
     263           0 :                 updval = ATOMPTR_LOCK;
     264             : 
     265           0 :                 do {
     266           0 :                         prevval = atomic_load_explicit(prev,
     267             :                                         memory_order_consume);
     268             : 
     269             :                         /* track the beginning of a chain of deleted items
     270             :                          * this is necessary to make this lock-free; we can
     271             :                          * complete deletions started by other threads.
     272             :                          */
     273           0 :                         if (!atomptr_l(prevval)) {
     274           0 :                                 updval = prevval;
     275           0 :                                 upd = prev;
     276             :                         }
     277             : 
     278           0 :                         prevptr = atomsort_itemp(prevval);
     279           0 :                         if (prevptr == item)
     280             :                                 break;
     281             : 
     282           0 :                         prev = &prevptr->next;
     283           0 :                 } while (prevptr);
     284             : 
     285           0 :                 if (prevptr != item)
     286             :                         /* another thread completed our deletion */
     287           0 :                         return;
     288             : 
     289           0 :                 if (!upd || atomptr_l(updval)) {
     290             :                         /* failed to find non-deleted predecessor...
     291             :                          * have to try again
     292             :                          */
     293           0 :                         prev = &h->first;
     294           0 :                         continue;
     295             :                 }
     296             : 
     297           0 :                 if (!atomic_compare_exchange_strong_explicit(upd, &updval,
     298             :                                         next, memory_order_relaxed,
     299             :                                         memory_order_relaxed)) {
     300             :                         /* prev doesn't point to item anymore, something
     301             :                          * was inserted.  continue at same position forward.
     302             :                          */
     303           0 :                         continue;
     304             :                 }
     305             :                 break;
     306             :         }
     307             : }
     308             : 
     309           0 : void atomsort_del_hint(struct atomsort_head *h, struct atomsort_item *item,
     310             :                 _Atomic atomptr_t *hint)
     311             : {
     312           0 :         atomptr_t next;
     313             : 
     314             :         /* mark ourselves in-delete - full barrier */
     315           0 :         next = atomic_fetch_or_explicit(&item->next, ATOMPTR_LOCK,
     316             :                                 memory_order_seq_cst);
     317           0 :         assert(!atomptr_l(next));       /* delete race on same item */
     318             : 
     319           0 :         atomsort_del_core(h, item, hint, next);
     320           0 : }
     321             : 
     322           0 : struct atomsort_item *atomsort_pop(struct atomsort_head *h)
     323             : {
     324           0 :         struct atomsort_item *item;
     325           0 :         atomptr_t next;
     326             : 
     327             :         /* grab head of the list - and remember it in replval for the
     328             :          * actual delete below.  No matter what, the head of the list is
     329             :          * where we start deleting because either it's our item, or it's
     330             :          * some delete-marked items and then our item.
     331             :          */
     332           0 :         next = atomic_load_explicit(&h->first, memory_order_consume);
     333             : 
     334           0 :         do {
     335           0 :                 item = atomsort_itemp(next);
     336           0 :                 if (!item)
     337             :                         return NULL;
     338             : 
     339             :                 /* try to mark deletion */
     340           0 :                 next = atomic_fetch_or_explicit(&item->next, ATOMPTR_LOCK,
     341             :                                         memory_order_acquire);
     342             : 
     343           0 :         } while (atomptr_l(next));
     344             :         /* if loop is taken: delete race on same item (another pop or del)
     345             :          * => proceed to next item
     346             :          * if loop exited here: we have our item selected and marked
     347             :          */
     348           0 :         atomsort_del_core(h, item, &h->first, next);
     349           0 :         return item;
     350             : }

Generated by: LCOV version v1.16-topotato