/src/openvswitch/lib/pvector.c
Line | Count | Source (jump to first uncovered line) |
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 | | #include <config.h> |
18 | | #include "pvector.h" |
19 | | |
20 | | /* Writers will preallocate space for some entries at the end to avoid future |
21 | | * reallocations. */ |
22 | | enum { PVECTOR_EXTRA_ALLOC = 4 }; |
23 | | |
24 | | static struct pvector_impl * |
25 | | pvector_impl_get(const struct pvector *pvec) |
26 | 0 | { |
27 | 0 | return ovsrcu_get(struct pvector_impl *, &pvec->impl); |
28 | 0 | } |
29 | | |
30 | | static struct pvector_impl * |
31 | | pvector_impl_alloc(size_t size) |
32 | 0 | { |
33 | 0 | struct pvector_impl *impl; |
34 | |
|
35 | 0 | impl = xmalloc(sizeof *impl + size * sizeof impl->vector[0]); |
36 | 0 | atomic_init(&impl->size, 0); |
37 | 0 | impl->allocated = size; |
38 | |
|
39 | 0 | return impl; |
40 | 0 | } |
41 | | |
42 | | static struct pvector_impl * |
43 | | pvector_impl_dup(struct pvector_impl *old) |
44 | 0 | { |
45 | 0 | struct pvector_impl *impl; |
46 | 0 | size_t alloc = old->size + PVECTOR_EXTRA_ALLOC; |
47 | |
|
48 | 0 | impl = xmalloc(sizeof *impl + alloc * sizeof impl->vector[0]); |
49 | 0 | impl->allocated = alloc; |
50 | 0 | impl->size = old->size; |
51 | 0 | memcpy(impl->vector, old->vector, old->size * sizeof old->vector[0]); |
52 | 0 | return impl; |
53 | 0 | } |
54 | | |
55 | | /* Initializes 'pvec' as an empty concurrent priority vector. */ |
56 | | void |
57 | | pvector_init(struct pvector *pvec) |
58 | 0 | { |
59 | 0 | ovsrcu_set(&pvec->impl, pvector_impl_alloc(PVECTOR_EXTRA_ALLOC)); |
60 | 0 | pvec->temp = NULL; |
61 | 0 | } |
62 | | |
63 | | /* Destroys 'pvec'. |
64 | | * |
65 | | * The client is responsible for destroying any data previously held in |
66 | | * 'pvec'. */ |
67 | | void |
68 | | pvector_destroy(struct pvector *pvec) |
69 | 0 | { |
70 | 0 | free(pvec->temp); |
71 | 0 | pvec->temp = NULL; |
72 | 0 | ovsrcu_postpone(free, pvector_impl_get(pvec)); |
73 | 0 | ovsrcu_set(&pvec->impl, NULL); /* Poison. */ |
74 | 0 | } |
75 | | |
76 | | /* Iterators for callers that need the 'index' afterward. */ |
77 | | #define PVECTOR_IMPL_FOR_EACH(ENTRY, INDEX, IMPL) \ |
78 | 0 | for ((INDEX) = 0; \ |
79 | 0 | (INDEX) < (IMPL)->size \ |
80 | 0 | && ((ENTRY) = &(IMPL)->vector[INDEX], true); \ |
81 | 0 | (INDEX)++) |
82 | | |
83 | | static int |
84 | | pvector_entry_cmp(const void *a_, const void *b_) |
85 | 0 | { |
86 | 0 | const struct pvector_entry *ap = a_; |
87 | 0 | const struct pvector_entry *bp = b_; |
88 | 0 | int a = ap->priority; |
89 | 0 | int b = bp->priority; |
90 | |
|
91 | 0 | return a > b ? -1 : a < b; |
92 | 0 | } |
93 | | |
94 | | static void |
95 | | pvector_impl_sort(struct pvector_impl *impl) |
96 | 0 | { |
97 | 0 | qsort(impl->vector, impl->size, sizeof *impl->vector, pvector_entry_cmp); |
98 | 0 | } |
99 | | |
100 | | /* Returns the index of the 'ptr' in the vector, or -1 if none is found. */ |
101 | | static int |
102 | | pvector_impl_find(struct pvector_impl *impl, void *target) |
103 | 0 | { |
104 | 0 | const struct pvector_entry *entry; |
105 | 0 | int index; |
106 | |
|
107 | 0 | PVECTOR_IMPL_FOR_EACH (entry, index, impl) { |
108 | 0 | if (entry->ptr == target) { |
109 | 0 | return index; |
110 | 0 | } |
111 | 0 | } |
112 | 0 | return -1; |
113 | 0 | } |
114 | | |
115 | | void |
116 | | pvector_insert(struct pvector *pvec, void *ptr, int priority) |
117 | 0 | { |
118 | 0 | struct pvector_impl *temp = pvec->temp; |
119 | 0 | struct pvector_impl *old = pvector_impl_get(pvec); |
120 | 0 | size_t size; |
121 | |
|
122 | 0 | ovs_assert(ptr != NULL); |
123 | | |
124 | | /* There is no possible concurrent writer. Insertions must be protected |
125 | | * by mutex or be always excuted from the same thread. */ |
126 | 0 | atomic_read_relaxed(&old->size, &size); |
127 | | |
128 | | /* Check if can add to the end without reallocation. */ |
129 | 0 | if (!temp && old->allocated > size && |
130 | 0 | (!size || priority <= old->vector[size - 1].priority)) { |
131 | 0 | old->vector[size].ptr = ptr; |
132 | 0 | old->vector[size].priority = priority; |
133 | | /* Size increment must not be visible to the readers before the new |
134 | | * entry is stored. */ |
135 | 0 | atomic_store_explicit(&old->size, size + 1, memory_order_release); |
136 | 0 | } else { |
137 | 0 | if (!temp) { |
138 | 0 | temp = pvector_impl_dup(old); |
139 | 0 | pvec->temp = temp; |
140 | 0 | } else if (temp->size == temp->allocated) { |
141 | 0 | temp = pvector_impl_dup(temp); |
142 | 0 | free(pvec->temp); |
143 | 0 | pvec->temp = temp; |
144 | 0 | } |
145 | | /* Insert at the end, publish will sort. */ |
146 | 0 | temp->vector[temp->size].ptr = ptr; |
147 | 0 | temp->vector[temp->size].priority = priority; |
148 | 0 | temp->size += 1; |
149 | 0 | } |
150 | 0 | } |
151 | | |
152 | | void |
153 | | pvector_remove(struct pvector *pvec, void *ptr) |
154 | 0 | { |
155 | 0 | struct pvector_impl *temp = pvec->temp; |
156 | 0 | int index; |
157 | |
|
158 | 0 | if (!temp) { |
159 | 0 | temp = pvector_impl_dup(pvector_impl_get(pvec)); |
160 | 0 | pvec->temp = temp; |
161 | 0 | } |
162 | 0 | ovs_assert(temp->size > 0); |
163 | 0 | index = pvector_impl_find(temp, ptr); |
164 | 0 | ovs_assert(index >= 0); |
165 | | /* Now at the index of the entry to be deleted. |
166 | | * Swap another entry in if needed, publish will sort anyway. */ |
167 | 0 | temp->size--; |
168 | 0 | if (index != temp->size) { |
169 | 0 | temp->vector[index] = temp->vector[temp->size]; |
170 | 0 | } |
171 | 0 | } |
172 | | |
173 | | /* Change entry's 'priority' and keep the vector ordered. */ |
174 | | void |
175 | | pvector_change_priority(struct pvector *pvec, void *ptr, int priority) |
176 | 0 | { |
177 | 0 | struct pvector_impl *old = pvec->temp; |
178 | 0 | int index; |
179 | |
|
180 | 0 | if (!old) { |
181 | 0 | old = pvector_impl_get(pvec); |
182 | 0 | } |
183 | |
|
184 | 0 | index = pvector_impl_find(old, ptr); |
185 | |
|
186 | 0 | ovs_assert(index >= 0); |
187 | | /* Now at the index of the entry to be updated. */ |
188 | | |
189 | | /* Check if can not update in place. */ |
190 | 0 | if ((priority > old->vector[index].priority && index > 0 |
191 | 0 | && priority > old->vector[index - 1].priority) |
192 | 0 | || (priority < old->vector[index].priority && index < old->size - 1 |
193 | 0 | && priority < old->vector[index + 1].priority)) { |
194 | | /* Have to use a temp. */ |
195 | 0 | if (!pvec->temp) { |
196 | | /* Have to reallocate to reorder. */ |
197 | 0 | pvec->temp = pvector_impl_dup(old); |
198 | 0 | old = pvec->temp; |
199 | | /* Publish will sort. */ |
200 | 0 | } |
201 | 0 | } |
202 | 0 | old->vector[index].priority = priority; |
203 | 0 | } |
204 | | |
205 | | /* Make the modified pvector available for iteration. */ |
206 | | void pvector_publish__(struct pvector *pvec) |
207 | 0 | { |
208 | 0 | struct pvector_impl *temp = pvec->temp; |
209 | |
|
210 | 0 | pvec->temp = NULL; |
211 | 0 | pvector_impl_sort(temp); /* Also removes gaps. */ |
212 | 0 | ovsrcu_postpone(free, ovsrcu_get_protected(struct pvector_impl *, |
213 | 0 | &pvec->impl)); |
214 | 0 | ovsrcu_set(&pvec->impl, temp); |
215 | 0 | } |