1 /**
2 * Copyright (C) Mellanox Technologies Ltd. 2001-2013. ALL RIGHTS RESERVED.
3 * Copyright (C) Huawei Technologies Co., Ltd. 2020. ALL RIGHTS RESERVED.
4 *
5 * See file LICENSE for terms.
6 */
7
8 #ifdef HAVE_CONFIG_H
9 # include "config.h"
10 #endif
11
12 #include "ptr_array.h"
13
14 #include <ucs/sys/string.h>
15 #include <ucs/sys/sys.h>
16 #include <ucs/debug/assert.h>
17 #include <ucs/debug/log.h>
18
19
20 /* Initial allocation size */
21 #define UCS_PTR_ARRAY_INITIAL_SIZE 8
22
23
24 static UCS_F_ALWAYS_INLINE int
ucs_ptr_array_is_free(ucs_ptr_array_t * ptr_array,unsigned element_index)25 ucs_ptr_array_is_free(ucs_ptr_array_t *ptr_array, unsigned element_index)
26 {
27 return (element_index < ptr_array->size) &&
28 __ucs_ptr_array_is_free(ptr_array->start[element_index]);
29 }
30
31 static UCS_F_ALWAYS_INLINE uint32_t
ucs_ptr_array_size_free_get_free_ahead(ucs_ptr_array_elem_t elem)32 ucs_ptr_array_size_free_get_free_ahead(ucs_ptr_array_elem_t elem)
33 {
34 ucs_assert(__ucs_ptr_array_is_free(elem));
35 return elem >> UCS_PTR_ARRAY_FREE_AHEAD_SHIFT;
36 }
37
38 static UCS_F_ALWAYS_INLINE unsigned
ucs_ptr_array_freelist_get_next(ucs_ptr_array_elem_t elem)39 ucs_ptr_array_freelist_get_next(ucs_ptr_array_elem_t elem)
40 {
41 ucs_assert(__ucs_ptr_array_is_free(elem));
42 return (elem & UCS_PTR_ARRAY_NEXT_MASK) >> UCS_PTR_ARRAY_NEXT_SHIFT;
43 }
44
45 static UCS_F_ALWAYS_INLINE void
ucs_ptr_array_freelist_set_next(ucs_ptr_array_elem_t * elem,unsigned next)46 ucs_ptr_array_freelist_set_next(ucs_ptr_array_elem_t *elem, unsigned next)
47 {
48 ucs_assert(next <= UCS_PTR_ARRAY_NEXT_MASK);
49 *elem = (*elem & ~UCS_PTR_ARRAY_NEXT_MASK) |
50 (((ucs_ptr_array_elem_t)next) << UCS_PTR_ARRAY_NEXT_SHIFT);
51 }
52
53 /**
54 * Sets all values of a free ptr array element
55 *
56 * @param [in/out] elem Pointer to a free element in the ptr array.
57 * @param [in] free_ahead Number of free consecutive elements ahead.
58 * @param [in] next Pointer to the next element in the ptr array.
59 *
60 * Complexity: O(1)
61 */
62 static UCS_F_ALWAYS_INLINE void
ucs_ptr_array_freelist_element_set(ucs_ptr_array_elem_t * elem,uint32_t free_ahead,unsigned next)63 ucs_ptr_array_freelist_element_set(ucs_ptr_array_elem_t *elem,
64 uint32_t free_ahead,
65 unsigned next)
66 {
67 ucs_assert(next <= UCS_PTR_ARRAY_NEXT_MASK);
68
69 *elem = UCS_PTR_ARRAY_FLAG_FREE |
70 (((ucs_ptr_array_elem_t)free_ahead) << UCS_PTR_ARRAY_FREE_AHEAD_SHIFT) |
71 (((ucs_ptr_array_elem_t)next) << UCS_PTR_ARRAY_NEXT_SHIFT);
72 }
73
74 static UCS_F_ALWAYS_INLINE void
ucs_ptr_array_freelist_element_set_free_ahead(ucs_ptr_array_elem_t * elem,uint32_t free_ahead)75 ucs_ptr_array_freelist_element_set_free_ahead(ucs_ptr_array_elem_t *elem,
76 uint32_t free_ahead)
77 {
78 ucs_ptr_array_freelist_element_set(elem, free_ahead,
79 ucs_ptr_array_freelist_get_next(*elem));
80 }
81
ucs_ptr_array_dump(ucs_ptr_array_t * ptr_array)82 static void UCS_F_MAYBE_UNUSED ucs_ptr_array_dump(ucs_ptr_array_t *ptr_array)
83 {
84 #if UCS_ENABLE_ASSERT
85 unsigned i;
86
87 ucs_trace_data("ptr_array start %p size %u",
88 ptr_array->start, ptr_array->size);
89 for (i = 0; i < ptr_array->size; ++i) {
90 if (ucs_ptr_array_is_free(ptr_array, i)) {
91 ucs_trace_data("(%u) [%u]=<free> [%u]=<next>", i,
92 ucs_ptr_array_size_free_get_free_ahead(ptr_array->start[i]),
93 ucs_ptr_array_freelist_get_next(ptr_array->start[i]));
94 } else {
95 ucs_trace_data("[%u]=%p", i, (void*)ptr_array->start[i]);
96 }
97 }
98
99 ucs_trace_data("freelist:");
100 i = ptr_array->freelist;
101 while (i != UCS_PTR_ARRAY_SENTINEL) {
102 ucs_trace_data("[%u] %p", i, &ptr_array->start[i]);
103 i = ucs_ptr_array_freelist_get_next(ptr_array->start[i]);
104 }
105 #endif
106 }
107
ucs_ptr_array_clear(ucs_ptr_array_t * ptr_array)108 static void ucs_ptr_array_clear(ucs_ptr_array_t *ptr_array)
109 {
110 ptr_array->start = NULL;
111 ptr_array->size = 0;
112 ptr_array->freelist = UCS_PTR_ARRAY_SENTINEL;
113 }
114
ucs_ptr_array_init(ucs_ptr_array_t * ptr_array,const char * name)115 void ucs_ptr_array_init(ucs_ptr_array_t *ptr_array, const char *name)
116 {
117 ucs_ptr_array_clear(ptr_array);
118 #ifdef ENABLE_MEMTRACK
119 ucs_snprintf_zero(ptr_array->name, sizeof(ptr_array->name), "%s", name);
120 #endif
121 }
122
ucs_ptr_array_cleanup(ucs_ptr_array_t * ptr_array)123 void ucs_ptr_array_cleanup(ucs_ptr_array_t *ptr_array)
124 {
125 unsigned i, inuse;
126
127 inuse = 0;
128 for (i = 0; i < ptr_array->size; ++i) {
129 if (!ucs_ptr_array_is_free(ptr_array, i)) {
130 ++inuse;
131 ucs_trace("ptr_array(%p) idx %d is not free during cleanup",
132 ptr_array, i);
133 }
134 }
135
136 if (inuse > 0) {
137 ucs_warn("releasing ptr_array with %u used items", inuse);
138 }
139
140 ucs_free(ptr_array->start);
141 ucs_ptr_array_clear(ptr_array);
142 }
143
ucs_ptr_array_grow(ucs_ptr_array_t * ptr_array,unsigned new_size UCS_MEMTRACK_ARG)144 static void ucs_ptr_array_grow(ucs_ptr_array_t *ptr_array, unsigned new_size
145 UCS_MEMTRACK_ARG)
146 {
147 ucs_ptr_array_elem_t *new_array;
148 unsigned curr_size, i, next;
149
150 /* Allocate new array */
151 new_array = ucs_malloc(new_size * sizeof(ucs_ptr_array_elem_t) UCS_MEMTRACK_VAL);
152 ucs_assert_always(new_array != NULL);
153 curr_size = ptr_array->size;
154 memcpy(new_array, ptr_array->start, curr_size * sizeof(ucs_ptr_array_elem_t));
155
156 /* Link all new array items */
157 for (i = curr_size; i < new_size; ++i) {
158 ucs_ptr_array_freelist_element_set(&new_array[i], new_size - i,
159 i + 1);
160 }
161 ucs_ptr_array_freelist_set_next(&new_array[new_size - 1], UCS_PTR_ARRAY_SENTINEL);
162
163 /* Find last free list element */
164 if (ptr_array->freelist == UCS_PTR_ARRAY_SENTINEL) {
165 ptr_array->freelist = curr_size;
166 } else {
167 next = ptr_array->freelist;
168 do {
169 i = next;
170 next = ucs_ptr_array_freelist_get_next(new_array[i]);
171 } while (next != UCS_PTR_ARRAY_SENTINEL);
172 ucs_ptr_array_freelist_set_next(&new_array[i], curr_size);
173 }
174
175 /* Switch to new array */
176 ucs_free(ptr_array->start);
177 ptr_array->start = new_array;
178 ptr_array->size = new_size;
179 }
180
ucs_ptr_array_insert(ucs_ptr_array_t * ptr_array,void * value)181 unsigned ucs_ptr_array_insert(ucs_ptr_array_t *ptr_array, void *value)
182 {
183 ucs_ptr_array_elem_t *elem;
184 unsigned element_index, new_size;
185
186 ucs_assert_always(((uintptr_t)value & UCS_PTR_ARRAY_FLAG_FREE) == 0);
187
188 if (ptr_array->freelist == UCS_PTR_ARRAY_SENTINEL) {
189 new_size = ucs_max(UCS_PTR_ARRAY_INITIAL_SIZE, ptr_array->size * 2);
190 ucs_ptr_array_grow(ptr_array, new_size UCS_MEMTRACK_NAME(ptr_array->name));
191 }
192
193 /* Get the first item on the free list */
194 element_index = ptr_array->freelist;
195 ucs_assert(element_index != UCS_PTR_ARRAY_SENTINEL);
196
197 elem = &ptr_array->start[element_index];
198
199 /* Remove from free list and populate */
200 ptr_array->freelist = ucs_ptr_array_freelist_get_next(*elem);
201 *elem = (uintptr_t)value;
202
203 return element_index;
204 }
205
ucs_ptr_array_set(ucs_ptr_array_t * ptr_array,unsigned element_index,void * new_val)206 void ucs_ptr_array_set(ucs_ptr_array_t *ptr_array, unsigned element_index,
207 void *new_val)
208 {
209 ucs_ptr_array_elem_t *elem;
210 unsigned next, free_iter, free_ahead, new_size;
211
212 if (ucs_unlikely(element_index > ptr_array->size)) {
213 new_size = ucs_max(ptr_array->size * 2, element_index + 1);
214 ucs_ptr_array_grow(ptr_array, new_size UCS_MEMTRACK_NAME(ptr_array->name));
215 } else if (!__ucs_ptr_array_is_free(ptr_array->start[element_index])) {
216 ptr_array->start[element_index] = (uintptr_t)new_val;
217 return;
218 }
219
220 next = ucs_ptr_array_freelist_get_next(ptr_array->start[element_index]);
221 ptr_array->start[element_index] = (uintptr_t)new_val;
222
223 /* update the "next index" in the free list (removing element_index from it) */
224 free_iter = ptr_array->freelist;
225 if (ucs_unlikely(free_iter == element_index)) {
226 ptr_array->freelist = next;
227 } else {
228 while (element_index !=
229 ucs_ptr_array_freelist_get_next(ptr_array->start[free_iter])) {
230 free_iter =
231 ucs_ptr_array_freelist_get_next(ptr_array->start[free_iter]);
232 ucs_assert(free_iter != UCS_PTR_ARRAY_SENTINEL);
233 }
234 ucs_ptr_array_freelist_set_next(ptr_array->start + free_iter, next);
235 }
236
237 /* update the "free-ahead" for the cells before me */
238 free_ahead = 1;
239 elem = ptr_array->start + element_index - 1;
240 while ((elem >= ptr_array->start) && (__ucs_ptr_array_is_free(*elem))) {
241 ucs_ptr_array_freelist_element_set_free_ahead(elem, free_ahead);
242 free_ahead++;
243 elem--;
244 }
245 }
246
ucs_ptr_array_remove(ucs_ptr_array_t * ptr_array,unsigned element_index)247 void ucs_ptr_array_remove(ucs_ptr_array_t *ptr_array, unsigned element_index)
248 {
249 ucs_ptr_array_elem_t *next_elem;
250 uint32_t size_free_ahead;
251
252 ucs_assert_always(!ucs_ptr_array_is_free(ptr_array, element_index));
253
254 if (ucs_ptr_array_is_free(ptr_array, element_index + 1)) {
255 next_elem = &ptr_array->start[element_index + 1];
256 size_free_ahead = ucs_ptr_array_size_free_get_free_ahead(*next_elem) + 1;
257 } else {
258 size_free_ahead = 1;
259 }
260
261 ucs_ptr_array_freelist_element_set(&ptr_array->start[element_index],
262 size_free_ahead, ptr_array->freelist);
263
264 /* Make sure the next element is free */
265 ucs_assert(__ucs_ptr_array_is_free(ptr_array->start[element_index + size_free_ahead - 1]));
266
267 ptr_array->freelist = element_index;
268 }
269
ucs_ptr_array_replace(ucs_ptr_array_t * ptr_array,unsigned element_index,void * new_val)270 void *ucs_ptr_array_replace(ucs_ptr_array_t *ptr_array, unsigned element_index,
271 void *new_val)
272 {
273 void *old_elem;
274
275 ucs_assert_always(!ucs_ptr_array_is_free(ptr_array, element_index));
276 old_elem = (void*)ptr_array->start[element_index];
277 ptr_array->start[element_index] = (uintptr_t)new_val;
278 return old_elem;
279 }
280
281
282 /*
283 * Locked interface functions implementation
284 */
285
286 ucs_status_t
ucs_ptr_array_locked_init(ucs_ptr_array_locked_t * locked_ptr_array,const char * name)287 ucs_ptr_array_locked_init(ucs_ptr_array_locked_t *locked_ptr_array,
288 const char *name)
289 {
290 ucs_status_t status;
291
292 /* Initialize spinlock */
293 status = ucs_recursive_spinlock_init(&locked_ptr_array->lock, 0);
294 if (status != UCS_OK) {
295 return status;
296 }
297
298 /* Call unlocked function */
299 ucs_ptr_array_init(&locked_ptr_array->super, name);
300
301 return UCS_OK;
302 }
303
ucs_ptr_array_locked_cleanup(ucs_ptr_array_locked_t * locked_ptr_array)304 void ucs_ptr_array_locked_cleanup(ucs_ptr_array_locked_t *locked_ptr_array)
305 {
306 ucs_status_t status;
307
308 ucs_recursive_spin_lock(&locked_ptr_array->lock);
309 /* Call unlocked function */
310 ucs_ptr_array_cleanup(&locked_ptr_array->super);
311 ucs_recursive_spin_unlock(&locked_ptr_array->lock);
312
313 /* Destroy spinlock */
314 status = ucs_recursive_spinlock_destroy(&locked_ptr_array->lock);
315 if (status != UCS_OK) {
316 ucs_warn("ucs_recursive_spinlock_destroy() - failed (%d)", status);
317 }
318 }
319
ucs_ptr_array_locked_insert(ucs_ptr_array_locked_t * locked_ptr_array,void * value)320 unsigned ucs_ptr_array_locked_insert(ucs_ptr_array_locked_t *locked_ptr_array,
321 void *value)
322 {
323 unsigned element_index;
324
325 ucs_recursive_spin_lock(&locked_ptr_array->lock);
326 /* Call unlocked function */
327 element_index = ucs_ptr_array_insert(&locked_ptr_array->super, value);
328 ucs_recursive_spin_unlock(&locked_ptr_array->lock);
329
330 return element_index;
331 }
332
ucs_ptr_array_locked_set(ucs_ptr_array_locked_t * locked_ptr_array,unsigned element_index,void * new_val)333 void ucs_ptr_array_locked_set(ucs_ptr_array_locked_t *locked_ptr_array,
334 unsigned element_index, void *new_val)
335 {
336 ucs_recursive_spin_lock(&locked_ptr_array->lock);
337 /* Call unlocked function */
338 ucs_ptr_array_set(&locked_ptr_array->super, element_index, new_val);
339 ucs_recursive_spin_unlock(&locked_ptr_array->lock);
340 }
341
ucs_ptr_array_locked_remove(ucs_ptr_array_locked_t * locked_ptr_array,unsigned element_index)342 void ucs_ptr_array_locked_remove(ucs_ptr_array_locked_t *locked_ptr_array,
343 unsigned element_index)
344 {
345 ucs_recursive_spin_lock(&locked_ptr_array->lock);
346 /* Call unlocked function */
347 ucs_ptr_array_remove(&locked_ptr_array->super, element_index);
348 ucs_recursive_spin_unlock(&locked_ptr_array->lock);
349 }
350
ucs_ptr_array_locked_replace(ucs_ptr_array_locked_t * locked_ptr_array,unsigned element_index,void * new_val)351 void *ucs_ptr_array_locked_replace(ucs_ptr_array_locked_t *locked_ptr_array,
352 unsigned element_index, void *new_val)
353 {
354 void *old_elem;
355
356 ucs_recursive_spin_lock(&locked_ptr_array->lock);
357 /* Call unlocked function */
358 old_elem = ucs_ptr_array_replace(&locked_ptr_array->super, element_index,
359 new_val);
360 ucs_recursive_spin_unlock(&locked_ptr_array->lock);
361
362 return old_elem;
363 }
364
365