LCOV - code coverage report
Current view: top level - lib - pvector.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 26 26 100.0 %
Date: 2016-09-14 01:02:56 Functions: 5 5 100.0 %
Branches: 12 12 100.0 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright (c) 2014, 2016 Nicira, Inc.
       3                 :            :  *
       4                 :            :  * Licensed under the Apache License, Version 2.0 (the "License");
       5                 :            :  * you may not use this file except in compliance with the License.
       6                 :            :  * You may obtain a copy of the License at:
       7                 :            :  *
       8                 :            :  *     http://www.apache.org/licenses/LICENSE-2.0
       9                 :            :  *
      10                 :            :  * Unless required by applicable law or agreed to in writing, software
      11                 :            :  * distributed under the License is distributed on an "AS IS" BASIS,
      12                 :            :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      13                 :            :  * See the License for the specific language governing permissions and
      14                 :            :  * limitations under the License.
      15                 :            :  */
      16                 :            : 
      17                 :            : #ifndef PVECTOR_H
      18                 :            : #define PVECTOR_H 1
      19                 :            : 
      20                 :            : #include <stdbool.h>
      21                 :            : #include <stdint.h>
      22                 :            : #include <stdlib.h>
      23                 :            : #include "ovs-rcu.h"
      24                 :            : #include "util.h"
      25                 :            : 
      26                 :            : /* Concurrent Priority Vector
      27                 :            :  * ==========================
      28                 :            :  *
      29                 :            :  * Concurrent priority vector holds non-NULL pointers to objects in an
      30                 :            :  * increasing priority order and allows readers to traverse the vector without
      31                 :            :  * being concerned about writers modifying the vector as they are traversing
      32                 :            :  * it.
      33                 :            :  *
      34                 :            :  * The priority order is maintained as a linear vector of elements to allow
      35                 :            :  * for efficient memory prefetching.
      36                 :            :  *
      37                 :            :  * Concurrency is implemented with OVS RCU so that the readers can assume
      38                 :            :  * that once they have taken a pointer to the vector with
      39                 :            :  * pvector_cursor_init(), the 'size' member will not decrease, so that
      40                 :            :  * they can safely read 'size' entries from 'vector', and find that each
      41                 :            :  * entry has a valid, non-NULL 'ptr', and the vector is in order from highest
      42                 :            :  * to lowest 'priority'.  The 'priority' values can change any time, but only
      43                 :            :  * so that the order of the entries does not change, so readers can use
      44                 :            :  * 'priority' values read at any time after acquisition of the vector pointer.
      45                 :            :  *
      46                 :            :  * Writers can concurrently add entries to the end of the vector, incrementing
      47                 :            :  * 'size', or update the 'priority' value of an entry, but only if that does
      48                 :            :  * not change the ordering of the entries.  Writers will never change the 'ptr'
      49                 :            :  * values, or decrement the 'size' on a copy that readers have access to.
      50                 :            :  *
      51                 :            :  * Most modifications are internally staged at the 'temp' vector, from which
      52                 :            :  * they can be published at 'impl' by calling pvector_publish().  This saves
      53                 :            :  * unnecessary memory allocations when many changes are done back-to-back.
      54                 :            :  * 'temp' may contain NULL pointers and it may be in unsorted order.  It is
      55                 :            :  * sorted before it is published at 'impl', which also removes the NULLs from
      56                 :            :  * the published vector.
      57                 :            :  */
      58                 :            : 
      59                 :            : struct pvector_entry {
      60                 :            :     int priority;
      61                 :            :     void *ptr;
      62                 :            : };
      63                 :            : 
      64                 :            : struct pvector_impl {
      65                 :            :     size_t size;       /* Number of entries in the vector. */
      66                 :            :     size_t allocated;  /* Number of allocated entries. */
      67                 :            :     struct pvector_entry vector[];
      68                 :            : };
      69                 :            : 
      70                 :            : /* Concurrent priority vector. */
      71                 :            : struct pvector {
      72                 :            :     OVSRCU_TYPE(struct pvector_impl *) impl;
      73                 :            :     struct pvector_impl *temp;
      74                 :            : };
      75                 :            : 
      76                 :            : /* Initialization. */
      77                 :            : void pvector_init(struct pvector *);
      78                 :            : void pvector_destroy(struct pvector *);
      79                 :            : 
      80                 :            : /* Insertion and deletion.  These work on 'temp'.  */
      81                 :            : void pvector_insert(struct pvector *, void *, int priority);
      82                 :            : void pvector_change_priority(struct pvector *, void *, int priority);
      83                 :            : void pvector_remove(struct pvector *, void *);
      84                 :            : 
      85                 :            : /* Make the modified pvector available for iteration. */
      86                 :            : static inline void pvector_publish(struct pvector *);
      87                 :            : 
      88                 :            : /* Count.  These operate on the published pvector. */
      89                 :            : static inline size_t pvector_count(const struct pvector *);
      90                 :            : static inline bool pvector_is_empty(const struct pvector *);
      91                 :            : 
      92                 :            : /* Iteration.
      93                 :            :  *
      94                 :            :  *
      95                 :            :  * Thread-safety
      96                 :            :  * =============
      97                 :            :  *
      98                 :            :  * Iteration is safe even in a pvector that is changing concurrently.
      99                 :            :  * Multiple writers must exclude each other via e.g., a mutex.
     100                 :            :  *
     101                 :            :  * Example
     102                 :            :  * =======
     103                 :            :  *
     104                 :            :  *     struct my_node {
     105                 :            :  *         int data;
     106                 :            :  *     };
     107                 :            :  *
     108                 :            :  *     struct my_node elem1, elem2, *iter;
     109                 :            :  *     struct pvector my_pvector;
     110                 :            :  *
     111                 :            :  *     pvector_init(&my_pvector);
     112                 :            :  *     ...add data...
     113                 :            :  *     pvector_insert(&my_pvector, &elem1, 1);
     114                 :            :  *     pvector_insert(&my_pvector, &elem2, 2);
     115                 :            :  *     ...
     116                 :            :  *     PVECTOR_FOR_EACH (iter, &my_pvector) {
     117                 :            :  *         ...operate on '*iter'...
     118                 :            :  *         ...elem2 to be seen before elem1...
     119                 :            :  *     }
     120                 :            :  *     ...
     121                 :            :  *     pvector_destroy(&my_pvector);
     122                 :            :  *
     123                 :            :  * There is no PVECTOR_FOR_EACH_SAFE variant as iteration is performed on RCU
     124                 :            :  * protected instance of the priority vector.  Any concurrent modifications
     125                 :            :  * that would be disruptive for readers (such as deletions), will be performed
     126                 :            :  * on a new instance.  To see any of the modifications, a new iteration loop
     127                 :            :  * has to be started.
     128                 :            :  *
     129                 :            :  * The PVECTOR_FOR_EACH_PRIORITY limits the iteration to entries with higher
     130                 :            :  * than or equal to the given priority and allows for object lookahead.
     131                 :            :  *
     132                 :            :  * The iteration loop must be completed without entering the OVS RCU quiescent
     133                 :            :  * period.  That is, an old iteration loop must not be continued after any
     134                 :            :  * blocking IO (VLOG is non-blocking, so that is OK).
     135                 :            :  */
     136                 :            : struct pvector_cursor {
     137                 :            :     size_t size;        /* Number of entries in the vector. */
     138                 :            :     size_t entry_idx;   /* Current index. */
     139                 :            :     const struct pvector_entry *vector;
     140                 :            : };
     141                 :            : 
     142                 :            : static inline struct pvector_cursor pvector_cursor_init(const struct pvector *,
     143                 :            :                                                         size_t n_ahead,
     144                 :            :                                                         size_t obj_size);
     145                 :            : static inline void *pvector_cursor_next(struct pvector_cursor *,
     146                 :            :                                         int lowest_priority,
     147                 :            :                                         size_t n_ahead, size_t obj_size);
     148                 :            : static inline void pvector_cursor_lookahead(const struct pvector_cursor *,
     149                 :            :                                             int n, size_t size);
     150                 :            : 
     151                 :            : #define PVECTOR_FOR_EACH(PTR, PVECTOR)                                  \
     152                 :            :     for (struct pvector_cursor cursor__ = pvector_cursor_init(PVECTOR, 0, 0); \
     153                 :            :          ((PTR) = pvector_cursor_next(&cursor__, INT_MIN, 0, 0)) != NULL; )
     154                 :            : 
     155                 :            : /* Loop while priority is higher than or equal to 'PRIORITY' and prefetch
     156                 :            :  * objects of size 'SZ' 'N' objects ahead from the current object. */
     157                 :            : #define PVECTOR_FOR_EACH_PRIORITY(PTR, PRIORITY, N, SZ, PVECTOR)        \
     158                 :            :     for (struct pvector_cursor cursor__ = pvector_cursor_init(PVECTOR, N, SZ); \
     159                 :            :          ((PTR) = pvector_cursor_next(&cursor__, PRIORITY, N, SZ)) != NULL; )
     160                 :            : 
     161                 :            : #define PVECTOR_CURSOR_FOR_EACH(PTR, CURSOR, PVECTOR)                \
     162                 :            :     for (*(CURSOR) = pvector_cursor_init(PVECTOR, 0, 0);             \
     163                 :            :          ((PTR) = pvector_cursor_next(CURSOR, INT_MIN, 0, 0)) != NULL; )
     164                 :            : 
     165                 :            : #define PVECTOR_CURSOR_FOR_EACH_CONTINUE(PTR, CURSOR)                   \
     166                 :            :     for (; ((PTR) = pvector_cursor_next(CURSOR, INT_MIN, 0, 0)) != NULL; )
     167                 :            : 
     168                 :            : 
     169                 :            : /* Inline implementations. */
     170                 :            : 
     171                 :            : static inline struct pvector_cursor
     172                 :   83771769 : pvector_cursor_init(const struct pvector *pvec,
     173                 :            :                     size_t n_ahead, size_t obj_size)
     174                 :            : {
     175                 :            :     const struct pvector_impl *impl;
     176                 :            :     struct pvector_cursor cursor;
     177                 :            : 
     178                 :   83771769 :     impl = ovsrcu_get(struct pvector_impl *, &pvec->impl);
     179                 :            : 
     180                 :   83771769 :     ovs_prefetch_range(impl->vector, impl->size * sizeof impl->vector[0]);
     181                 :            : 
     182                 :   83771771 :     cursor.size = impl->size;
     183                 :   83771771 :     cursor.vector = impl->vector;
     184                 :   83771771 :     cursor.entry_idx = -1;
     185                 :            : 
     186         [ +  + ]:  249085951 :     for (size_t i = 0; i < n_ahead; i++) {
     187                 :            :         /* Prefetch the first objects. */
     188                 :  165314182 :         pvector_cursor_lookahead(&cursor, i, obj_size);
     189                 :            :     }
     190                 :   83771769 :     return cursor;
     191                 :            : }
     192                 :            : 
     193                 :  171132093 : static inline void *pvector_cursor_next(struct pvector_cursor *cursor,
     194                 :            :                                         int lowest_priority,
     195                 :            :                                         size_t n_ahead, size_t obj_size)
     196                 :            : {
     197 [ +  + ][ +  + ]:  171132093 :     if (++cursor->entry_idx < cursor->size &&
     198                 :   91338524 :         cursor->vector[cursor->entry_idx].priority >= lowest_priority) {
     199         [ +  + ]:   87366221 :         if (n_ahead) {
     200                 :   86784282 :             pvector_cursor_lookahead(cursor, n_ahead, obj_size);
     201                 :            :         }
     202                 :   87366221 :         return cursor->vector[cursor->entry_idx].ptr;
     203                 :            :     }
     204                 :   83765872 :     return NULL;
     205                 :            : }
     206                 :            : 
     207                 :  252098456 : static inline void pvector_cursor_lookahead(const struct pvector_cursor *cursor,
     208                 :            :                                             int n, size_t size)
     209                 :            : {
     210         [ +  + ]:  252098456 :     if (cursor->entry_idx + n < cursor->size) {
     211                 :   76633730 :         ovs_prefetch_range(cursor->vector[cursor->entry_idx + n].ptr, size);
     212                 :            :     }
     213                 :  252098456 : }
     214                 :            : 
     215                 :      50792 : static inline size_t pvector_count(const struct pvector *pvec)
     216                 :            : {
     217                 :      50792 :     return ovsrcu_get(struct pvector_impl *, &pvec->impl)->size;
     218                 :            : }
     219                 :            : 
     220                 :            : static inline bool pvector_is_empty(const struct pvector *pvec)
     221                 :            : {
     222                 :            :     return pvector_count(pvec) == 0;
     223                 :            : }
     224                 :            : 
     225                 :            : void pvector_publish__(struct pvector *);
     226                 :            : 
     227                 :            : /* Make the modified pvector available for iteration. */
     228                 :     884984 : static inline void pvector_publish(struct pvector *pvec)
     229                 :            : {
     230         [ +  + ]:     884984 :     if (pvec->temp) {
     231                 :     389567 :         pvector_publish__(pvec);
     232                 :            :     }
     233                 :     884984 : }
     234                 :            : 
     235                 :            : #endif /* pvector.h */

Generated by: LCOV version 1.12