1 /* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */
2 /*
3 * Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana
4 * University Research and Technology
5 * Corporation. All rights reserved.
6 * Copyright (c) 2004-2013 The University of Tennessee and The University
7 * of Tennessee Research Foundation. All rights
8 * reserved.
9 * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart,
10 * University of Stuttgart. All rights reserved.
11 * Copyright (c) 2004-2005 The Regents of the University of California.
12 * All rights reserved.
13 * Copyright (c) 2010 IBM Corporation. All rights reserved.
14 * Copyright (c) 2010-2015 Cisco Systems, Inc. All rights reserved.
15 * Copyright (c) 2014-2018 Los Alamos National Security, LLC. All rights
16 * reserved.
17 * $COPYRIGHT$
18 *
19 * Additional copyrights may follow
20 *
21 * $HEADER$
22 */
23
24 #ifndef OPAL_FREE_LIST_H
25 #define OPAL_FREE_LIST_H
26
27 #include "opal_config.h"
28 #include "opal/class/opal_lifo.h"
29 #include "opal/prefetch.h"
30 #include "opal/threads/condition.h"
31 #include "opal/constants.h"
32 #include "opal/runtime/opal.h"
33
34 BEGIN_C_DECLS
35
36 struct mca_mem_pool_t;
37 struct opal_free_list_item_t;
38
39 /**
40 * Free list item initializtion function.
41 *
42 * @param item (IN) Free list item to initialize
43 * @param ctx (IN) Free list initialization context
44 *
45 * @returns OPAL_SUCCESS on success
46 * @returns opal error code on failure
47 *
48 * This function attempts to initialize the free list item
49 * specified in item. If the item can be initialized the
50 * function should return OPAL_SUCCESS. On any error
51 * opal_free_list_grow will stop initializing new items.
52 */
53 typedef int (*opal_free_list_item_init_fn_t) (
54 struct opal_free_list_item_t *item, void *ctx);
55
56 struct opal_free_list_t {
57 /** Items in a free list are stored last-in first-out */
58 opal_lifo_t super;
59 /** Maximum number of items to allocate in the free list */
60 size_t fl_max_to_alloc;
61 /** Current number of items allocated */
62 size_t fl_num_allocated;
63 /** Number of items to allocate when growing the free list */
64 size_t fl_num_per_alloc;
65 /** Number of threads waiting on free list item availability */
66 size_t fl_num_waiting;
67 /** Size of each free list item */
68 size_t fl_frag_size;
69 /** Free list item alignment */
70 size_t fl_frag_alignment;
71 /** Free list item buffer size */
72 size_t fl_payload_buffer_size;
73 /** Free list item buffer alignment */
74 size_t fl_payload_buffer_alignment;
75 /** Class of free list items */
76 opal_class_t *fl_frag_class;
77 /** mpool to use for free list buffer allocation (posix_memalign/malloc
78 * are used if this is NULL) */
79 struct mca_mpool_base_module_t *fl_mpool;
80 /** registration cache */
81 struct mca_rcache_base_module_t *fl_rcache;
82 /** Multi-threaded lock. Used when the free list is empty. */
83 opal_mutex_t fl_lock;
84 /** Multi-threaded condition. Used when threads are waiting on free
85 * list item availability. */
86 opal_condition_t fl_condition;
87 /** List of free list allocation */
88 opal_list_t fl_allocations;
89 /** Flags to pass to the rcache register function */
90 int fl_rcache_reg_flags;
91 /** Free list item initialization function */
92 opal_free_list_item_init_fn_t item_init;
93 /** Initialization function context */
94 void *ctx;
95 };
96 typedef struct opal_free_list_t opal_free_list_t;
97 OPAL_DECLSPEC OBJ_CLASS_DECLARATION(opal_free_list_t);
98
99 struct mca_mpool_base_registration_t;
100 struct opal_free_list_item_t
101 {
102 opal_list_item_t super;
103 struct mca_rcache_base_registration_t *registration;
104 void *ptr;
105 };
106 typedef struct opal_free_list_item_t opal_free_list_item_t;
107 OPAL_DECLSPEC OBJ_CLASS_DECLARATION(opal_free_list_item_t);
108
109
110 /**
111 * Initialize a free list.
112 *
113 * @param free_list (IN) Free list.
114 * @param frag_size (IN) Size of each element - allocated by malloc.
115 * @param frag_alignment (IN) Fragment alignment.
116 * @param frag_class (IN) opal_class_t of element - used to initialize allocated elements.
117 * @param payload_buffer_size (IN) Size of payload buffer - allocated from mpool.
118 * @param payload_buffer_alignment (IN) Payload buffer alignment.
119 * @param num_elements_to_alloc (IN) Initial number of elements to allocate.
120 * @param max_elements_to_alloc (IN) Maximum number of elements to allocate.
121 * @param num_elements_per_alloc (IN) Number of elements to grow by per allocation.
122 * @param mpool (IN) Optional memory pool for allocations.
123 * @param rcache_reg_flags (IN) Flags to pass to rcache registration function.
124 * @param rcache (IN) Optional registration cache.
125 * @param item_init (IN) Optional item initialization function
126 * @param ctx (IN) Initialization function context.
127 */
128
129 OPAL_DECLSPEC int opal_free_list_init (opal_free_list_t *free_list,
130 size_t frag_size,
131 size_t frag_alignment,
132 opal_class_t* frag_class,
133 size_t payload_buffer_size,
134 size_t payload_buffer_alignment,
135 int num_elements_to_alloc,
136 int max_elements_to_alloc,
137 int num_elements_per_alloc,
138 struct mca_mpool_base_module_t *mpool,
139 int rcache_reg_flags,
140 struct mca_rcache_base_module_t *rcache,
141 opal_free_list_item_init_fn_t item_init,
142 void *ctx);
143
144 /**
145 * Grow the free list by at most num_elements elements.
146 *
147 * @param flist (IN) Free list to grow
148 * @param num_elements (IN) Number of elements to add
149 * @param item_out (OUT) Location to store new free list item (can be NULL)
150 *
151 * @returns OPAL_SUCCESS if any elements were added
152 * @returns OPAL_ERR_OUT_OF_RESOURCE if no elements could be added
153 *
154 * This function will attempt to grow the free list by num_elements items. The
155 * caller must hold the free list lock if calling this function on a free list
156 * that may be accessed by multiple threads simultaneously. Note: this is an
157 * internal function that will be used when needed by opal_free_list_get* and
158 * opal_free_list_wait*.
159 *
160 * The item_out parameter can be used to ensure that the thread calling this
161 * function always gets a free list item if the list is successfully grown.
162 * This eliminates a race condition with code that simply calls free_list_get
163 * and assumes NULL is an out of memory condition (which it wasn't necessarily
164 * before this parameter was added).
165 */
166 OPAL_DECLSPEC int opal_free_list_grow_st (opal_free_list_t *flist, size_t num_elements, opal_free_list_item_t **item_out);
167
168 /**
169 * Grow the free list to be at least size elements.
170 *
171 * @param flist (IN) Free list to resize.
172 * @param size (IN) New size
173 *
174 * @returns OPAL_SUCCESS if the free list was resized
175 * @returns OPAL_ERR_OUT_OF_RESOURCE if resources could not be allocated
176 *
177 * This function will not shrink the list if it is already larger than size
178 * and may grow it past size if necessary (it will grow in num_elements_per_alloc
179 * chunks). This function is thread-safe and will obtain the free list lock before
180 * growing the free list.
181 */
182 OPAL_DECLSPEC int opal_free_list_resize_mt (opal_free_list_t *flist, size_t size);
183
184
185 /**
186 * Attemp to obtain an item from a free list.
187 *
188 * @param fl (IN) Free list.
189 * @param item (OUT) Allocated item.
190 *
191 * If the requested item is not available the free list is grown to
192 * accomodate the request - unless the max number of allocations has
193 * been reached. If this is the case - a NULL pointer is returned
194 * to the caller. This function comes in three flavor: thread safe
195 * (opal_free_list_get_mt), single threaded (opal_free_list_get_st),
196 * and opal_using_threads conditioned (opal_free_list_get).
197 */
opal_free_list_get_mt(opal_free_list_t * flist)198 static inline opal_free_list_item_t *opal_free_list_get_mt (opal_free_list_t *flist)
199 {
200 opal_free_list_item_t *item =
201 (opal_free_list_item_t*) opal_lifo_pop_atomic (&flist->super);
202
203 if (OPAL_UNLIKELY(NULL == item)) {
204 opal_mutex_lock (&flist->fl_lock);
205 opal_free_list_grow_st (flist, flist->fl_num_per_alloc, &item);
206 opal_mutex_unlock (&flist->fl_lock);
207 }
208
209 return item;
210 }
211
opal_free_list_get_st(opal_free_list_t * flist)212 static inline opal_free_list_item_t *opal_free_list_get_st (opal_free_list_t *flist)
213 {
214 opal_free_list_item_t *item =
215 (opal_free_list_item_t*) opal_lifo_pop_st (&flist->super);
216
217 if (OPAL_UNLIKELY(NULL == item)) {
218 opal_free_list_grow_st (flist, flist->fl_num_per_alloc, &item);
219 }
220
221 return item;
222 }
223
opal_free_list_get(opal_free_list_t * flist)224 static inline opal_free_list_item_t *opal_free_list_get (opal_free_list_t *flist)
225 {
226 if (opal_using_threads ()) {
227 return opal_free_list_get_mt (flist);
228 }
229
230 return opal_free_list_get_st (flist);
231 }
232
233 /** compatibility macro */
234 #define OPAL_FREE_LIST_GET(fl, item) \
235 (item) = opal_free_list_get (fl)
236
237 /**
238 * Blocking call to obtain an item from a free list.
239 *
240 * @param fl (IN) Free list.
241 * @param item (OUT) Allocated item.
242 *
243 * If the requested item is not available the free list is grown to
244 * accomodate the request - unless the max number of allocations has
245 * been reached. In this case the caller is blocked until an item
246 * is returned to the list.
247 */
248
249 /** compatibility macro */
250 #define OPAL_FREE_LIST_WAIT(fl, item) \
251 (item) = opal_free_list_wait (fl)
252
opal_free_list_wait_mt(opal_free_list_t * fl)253 static inline opal_free_list_item_t *opal_free_list_wait_mt (opal_free_list_t *fl)
254 {
255 opal_free_list_item_t *item =
256 (opal_free_list_item_t *) opal_lifo_pop_atomic (&fl->super);
257
258 while (NULL == item) {
259 if (!opal_mutex_trylock (&fl->fl_lock)) {
260 if (fl->fl_max_to_alloc <= fl->fl_num_allocated ||
261 OPAL_SUCCESS != opal_free_list_grow_st (fl, fl->fl_num_per_alloc, &item)) {
262 fl->fl_num_waiting++;
263 opal_condition_wait (&fl->fl_condition, &fl->fl_lock);
264 fl->fl_num_waiting--;
265 } else {
266 if (0 < fl->fl_num_waiting) {
267 if (1 == fl->fl_num_waiting) {
268 opal_condition_signal (&fl->fl_condition);
269 } else {
270 opal_condition_broadcast (&fl->fl_condition);
271 }
272 }
273 }
274 } else {
275 /* If I wasn't able to get the lock in the begining when I finaly grab it
276 * the one holding the lock in the begining already grow the list. I will
277 * release the lock and try to get a new element until I succeed.
278 */
279 opal_mutex_lock (&fl->fl_lock);
280 }
281 opal_mutex_unlock (&fl->fl_lock);
282 if (NULL == item) {
283 item = (opal_free_list_item_t *) opal_lifo_pop_atomic (&fl->super);
284 }
285 }
286
287 return item;
288 }
289
opal_free_list_wait_st(opal_free_list_t * fl)290 static inline opal_free_list_item_t *opal_free_list_wait_st (opal_free_list_t *fl)
291 {
292 opal_free_list_item_t *item =
293 (opal_free_list_item_t *) opal_lifo_pop (&fl->super);
294
295 while (NULL == item) {
296 if (fl->fl_max_to_alloc <= fl->fl_num_allocated ||
297 OPAL_SUCCESS != opal_free_list_grow_st (fl, fl->fl_num_per_alloc, &item)) {
298 /* try to make progress */
299 opal_progress ();
300 }
301 if (NULL == item) {
302 item = (opal_free_list_item_t *) opal_lifo_pop (&fl->super);
303 }
304 }
305
306 return item;
307 }
308
opal_free_list_wait(opal_free_list_t * fl)309 static inline opal_free_list_item_t *opal_free_list_wait (opal_free_list_t *fl)
310 {
311 if (opal_using_threads ()) {
312 return opal_free_list_wait_mt (fl);
313 } else {
314 return opal_free_list_wait_st (fl);
315 }
316 }
317
318 /**
319 * Return an item to a free list.
320 *
321 * @param fl (IN) Free list.
322 * @param item (OUT) Allocated item.
323 *
324 */
opal_free_list_return_mt(opal_free_list_t * flist,opal_free_list_item_t * item)325 static inline void opal_free_list_return_mt (opal_free_list_t *flist,
326 opal_free_list_item_t *item)
327 {
328 opal_list_item_t* original;
329
330 original = opal_lifo_push_atomic (&flist->super, &item->super);
331 if (&flist->super.opal_lifo_ghost == original) {
332 if (flist->fl_num_waiting > 0) {
333 /* one one item is being returned so it doesn't make sense to wake
334 * more than a single waiting thread. additionally, posix thread
335 * semantics do not require that the lock be held to signal the
336 * condition variable. */
337 opal_condition_signal (&flist->fl_condition);
338 }
339 }
340 }
341
opal_free_list_return_st(opal_free_list_t * flist,opal_free_list_item_t * item)342 static inline void opal_free_list_return_st (opal_free_list_t *flist,
343 opal_free_list_item_t *item)
344 {
345 opal_list_item_t* original;
346
347 original = opal_lifo_push_st (&flist->super, &item->super);
348 if (&flist->super.opal_lifo_ghost == original) {
349 if (flist->fl_num_waiting > 0) {
350 /* one one item is being returned so it doesn't make sense to wake
351 * more than a single waiting thread. additionally, posix thread
352 * semantics do not require that the lock be held to signal the
353 * condition variable. */
354 opal_condition_signal (&flist->fl_condition);
355 }
356 }
357 }
358
opal_free_list_return(opal_free_list_t * flist,opal_free_list_item_t * item)359 static inline void opal_free_list_return (opal_free_list_t *flist,
360 opal_free_list_item_t *item)
361 {
362 if (opal_using_threads ()) {
363 opal_free_list_return_mt (flist, item);
364 } else {
365 opal_free_list_return_st (flist, item);
366 }
367 }
368
369 /** compatibility macro */
370 #define OPAL_FREE_LIST_RETURN(fl, item) \
371 opal_free_list_return (fl, item)
372
373 END_C_DECLS
374 #endif
375
376