ARGOBOTS  1.1
mem_pool.c
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 #include "abti.h"
7 #include <stddef.h>
8 
9 static inline ABTI_mem_pool_page *
11 {
12  return (ABTI_mem_pool_page *)(((char *)lifo_elem) -
13  offsetof(ABTI_mem_pool_page, lifo_elem));
14 }
15 
16 static inline ABTI_mem_pool_header *
18 {
19  return (
20  ABTI_mem_pool_header *)(((char *)lifo_elem) -
21  (offsetof(ABTI_mem_pool_header, bucket_info) +
23  lifo_elem)));
24 }
25 
26 static void
28  ABTI_mem_pool_header *bucket)
29 {
30  int i;
31  const int num_headers_per_bucket = p_global_pool->num_headers_per_bucket;
32  /* Return headers in the last bucket to partial_bucket. */
33  ABTD_spinlock_acquire(&p_global_pool->partial_bucket_lock);
34  if (!p_global_pool->partial_bucket) {
35  p_global_pool->partial_bucket = bucket;
36  } else {
37  int num_headers_in_partial_bucket =
38  p_global_pool->partial_bucket->bucket_info.num_headers;
39  int num_headers_in_bucket = bucket->bucket_info.num_headers;
40  if (num_headers_in_partial_bucket + num_headers_in_bucket <
41  num_headers_per_bucket) {
42  /* Connect partial_bucket + bucket. Still not enough to make
43  * a complete bucket. */
44  ABTI_mem_pool_header *partial_bucket_tail =
45  p_global_pool->partial_bucket;
46  for (i = 1; i < num_headers_in_partial_bucket; i++) {
47  partial_bucket_tail = partial_bucket_tail->p_next;
48  }
49  partial_bucket_tail->p_next = bucket;
50  p_global_pool->partial_bucket->bucket_info.num_headers =
51  num_headers_in_partial_bucket + num_headers_in_bucket;
52  } else {
53  /* partial_bucket + bucket can make a complete bucket. */
54  ABTI_mem_pool_header *partial_bucket_header =
55  p_global_pool->partial_bucket;
56  for (i = 1; i < num_headers_per_bucket - num_headers_in_bucket;
57  i++) {
58  partial_bucket_header = partial_bucket_header->p_next;
59  }
60  ABTI_mem_pool_header *new_partial_bucket = NULL;
61  if (num_headers_in_partial_bucket + num_headers_in_bucket !=
62  num_headers_per_bucket) {
63  new_partial_bucket = partial_bucket_header->p_next;
64  new_partial_bucket->bucket_info.num_headers =
65  num_headers_per_bucket -
66  (num_headers_in_partial_bucket + num_headers_in_bucket);
67  }
68  partial_bucket_header->p_next = bucket;
69  ABTI_mem_pool_return_bucket(p_global_pool,
70  p_global_pool->partial_bucket);
71  p_global_pool->partial_bucket = new_partial_bucket;
72  }
73  }
74  ABTD_spinlock_release(&p_global_pool->partial_bucket_lock);
75 }
76 
78  ABTI_mem_pool_global_pool *p_global_pool, size_t num_headers_per_bucket,
79  size_t header_size, size_t header_offset, size_t page_size,
80  const ABTU_MEM_LARGEPAGE_TYPE *lp_type_requests,
81  uint32_t num_lp_type_requests, size_t alignment_hint)
82 {
83  p_global_pool->num_headers_per_bucket = num_headers_per_bucket;
84  ABTI_ASSERT(header_offset + sizeof(ABTI_mem_pool_header) <= header_size);
85  p_global_pool->header_size = header_size;
86  p_global_pool->header_offset = header_offset;
87  p_global_pool->page_size = page_size;
88 
89  /* Note that lp_type_requests is a constant-sized array */
90  ABTI_ASSERT(num_lp_type_requests <=
91  sizeof(p_global_pool->lp_type_requests) /
92  sizeof(ABTU_MEM_LARGEPAGE_TYPE));
93  p_global_pool->num_lp_type_requests = num_lp_type_requests;
94  memcpy(p_global_pool->lp_type_requests, lp_type_requests,
95  sizeof(ABTU_MEM_LARGEPAGE_TYPE) * num_lp_type_requests);
96  p_global_pool->alignment_hint = alignment_hint;
97 
98  ABTI_sync_lifo_init(&p_global_pool->mem_page_lifo);
99  ABTD_atomic_relaxed_store_ptr(&p_global_pool->p_mem_page_empty, NULL);
100  ABTI_sync_lifo_init(&p_global_pool->bucket_lifo);
101  ABTD_spinlock_clear(&p_global_pool->partial_bucket_lock);
102  p_global_pool->partial_bucket = NULL;
103 }
104 
106 {
107  /* All local pools must be released in advance.
108  * Because all headers are from memory pages, each individual header does
109  * not need to be freed. */
110  ABTI_mem_pool_page *p_page;
111  ABTI_sync_lifo_element *p_page_lifo_elem;
112  while ((p_page_lifo_elem =
113  ABTI_sync_lifo_pop_unsafe(&p_global_pool->mem_page_lifo))) {
114  p_page = mem_pool_lifo_elem_to_page(p_page_lifo_elem);
115  ABTU_free_largepage(p_page->mem, p_page->page_size, p_page->lp_type);
116  }
118  &p_global_pool->p_mem_page_empty);
119  while (p_page) {
120  ABTI_mem_pool_page *p_next = p_page->p_next_empty_page;
121  ABTU_free_largepage(p_page->mem, p_page->page_size, p_page->lp_type);
122  p_page = p_next;
123  }
124  ABTI_sync_lifo_destroy(&p_global_pool->bucket_lifo);
125  ABTI_sync_lifo_destroy(&p_global_pool->mem_page_lifo);
126 }
127 
128 ABTU_ret_err int
130  ABTI_mem_pool_global_pool *p_global_pool)
131 {
132  p_local_pool->p_global_pool = p_global_pool;
133  p_local_pool->num_headers_per_bucket =
134  p_global_pool->num_headers_per_bucket;
135  /* There must be always at least one header in the local pool.
136  * Let's take one bucket. */
137  int abt_errno =
138  ABTI_mem_pool_take_bucket(p_global_pool, &p_local_pool->buckets[0]);
139  ABTI_CHECK_ERROR(abt_errno);
140  p_local_pool->bucket_index = 0;
141  return ABT_SUCCESS;
142 }
143 
145 {
146  /* Return the remaining buckets to the global pool. */
147  int bucket_index = p_local_pool->bucket_index;
148  int i;
149  for (i = 0; i < bucket_index; i++) {
151  p_local_pool->buckets[i]);
152  }
153  const size_t num_headers_per_bucket = p_local_pool->num_headers_per_bucket;
154  ABTI_mem_pool_header *cur_bucket = p_local_pool->buckets[bucket_index];
155  if (cur_bucket->bucket_info.num_headers == num_headers_per_bucket) {
156  /* The last bucket is also full. Return the last bucket as well. */
158  p_local_pool->buckets[bucket_index]);
159  } else {
160  mem_pool_return_partial_bucket(p_local_pool->p_global_pool, cur_bucket);
161  }
162 }
163 
164 ABTU_ret_err int
166  ABTI_mem_pool_header **p_bucket)
167 {
168  /* Try to get a bucket. */
169  ABTI_sync_lifo_element *p_popped_bucket_lifo_elem =
170  ABTI_sync_lifo_pop(&p_global_pool->bucket_lifo);
171  const int num_headers_per_bucket = p_global_pool->num_headers_per_bucket;
172  if (ABTU_likely(p_popped_bucket_lifo_elem)) {
173  /* Use this bucket. */
174  ABTI_mem_pool_header *popped_bucket =
175  mem_pool_lifo_elem_to_header(p_popped_bucket_lifo_elem);
176  popped_bucket->bucket_info.num_headers = num_headers_per_bucket;
177  *p_bucket = popped_bucket;
178  return ABT_SUCCESS;
179  } else {
180  /* Allocate headers by myself */
181  const size_t header_size = p_global_pool->header_size;
182  int num_headers = 0, i;
183  ABTI_mem_pool_header *p_head = NULL;
184  while (1) {
185  ABTI_mem_pool_page *p_page;
186  ABTI_sync_lifo_element *p_page_lifo_elem;
187  /* Before really allocating memory, check if a page has unused
188  * memory. */
189  if ((p_page_lifo_elem =
190  ABTI_sync_lifo_pop(&p_global_pool->mem_page_lifo))) {
191  /* Use a page popped from mem_page_lifo */
192  p_page = mem_pool_lifo_elem_to_page(p_page_lifo_elem);
193  } else {
194  /* Let's allocate memory by myself */
195  const size_t page_size = p_global_pool->page_size;
196  ABTU_MEM_LARGEPAGE_TYPE lp_type;
197  void *p_alloc_mem;
198  int abt_errno =
199  ABTU_alloc_largepage(page_size,
200  p_global_pool->alignment_hint,
201  p_global_pool->lp_type_requests,
202  p_global_pool->num_lp_type_requests,
203  &lp_type, &p_alloc_mem);
204  if (ABTI_IS_ERROR_CHECK_ENABLED && abt_errno != ABT_SUCCESS) {
205  /* It fails to take a large page. Let's return. */
206  if (num_headers != 0) {
207  /* p_head has some elements, so let's return them. */
208  p_head->bucket_info.num_headers = num_headers;
209  mem_pool_return_partial_bucket(p_global_pool, p_head);
210  }
211  return abt_errno;
212  }
213  p_page =
214  (ABTI_mem_pool_page *)(((char *)p_alloc_mem) + page_size -
215  sizeof(ABTI_mem_pool_page));
216  p_page->mem = p_alloc_mem;
217  p_page->page_size = page_size;
218  p_page->lp_type = lp_type;
219  p_page->p_mem_extra = p_alloc_mem;
220  p_page->mem_extra_size = page_size - sizeof(ABTI_mem_pool_page);
221  }
222  /* Take some memory left in this page. */
223  int num_provided = p_page->mem_extra_size / header_size;
224  int num_required = num_headers_per_bucket - num_headers;
225  if (num_required < num_provided)
226  num_provided = num_required;
227  ABTI_ASSERT(num_provided != 0);
228 
229  void *p_mem_extra = p_page->p_mem_extra;
230  p_page->p_mem_extra =
231  (void *)(((char *)p_mem_extra) + header_size * num_provided);
232  p_page->mem_extra_size -= header_size * num_provided;
233  /* We've already gotten necessary p_mem_extra from this page. Let's
234  * return it. */
235  if (p_page->mem_extra_size >= header_size) {
236  /* This page still has some extra memory. Someone will use it in
237  * the future. */
238  ABTI_sync_lifo_push(&p_global_pool->mem_page_lifo,
239  &p_page->lifo_elem);
240  } else {
241  /* No extra memory is left in this page. Let's push it to a list
242  * of empty pages. Since mem_page_empty_lifo is push-only and
243  * thus there's no ABA problem, use a simpler lock-free LIFO
244  * algorithm. */
245  void *p_cur_mem_page;
246  do {
247  p_cur_mem_page = ABTD_atomic_acquire_load_ptr(
248  &p_global_pool->p_mem_page_empty);
249  p_page->p_next_empty_page =
250  (ABTI_mem_pool_page *)p_cur_mem_page;
251  } while (!ABTD_atomic_bool_cas_weak_ptr(&p_global_pool
252  ->p_mem_page_empty,
253  p_cur_mem_page,
254  p_page));
255  }
256 
257  size_t header_offset = p_global_pool->header_offset;
258  ABTI_mem_pool_header *p_local_tail =
259  (ABTI_mem_pool_header *)(((char *)p_mem_extra) + header_offset);
260  p_local_tail->p_next = p_head;
261  ABTI_mem_pool_header *p_prev = p_local_tail;
262  for (i = 1; i < num_provided; i++) {
263  ABTI_mem_pool_header *p_cur =
264  (ABTI_mem_pool_header *)(((char *)p_prev) + header_size);
265  p_cur->p_next = p_prev;
266  p_prev = p_cur;
267  }
268  p_head = p_prev;
269  num_headers += num_provided;
270  if (num_headers == num_headers_per_bucket) {
271  p_head->bucket_info.num_headers = num_headers_per_bucket;
272  *p_bucket = p_head;
273  return ABT_SUCCESS;
274  }
275  }
276  }
277 }
278 
280  ABTI_mem_pool_header *bucket)
281 {
282  /* Simply return that bucket to the pool */
283  ABTI_sync_lifo_push(&p_global_pool->bucket_lifo,
284  &bucket->bucket_info.lifo_elem);
285 }
ABTI_mem_pool_init_local_pool
ABTU_ret_err int ABTI_mem_pool_init_local_pool(ABTI_mem_pool_local_pool *p_local_pool, ABTI_mem_pool_global_pool *p_global_pool)
Definition: mem_pool.c:129
ABTI_mem_pool_take_bucket
ABTU_ret_err int ABTI_mem_pool_take_bucket(ABTI_mem_pool_global_pool *p_global_pool, ABTI_mem_pool_header **p_bucket)
Definition: mem_pool.c:165
ABTI_mem_pool_header::bucket_info
ABTI_mem_pool_header_bucket_info bucket_info
Definition: abti_mem_pool.h:22
mem_pool_lifo_elem_to_header
static ABTI_mem_pool_header * mem_pool_lifo_elem_to_header(ABTI_sync_lifo_element *lifo_elem)
Definition: mem_pool.c:17
ABTI_sync_lifo_init
static void ABTI_sync_lifo_init(ABTI_sync_lifo *p_lifo)
Definition: abti_sync_lifo.h:29
ABTI_mem_pool_global_pool::num_lp_type_requests
uint32_t num_lp_type_requests
Definition: abti_mem_pool.h:58
ABTI_mem_pool_init_global_pool
void ABTI_mem_pool_init_global_pool(ABTI_mem_pool_global_pool *p_global_pool, size_t num_headers_per_bucket, size_t header_size, size_t header_offset, size_t page_size, const ABTU_MEM_LARGEPAGE_TYPE *lp_type_requests, uint32_t num_lp_type_requests, size_t alignment_hint)
Definition: mem_pool.c:77
ABTI_mem_pool_local_pool
Definition: abti_mem_pool.h:90
ABTI_CHECK_ERROR
#define ABTI_CHECK_ERROR(abt_errno)
Definition: abti_error.h:120
ABTI_mem_pool_local_pool::bucket_index
size_t bucket_index
Definition: abti_mem_pool.h:95
ABTI_sync_lifo_element
Definition: abti_sync_lifo.h:16
ABTU_alloc_largepage
ABTU_ret_err int ABTU_alloc_largepage(size_t size, size_t alignment_hint, const ABTU_MEM_LARGEPAGE_TYPE *requested_types, int num_requested_types, ABTU_MEM_LARGEPAGE_TYPE *p_actual, void **p_ptr)
Definition: largepage.c:90
ABTU_free_largepage
void ABTU_free_largepage(void *ptr, size_t size, ABTU_MEM_LARGEPAGE_TYPE type)
Definition: largepage.c:132
ABTI_mem_pool_page::p_mem_extra
void * p_mem_extra
Definition: abti_mem_pool.h:31
ABTI_IS_ERROR_CHECK_ENABLED
#define ABTI_IS_ERROR_CHECK_ENABLED
Definition: abti.h:20
ABTI_mem_pool_page::p_next_empty_page
struct ABTI_mem_pool_page * p_next_empty_page
Definition: abti_mem_pool.h:27
ABTU_likely
#define ABTU_likely(cond)
Definition: abtu.h:111
ABTI_mem_pool_local_pool::p_global_pool
ABTI_mem_pool_global_pool * p_global_pool
Definition: abti_mem_pool.h:91
ABTI_sync_lifo_pop
static ABTI_sync_lifo_element * ABTI_sync_lifo_pop(ABTI_sync_lifo *p_lifo)
Definition: abti_sync_lifo.h:116
abti.h
ABTI_mem_pool_global_pool::page_size
size_t page_size
Definition: abti_mem_pool.h:51
ABTD_spinlock_acquire
static void ABTD_spinlock_acquire(ABTD_spinlock *p_lock)
Definition: abtd_spinlock.h:28
ABTI_mem_pool_global_pool::num_headers_per_bucket
size_t num_headers_per_bucket
Definition: abti_mem_pool.h:56
ABTI_mem_pool_global_pool::alignment_hint
size_t alignment_hint
Definition: abti_mem_pool.h:52
ABTI_mem_pool_header_bucket_info::num_headers
size_t num_headers
Definition: abti_mem_pool.h:17
ABTI_mem_pool_global_pool::header_offset
size_t header_offset
Definition: abti_mem_pool.h:53
mem_pool_return_partial_bucket
static void mem_pool_return_partial_bucket(ABTI_mem_pool_global_pool *p_global_pool, ABTI_mem_pool_header *bucket)
Definition: mem_pool.c:27
ABTI_mem_pool_page::page_size
size_t page_size
Definition: abti_mem_pool.h:29
ABTI_ASSERT
#define ABTI_ASSERT(cond)
Definition: abti_error.h:12
ABTI_mem_pool_header
Definition: abti_mem_pool.h:20
ABTU_MEM_LARGEPAGE_TYPE
ABTU_MEM_LARGEPAGE_TYPE
Definition: abtu.h:297
ABT_SUCCESS
#define ABT_SUCCESS
Error code: the routine returns successfully.
Definition: abt.h:92
ABTD_atomic_bool_cas_weak_ptr
static int ABTD_atomic_bool_cas_weak_ptr(ABTD_atomic_ptr *ptr, void *oldv, void *newv)
Definition: abtd_atomic.h:379
ABTU_ret_err
#define ABTU_ret_err
Definition: abtu.h:146
ABTI_mem_pool_return_bucket
void ABTI_mem_pool_return_bucket(ABTI_mem_pool_global_pool *p_global_pool, ABTI_mem_pool_header *bucket)
Definition: mem_pool.c:279
ABTI_mem_pool_global_pool::lp_type_requests
ABTU_MEM_LARGEPAGE_TYPE lp_type_requests[4]
Definition: abti_mem_pool.h:61
ABTI_mem_pool_destroy_local_pool
void ABTI_mem_pool_destroy_local_pool(ABTI_mem_pool_local_pool *p_local_pool)
Definition: mem_pool.c:144
ABTI_sync_lifo_push
static void ABTI_sync_lifo_push(ABTI_sync_lifo *p_lifo, ABTI_sync_lifo_element *p_elem)
Definition: abti_sync_lifo.h:90
ABTI_sync_lifo_destroy
static void ABTI_sync_lifo_destroy(ABTI_sync_lifo *p_lifo)
Definition: abti_sync_lifo.h:40
ABTI_mem_pool_global_pool
Definition: abti_mem_pool.h:49
ABTI_mem_pool_page::mem
void * mem
Definition: abti_mem_pool.h:28
ABTI_mem_pool_header_bucket_info::lifo_elem
ABTI_sync_lifo_element lifo_elem
Definition: abti_mem_pool.h:15
ABTD_spinlock_clear
static void ABTD_spinlock_clear(ABTD_spinlock *p_lock)
Definition: abtd_spinlock.h:23
ABTI_mem_pool_destroy_global_pool
void ABTI_mem_pool_destroy_global_pool(ABTI_mem_pool_global_pool *p_global_pool)
Definition: mem_pool.c:105
ABTI_mem_pool_page::lp_type
ABTU_MEM_LARGEPAGE_TYPE lp_type
Definition: abti_mem_pool.h:30
ABTD_spinlock_release
static void ABTD_spinlock_release(ABTD_spinlock *p_lock)
Definition: abtd_spinlock.h:42
ABTI_mem_pool_global_pool::header_size
size_t header_size
Definition: abti_mem_pool.h:50
ABTD_atomic_acquire_load_ptr
static void * ABTD_atomic_acquire_load_ptr(const ABTD_atomic_ptr *ptr)
Definition: abtd_atomic.h:979
ABTI_mem_pool_page
Definition: abti_mem_pool.h:25
ABTI_mem_pool_global_pool::partial_bucket
ABTI_mem_pool_header * partial_bucket
Definition: abti_mem_pool.h:73
ABTD_atomic_relaxed_store_ptr
static void ABTD_atomic_relaxed_store_ptr(ABTD_atomic_ptr *ptr, void *val)
Definition: abtd_atomic.h:1055
ABTI_mem_pool_header::p_next
struct ABTI_mem_pool_header * p_next
Definition: abti_mem_pool.h:21
ABTI_mem_pool_header_bucket_info
Definition: abti_mem_pool.h:13
ABTI_sync_lifo_pop_unsafe
static ABTI_sync_lifo_element * ABTI_sync_lifo_pop_unsafe(ABTI_sync_lifo *p_lifo)
Definition: abti_sync_lifo.h:66
mem_pool_lifo_elem_to_page
static ABTI_mem_pool_page * mem_pool_lifo_elem_to_page(ABTI_sync_lifo_element *lifo_elem)
Definition: mem_pool.c:10
ABTI_mem_pool_page::lifo_elem
ABTI_sync_lifo_element lifo_elem
Definition: abti_mem_pool.h:26
ABTI_mem_pool_page::mem_extra_size
size_t mem_extra_size
Definition: abti_mem_pool.h:32
ABTI_mem_pool_page
struct ABTI_mem_pool_page ABTI_mem_pool_page
ABTI_mem_pool_local_pool::num_headers_per_bucket
size_t num_headers_per_bucket
Definition: abti_mem_pool.h:92
ABTD_atomic_relaxed_load_ptr
static void * ABTD_atomic_relaxed_load_ptr(const ABTD_atomic_ptr *ptr)
Definition: abtd_atomic.h:846
ABTI_mem_pool_local_pool::buckets
ABTI_mem_pool_header * buckets[ABT_MEM_POOL_MAX_LOCAL_BUCKETS]
Definition: abti_mem_pool.h:96