ARGOBOTS
abti_mem.h
Go to the documentation of this file.
1 /* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil ; -*- */
2 /*
3  * See COPYRIGHT in top-level directory.
4  */
5 
6 #ifndef ABTI_MEM_H_INCLUDED
7 #define ABTI_MEM_H_INCLUDED
8 
9 /* Memory allocation */
10 
11 /* Header size should be a multiple of cache line size. It is constant. */
12 #define ABTI_MEM_SH_SIZE \
13  (((sizeof(ABTI_thread) + sizeof(ABTI_stack_header) + \
14  ABT_CONFIG_STATIC_CACHELINE_SIZE - 1) / \
15  ABT_CONFIG_STATIC_CACHELINE_SIZE) * \
16  ABT_CONFIG_STATIC_CACHELINE_SIZE)
17 
18 #ifdef ABT_CONFIG_USE_MEM_POOL
19 typedef struct ABTI_blk_header ABTI_blk_header;
20 
21 enum {
22  ABTI_MEM_LP_MALLOC = 0,
23  ABTI_MEM_LP_MMAP_RP,
24  ABTI_MEM_LP_MMAP_HP_RP,
25  ABTI_MEM_LP_MMAP_HP_THP,
26  ABTI_MEM_LP_THP
27 };
28 
29 struct ABTI_sp_header {
30  uint32_t num_total_stacks; /* Number of total stacks */
31  ABTD_atomic_uint32 num_empty_stacks; /* Number of empty stacks */
32  size_t stacksize; /* Stack size */
33  uint64_t id; /* ID */
34  ABT_bool is_mmapped; /* ABT_TRUE if it is mmapped */
35  void *p_sp; /* Pointer to the allocated stack page */
36  ABTI_sp_header *p_next; /* Next stack page header */
37 };
38 
39 struct ABTI_stack_header {
40  ABTI_stack_header *p_next;
41  ABTI_sp_header *p_sph;
42  void *p_stack;
43 };
44 
45 struct ABTI_page_header {
46  uint32_t blk_size; /* Block size in bytes */
47  uint32_t num_total_blks; /* Number of total blocks */
48  uint32_t num_empty_blks; /* Number of empty blocks */
49  ABTD_atomic_uint32 num_remote_free; /* Number of remote free blocks */
50  ABTI_blk_header *p_head; /* First empty block */
51  ABTI_blk_header *p_free; /* For remote free */
52  ABTI_native_thread_id owner_id; /* Owner's ID */
53  ABTI_page_header *p_prev; /* Prev page header */
54  ABTI_page_header *p_next; /* Next page header */
55  ABT_bool is_mmapped; /* ABT_TRUE if it is mmapped */
56 };
57 
58 struct ABTI_blk_header {
59  ABTI_page_header *p_ph; /* Page header */
60  ABTI_blk_header *p_next; /* Next block header */
61 };
62 
63 void ABTI_mem_init(ABTI_global *p_global);
64 void ABTI_mem_init_local(ABTI_local *p_local);
65 void ABTI_mem_finalize(ABTI_global *p_global);
66 void ABTI_mem_finalize_local(ABTI_local *p_local);
67 int ABTI_mem_check_lp_alloc(int lp_alloc);
68 
69 char *ABTI_mem_take_global_stack(ABTI_local *p_local);
70 void ABTI_mem_add_stack_to_global(ABTI_stack_header *p_sh);
71 ABTI_page_header *ABTI_mem_alloc_page(ABTI_local *p_local, size_t blk_size);
72 void ABTI_mem_free_page(ABTI_local *p_local, ABTI_page_header *p_ph);
73 void ABTI_mem_take_free(ABTI_page_header *p_ph);
74 void ABTI_mem_free_remote(ABTI_page_header *p_ph, ABTI_blk_header *p_bh);
75 ABTI_page_header *ABTI_mem_take_global_page(ABTI_local *p_local);
76 
77 char *ABTI_mem_alloc_sp(ABTI_local *p_local, size_t stacksize);
78 
79 /******************************************************************************
80  * Unless the stack is given by the user, we allocate a stack first and then
81  * use the beginning of the allocated stack for allocating ABTI_thread and
82  * ABTI_stack_header. This way we need only one memory allocation call (e.g.,
83  * ABTU_malloc). The memory layout of the allocated stack will look like
84  * |-------------------|
85  * | ABTI_thread |
86  * |-------------------|
87  * | ABTI_stack_header |
88  * |-------------------|
89  * | actual stack area |
90  * |-------------------|
91  * Thus, the actual size of stack becomes
92  * (requested stack size) - sizeof(ABTI_thread) - sizeof(ABTI_stack_header)
93  * and it is set in the attribute field of ABTI_thread.
94  *****************************************************************************/
95 
96 /* Inline functions */
97 static inline ABTI_thread *
98 ABTI_mem_alloc_thread_with_stacksize(size_t stacksize, ABTI_thread_attr *p_attr)
99 {
100  size_t actual_stacksize;
101  char *p_blk;
102  ABTI_thread *p_thread;
103  void *p_stack;
104 
105  /* Get the stack size */
106  actual_stacksize = stacksize - sizeof(ABTI_thread);
107 
108  /* Allocate a stack */
109  p_blk = (char *)ABTU_malloc(stacksize);
110 
111  /* Allocate ABTI_thread, ABTI_stack_header, and the actual stack area in
112  * the allocated stack memory */
113  p_thread = (ABTI_thread *)p_blk;
114  p_stack = (void *)(p_blk + sizeof(ABTI_thread));
115 
116  /* Set attributes */
117  if (p_attr) {
118  /* Copy p_attr. */
119  ABTI_thread_attr_copy(&p_thread->attr, p_attr);
120  p_thread->attr.stacksize = actual_stacksize;
121  p_thread->attr.p_stack = p_stack;
122  p_thread->attr.stacktype = ABTI_STACK_TYPE_MALLOC;
123  } else {
124  /* Initialize p_attr. */
125  ABTI_thread_attr_init(&p_thread->attr, p_stack, actual_stacksize,
126  ABTI_STACK_TYPE_MALLOC, ABT_TRUE);
127  }
128 
129  ABTI_VALGRIND_REGISTER_STACK(p_thread->attr.p_stack, actual_stacksize);
130  return p_thread;
131 }
132 
133 static inline ABTI_thread *ABTI_mem_alloc_thread(ABTI_local *p_local,
134  ABTI_thread_attr *p_attr)
135 {
136  /* Basic idea: allocate a memory for stack and use the first some memory as
137  * ABTI_stack_header and ABTI_thread. So, the effective stack area is
138  * reduced as much as the size of ABTI_stack_header and ABTI_thread. */
139 
140  size_t stacksize, def_stacksize, actual_stacksize;
141  char *p_blk = NULL;
142  ABTI_thread *p_thread;
143  ABTI_stack_header *p_sh;
144  void *p_stack;
145 
146  /* Get the stack size */
147  def_stacksize = ABTI_global_get_thread_stacksize();
148  if (p_attr == NULL) {
149  stacksize = def_stacksize;
150 
151 #ifndef ABT_CONFIG_DISABLE_EXT_THREAD
152  /* If an external thread allocates a stack, we use ABTU_malloc. */
153  if (p_local == NULL) {
154  return ABTI_mem_alloc_thread_with_stacksize(stacksize, NULL);
155  }
156 #endif
157 
158  } else {
159  ABTI_stack_type stacktype = p_attr->stacktype;
160  if (stacktype == ABTI_STACK_TYPE_USER ||
161  stacktype == ABTI_STACK_TYPE_MAIN) {
162  /* Since the stack is given by the user, we create ABTI_thread and
163  * ABTI_stack_header explicitly with a single ABTU_malloc call. */
164  p_thread = (ABTI_thread *)ABTU_malloc(sizeof(ABTI_thread));
165  ABTI_thread_attr_copy(&p_thread->attr, p_attr);
166 
167  if (p_attr->stacktype != ABTI_STACK_TYPE_MAIN) {
168  ABTI_VALGRIND_REGISTER_STACK(p_thread->attr.p_stack,
169  p_attr->stacksize);
170  }
171  return p_thread;
172  }
173 
174  stacksize = p_attr->stacksize;
175  if (stacksize != def_stacksize || stacktype == ABTI_STACK_TYPE_MALLOC) {
176  /* Since the stack size requested is not the same as default one,
177  * we use ABTU_malloc. */
178  return ABTI_mem_alloc_thread_with_stacksize(stacksize, p_attr);
179  }
180  }
181 
182  /* Use the stack pool */
183  if (p_local->p_mem_stack) {
184  /* ES's stack pool has an available stack */
185  p_sh = p_local->p_mem_stack;
186  p_local->p_mem_stack = p_sh->p_next;
187  p_local->num_stacks--;
188 
189  p_sh->p_next = NULL;
190  p_blk = (char *)p_sh - sizeof(ABTI_thread);
191 
192  } else {
193  /* Check stacks in the global data */
194  if (gp_ABTI_global->p_mem_stack) {
195  p_blk = ABTI_mem_take_global_stack(p_local);
196  if (p_blk == NULL) {
197  p_blk = ABTI_mem_alloc_sp(p_local, stacksize);
198  }
199  } else {
200  /* Allocate a new stack if we don't have any empty stack */
201  p_blk = ABTI_mem_alloc_sp(p_local, stacksize);
202  }
203 
204  p_sh = (ABTI_stack_header *)(p_blk + sizeof(ABTI_thread));
205  p_sh->p_next = NULL;
206  }
207 
208  /* Actual stack size */
209  actual_stacksize = stacksize - ABTI_MEM_SH_SIZE;
210 
211  /* Get the ABTI_thread pointer and stack pointer */
212  p_thread = (ABTI_thread *)p_blk;
213  p_stack = p_sh->p_stack;
214 
215  /* Set attributes */
216  if (p_attr == NULL) {
217  ABTI_thread_attr *p_myattr = &p_thread->attr;
218  ABTI_thread_attr_init(p_myattr, p_stack, actual_stacksize,
219  ABTI_STACK_TYPE_MEMPOOL, ABT_TRUE);
220  } else {
221  ABTI_thread_attr_copy(&p_thread->attr, p_attr);
222  p_thread->attr.stacksize = actual_stacksize;
223  p_thread->attr.p_stack = p_stack;
224  }
225 
226  ABTI_VALGRIND_REGISTER_STACK(p_thread->attr.p_stack, actual_stacksize);
227  return p_thread;
228 }
229 
230 static inline void ABTI_mem_free_thread(ABTI_local *p_local,
231  ABTI_thread *p_thread)
232 {
233  ABTI_stack_header *p_sh;
234  ABTI_VALGRIND_UNREGISTER_STACK(p_thread->attr.p_stack);
235 
236  if (p_thread->attr.stacktype != ABTI_STACK_TYPE_MEMPOOL) {
237  ABTU_free((void *)p_thread);
238  return;
239  }
240  p_sh = (ABTI_stack_header *)((char *)p_thread + sizeof(ABTI_thread));
241 
242 #ifndef ABT_CONFIG_DISABLE_EXT_THREAD
243  if (!p_local) {
244  /* This thread has been allocated internally,
245  * but now is being freed by an external thread. */
246  ABTI_mem_add_stack_to_global(p_sh);
247  return;
248  }
249 #endif
250 
251  if (p_local->num_stacks <= gp_ABTI_global->mem_max_stacks) {
252  p_sh->p_next = p_local->p_mem_stack;
253  p_local->p_mem_stack = p_sh;
254  p_local->num_stacks++;
255  } else {
256  ABTI_mem_add_stack_to_global(p_sh);
257  }
258 }
259 
260 static inline ABTI_task *ABTI_mem_alloc_task(ABTI_local *p_local)
261 {
262  ABTI_task *p_task = NULL;
263  const size_t blk_size = sizeof(ABTI_blk_header) + sizeof(ABTI_task);
264 
265 #ifndef ABT_CONFIG_DISABLE_EXT_THREAD
266  if (p_local == NULL) {
267  /* For external threads */
268  char *p_blk = (char *)ABTU_malloc(blk_size);
269  ABTI_blk_header *p_blk_header = (ABTI_blk_header *)p_blk;
270  p_blk_header->p_ph = NULL;
271  p_task = (ABTI_task *)(p_blk + sizeof(ABTI_blk_header));
272  return p_task;
273  }
274 #endif
275 
276  /* Find the page that has an empty block */
277  ABTI_page_header *p_ph = p_local->p_mem_task_head;
278  while (p_ph) {
279  if (p_ph->p_head)
280  break;
281  if (p_ph->p_free) {
282  ABTI_mem_take_free(p_ph);
283  break;
284  }
285 
286  p_ph = p_ph->p_next;
287  if (p_ph == p_local->p_mem_task_head) {
288  p_ph = NULL;
289  break;
290  }
291  }
292 
293  /* If there is no page that has an empty block */
294  if (p_ph == NULL) {
295  /* Check pages in the global data */
296  if (gp_ABTI_global->p_mem_task) {
297  p_ph = ABTI_mem_take_global_page(p_local);
298  if (p_ph == NULL) {
299  p_ph = ABTI_mem_alloc_page(p_local, blk_size);
300  }
301  } else {
302  /* Allocate a new page */
303  p_ph = ABTI_mem_alloc_page(p_local, blk_size);
304  }
305  }
306 
307  ABTI_blk_header *p_head = p_ph->p_head;
308  p_ph->p_head = p_head->p_next;
309  p_ph->num_empty_blks--;
310 
311  p_task = (ABTI_task *)((char *)p_head + sizeof(ABTI_blk_header));
312 
313  return p_task;
314 }
315 
316 static inline void ABTI_mem_free_task(ABTI_local *p_local, ABTI_task *p_task)
317 {
318  ABTI_blk_header *p_head;
319  ABTI_page_header *p_ph;
320 
321  p_head = (ABTI_blk_header *)((char *)p_task - sizeof(ABTI_blk_header));
322  p_ph = p_head->p_ph;
323 
324 #ifndef ABT_CONFIG_DISABLE_EXT_THREAD
325  if (p_ph == NULL) {
326  /* This was allocated by an external thread. */
327  ABTU_free(p_head);
328  return;
329  } else if (!p_local) {
330  /* This task has been allocated internally,
331  * but now is being freed by an external thread. */
332  ABTI_mem_free_remote(p_ph, p_head);
333  return;
334  }
335 #endif
336 
337  if (p_ph->owner_id == ABTI_self_get_native_thread_id(p_local)) {
338  p_head->p_next = p_ph->p_head;
339  p_ph->p_head = p_head;
340  p_ph->num_empty_blks++;
341 
342  /* TODO: Need to decrease the number of pages */
343  /* ABTI_mem_free_page(p_local, p_ph); */
344  } else {
345  /* Remote free */
346  ABTI_mem_free_remote(p_ph, p_head);
347  }
348 }
349 
350 #else /* ABT_CONFIG_USE_MEM_POOL */
351 
352 #define ABTI_mem_init(p)
353 #define ABTI_mem_init_local(p)
354 #define ABTI_mem_finalize(p)
355 #define ABTI_mem_finalize_local(p)
356 
357 static inline ABTI_thread *
358 ABTI_mem_alloc_thread_with_stacksize(size_t *p_stacksize)
359 {
360  size_t stacksize, actual_stacksize;
361  char *p_blk;
362  void *p_stack;
363  ABTI_thread *p_thread;
364 
365  /* Get the stack size */
366  stacksize = *p_stacksize;
367  actual_stacksize = stacksize - sizeof(ABTI_thread);
368 
369  /* Allocate ABTI_thread and a stack */
370  p_blk = (char *)ABTU_malloc(stacksize);
371  p_thread = (ABTI_thread *)p_blk;
372  p_stack = (void *)(p_blk + sizeof(ABTI_thread));
373 
374  /* Set attributes */
375  ABTI_thread_attr *p_myattr = &p_thread->attr;
376  ABTI_thread_attr_init(p_myattr, p_stack, actual_stacksize,
377  ABTI_STACK_TYPE_MALLOC, ABT_TRUE);
378 
379  *p_stacksize = actual_stacksize;
380  ABTI_VALGRIND_REGISTER_STACK(p_thread->attr.p_stack, *p_stacksize);
381  return p_thread;
382 }
383 
384 static inline ABTI_thread *ABTI_mem_alloc_thread(ABT_thread_attr attr,
385  size_t *p_stacksize)
386 {
387  ABTI_thread *p_thread;
388 
389  if (attr == ABT_THREAD_ATTR_NULL) {
390  *p_stacksize = ABTI_global_get_thread_stacksize();
391  return ABTI_mem_alloc_thread_with_stacksize(p_stacksize);
392  }
393 
394  /* Allocate a stack and set attributes */
395  ABTI_thread_attr *p_attr = ABTI_thread_attr_get_ptr(attr);
396  if (p_attr->p_stack == NULL) {
397  ABTI_ASSERT(p_attr->userstack == ABT_FALSE);
398 
399  char *p_blk = (char *)ABTU_malloc(p_attr->stacksize);
400  p_thread = (ABTI_thread *)p_blk;
401 
402  ABTI_thread_attr_copy(&p_thread->attr, p_attr);
403  p_thread->attr.stacksize -= sizeof(ABTI_thread);
404  p_thread->attr.p_stack = (void *)(p_blk + sizeof(ABTI_thread));
405 
406  } else {
407  /* Since the stack is given by the user, we create ABTI_thread
408  * explicitly instead of using a part of stack because the stack
409  * will be freed by the user. */
410  p_thread = (ABTI_thread *)ABTU_malloc(sizeof(ABTI_thread));
411  ABTI_thread_attr_copy(&p_thread->attr, p_attr);
412  }
413 
414  *p_stacksize = p_thread->attr.stacksize;
415  return p_thread;
416 }
417 
418 static inline ABTI_thread *ABTI_mem_alloc_main_thread(ABT_thread_attr attr)
419 {
420  ABTI_thread *p_thread;
421 
422  p_thread = (ABTI_thread *)ABTU_malloc(sizeof(ABTI_thread));
423 
424  /* Set attributes */
425  /* TODO: Need to set the actual stack address and size for the main ULT */
426  ABTI_thread_attr *p_attr = &p_thread->attr;
427  ABTI_thread_attr_init(p_attr, NULL, 0, ABTI_STACK_TYPE_MAIN, ABT_FALSE);
428 
429  return p_thread;
430 }
431 
432 static inline void ABTI_mem_free_thread(ABTI_thread *p_thread)
433 {
434  ABTI_VALGRIND_UNREGISTER_STACK(p_thread->attr.p_stack);
435  ABTU_free(p_thread);
436 }
437 
438 static inline ABTI_task *ABTI_mem_alloc_task(void)
439 {
440  return (ABTI_task *)ABTU_malloc(sizeof(ABTI_task));
441 }
442 
443 static inline void ABTI_mem_free_task(ABTI_task *p_task)
444 {
445  ABTU_free(p_task);
446 }
447 
448 #endif /* ABT_CONFIG_USE_MEM_POOL */
449 
450 #endif /* ABTI_MEM_H_INCLUDED */
struct ABT_thread_attr_opaque * ABT_thread_attr
Definition: abt.h:281
static void * ABTU_malloc(size_t size)
Definition: abtu.h:39
int ABT_bool
Definition: abt.h:309
#define ABT_FALSE
Definition: abt.h:224
ABTI_global * gp_ABTI_global
Definition: global.c:14
#define ABT_TRUE
Definition: abt.h:223
#define ABT_THREAD_ATTR_NULL
Definition: abt.h:345
static void ABTU_free(void *ptr)
Definition: abtu.h:32