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