/ components / heap / multi_heap.c
multi_heap.c
  1  // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
  2  //
  3  // Licensed under the Apache License, Version 2.0 (the "License");
  4  // you may not use this file except in compliance with the License.
  5  // You may obtain a copy of the License at
  6  
  7  //     http://www.apache.org/licenses/LICENSE-2.0
  8  //
  9  // Unless required by applicable law or agreed to in writing, software
 10  // distributed under the License is distributed on an "AS IS" BASIS,
 11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12  // See the License for the specific language governing permissions and
 13  // limitations under the License.
 14  #include <stdint.h>
 15  #include <stdlib.h>
 16  #include <stdbool.h>
 17  #include <assert.h>
 18  #include <string.h>
 19  #include <stddef.h>
 20  #include <stdio.h>
 21  #include <sys/cdefs.h>
 22  #include "heap_tlsf.h"
 23  #include <multi_heap.h>
 24  #include "multi_heap_internal.h"
 25  
 26  /* Note: Keep platform-specific parts in this header, this source
 27     file should depend on libc only */
 28  #include "multi_heap_platform.h"
 29  
 30  /* Defines compile-time configuration macros */
 31  #include "multi_heap_config.h"
 32  
 33  #ifndef MULTI_HEAP_POISONING
 34  /* if no heap poisoning, public API aliases directly to these implementations */
 35  void *multi_heap_malloc(multi_heap_handle_t heap, size_t size)
 36      __attribute__((alias("multi_heap_malloc_impl")));
 37  
 38  void *multi_heap_aligned_alloc(multi_heap_handle_t heap, size_t size, size_t alignment)
 39      __attribute__((alias("multi_heap_aligned_alloc_impl")));
 40  
 41  void multi_heap_aligned_free(multi_heap_handle_t heap, void *p)
 42      __attribute__((alias("multi_heap_free_impl")));
 43  
 44  void multi_heap_free(multi_heap_handle_t heap, void *p)
 45      __attribute__((alias("multi_heap_free_impl")));
 46  
 47  void *multi_heap_realloc(multi_heap_handle_t heap, void *p, size_t size)
 48      __attribute__((alias("multi_heap_realloc_impl")));
 49  
 50  size_t multi_heap_get_allocated_size(multi_heap_handle_t heap, void *p)
 51      __attribute__((alias("multi_heap_get_allocated_size_impl")));
 52  
 53  multi_heap_handle_t multi_heap_register(void *start, size_t size)
 54      __attribute__((alias("multi_heap_register_impl")));
 55  
 56  void multi_heap_get_info(multi_heap_handle_t heap, multi_heap_info_t *info)
 57      __attribute__((alias("multi_heap_get_info_impl")));
 58  
 59  size_t multi_heap_free_size(multi_heap_handle_t heap)
 60      __attribute__((alias("multi_heap_free_size_impl")));
 61  
 62  size_t multi_heap_minimum_free_size(multi_heap_handle_t heap)
 63      __attribute__((alias("multi_heap_minimum_free_size_impl")));
 64  
 65  void *multi_heap_get_block_address(multi_heap_block_handle_t block)
 66      __attribute__((alias("multi_heap_get_block_address_impl")));
 67  
 68  void *multi_heap_get_block_owner(multi_heap_block_handle_t block)
 69  {
 70      return NULL;
 71  }
 72  
 73  #endif
 74  
 75  #define ALIGN(X) ((X) & ~(sizeof(void *)-1))
 76  #define ALIGN_UP(X) ALIGN((X)+sizeof(void *)-1)
 77  #define ALIGN_UP_BY(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
 78  
 79  
 80  typedef struct multi_heap_info {
 81      void *lock;
 82      size_t free_bytes;
 83      size_t minimum_free_bytes;
 84      size_t pool_size;
 85      tlsf_t heap_data;
 86  } heap_t;
 87  
 88  /* Return true if this block is free. */
 89  static inline bool is_free(const block_header_t *block)
 90  {
 91      return ((block->size & 0x01) != 0);
 92  }
 93  
 94  /* Data size of the block (excludes this block's header) */
 95  static inline size_t block_data_size(const block_header_t *block)
 96  {
 97      return (block->size & ~0x03);
 98  }
 99  
100  /* Check a block is valid for this heap. Used to verify parameters. */
101  static void assert_valid_block(const heap_t *heap, const block_header_t *block)
102  {
103      pool_t pool = tlsf_get_pool(heap->heap_data);
104      void *ptr = block_to_ptr(block);
105  
106      MULTI_HEAP_ASSERT((ptr >= pool) && 
107                      (ptr < pool + heap->pool_size), 
108                      (uintptr_t)ptr);
109  }
110  
111  void *multi_heap_get_block_address_impl(multi_heap_block_handle_t block)
112  {
113      void *ptr = block_to_ptr(block);
114      return (ptr);
115  }
116  
117  size_t multi_heap_get_allocated_size_impl(multi_heap_handle_t heap, void *p)
118  {
119      return tlsf_block_size(p);
120  }
121  
122  multi_heap_handle_t multi_heap_register_impl(void *start_ptr, size_t size)
123  {
124      assert(start_ptr);
125      if(size < (tlsf_size() + tlsf_block_size_min() + sizeof(heap_t))) {
126          //Region too small to be a heap.
127          return NULL;
128      }
129  
130      heap_t *result = (heap_t *)start_ptr;
131      size -= sizeof(heap_t);
132  
133      result->heap_data = tlsf_create_with_pool(start_ptr + sizeof(heap_t), size);
134      if(!result->heap_data) {
135          return NULL;
136      }
137  
138      result->lock = NULL;
139      result->free_bytes = size - tlsf_size();
140      result->pool_size = size;
141      result->minimum_free_bytes = result->free_bytes;
142      return result;
143  }
144  
145  void multi_heap_set_lock(multi_heap_handle_t heap, void *lock)
146  {
147      heap->lock = lock;
148  }
149  
150  void inline multi_heap_internal_lock(multi_heap_handle_t heap)
151  {
152      MULTI_HEAP_LOCK(heap->lock);
153  }
154  
155  void inline multi_heap_internal_unlock(multi_heap_handle_t heap)
156  {
157      MULTI_HEAP_UNLOCK(heap->lock);
158  }
159  
160  multi_heap_block_handle_t multi_heap_get_first_block(multi_heap_handle_t heap)
161  {
162      assert(heap != NULL);
163      pool_t pool = tlsf_get_pool(heap->heap_data);
164      block_header_t* block = offset_to_block(pool, -(int)block_header_overhead);
165  
166      return (multi_heap_block_handle_t)block;
167  }
168  
169  multi_heap_block_handle_t multi_heap_get_next_block(multi_heap_handle_t heap, multi_heap_block_handle_t block)
170  {
171      assert(heap != NULL);
172      assert_valid_block(heap, block);
173      block_header_t* next = block_next(block);
174   
175      if(block_data_size(next) == 0) {
176          //Last block:
177          return NULL;
178      } else {
179          return (multi_heap_block_handle_t)next;
180      }
181  
182  }
183  
184  bool multi_heap_is_free(multi_heap_block_handle_t block)
185  {
186      return is_free(block);
187  }
188  
189  void *multi_heap_malloc_impl(multi_heap_handle_t heap, size_t size)
190  {
191      if (size == 0 || heap == NULL) {
192          return NULL;
193      }
194  
195  
196      multi_heap_internal_lock(heap);
197      void *result = tlsf_malloc(heap->heap_data, size);
198      if(result) {
199          heap->free_bytes -= tlsf_block_size(result);
200          if (heap->free_bytes < heap->minimum_free_bytes) {
201              heap->minimum_free_bytes = heap->free_bytes;
202          }
203      }    
204      multi_heap_internal_unlock(heap);
205  
206      return result;
207  }
208  
209  void multi_heap_free_impl(multi_heap_handle_t heap, void *p)
210  {
211  
212      if (heap == NULL || p == NULL) {
213          return;
214      }
215  
216      assert_valid_block(heap, p);
217  
218      multi_heap_internal_lock(heap);
219      heap->free_bytes += tlsf_block_size(p);
220      tlsf_free(heap->heap_data, p);
221      multi_heap_internal_unlock(heap);
222  }
223  
224  void *multi_heap_realloc_impl(multi_heap_handle_t heap, void *p, size_t size)
225  {
226      assert(heap != NULL);
227  
228      if (p == NULL) {
229          return multi_heap_malloc_impl(heap, size);
230      }
231  
232      assert_valid_block(heap, p);
233  
234      if (heap == NULL) {
235          return NULL;
236      }
237  
238      multi_heap_internal_lock(heap);
239      size_t previous_block_size =  tlsf_block_size(p);
240      void *result = tlsf_realloc(heap->heap_data, p, size);
241      if(result) {
242          heap->free_bytes += previous_block_size;
243          heap->free_bytes -= tlsf_block_size(result);
244          if (heap->free_bytes < heap->minimum_free_bytes) {
245              heap->minimum_free_bytes = heap->free_bytes;
246          }
247      }
248      
249      multi_heap_internal_unlock(heap);
250  
251      return result;
252  }
253  
254  void *multi_heap_aligned_alloc_impl(multi_heap_handle_t heap, size_t size, size_t alignment)
255  {
256      if(heap == NULL) {
257          return NULL;
258      }
259  
260      if(!size) {
261          return NULL;
262      }
263  
264      //Alignment must be a power of two:
265      if(((alignment & (alignment - 1)) != 0) ||(!alignment)) {
266          return NULL;
267      }
268  
269      multi_heap_internal_lock(heap);
270      void *result = tlsf_memalign(heap->heap_data, alignment, size);
271      if(result) {
272          heap->free_bytes -= tlsf_block_size(result);
273          if(heap->free_bytes < heap->minimum_free_bytes) {
274              heap->minimum_free_bytes = heap->free_bytes;
275          }
276      }
277      multi_heap_internal_unlock(heap);
278  
279      return result;
280  }
281  
282  bool multi_heap_check(multi_heap_handle_t heap, bool print_errors)
283  {
284      (void)print_errors;
285      bool valid = true;
286      assert(heap != NULL);
287  
288      multi_heap_internal_lock(heap);
289      if(tlsf_check(heap->heap_data)) {
290          valid = false;
291      }
292  
293      if(tlsf_check_pool(tlsf_get_pool(heap->heap_data))) {
294          valid = false;
295      }
296  
297      multi_heap_internal_unlock(heap);
298      return valid;
299  }
300  
301  static void multi_heap_dump_tlsf(void* ptr, size_t size, int used, void* user)
302  {
303      (void)user;
304      MULTI_HEAP_STDERR_PRINTF("Block %p data, size: %d bytes, Free: %s \n", 
305                              (void *)ptr,
306                              size,
307                              used ? "No" : "Yes");
308  }
309  
310  void multi_heap_dump(multi_heap_handle_t heap)
311  {
312      assert(heap != NULL);
313  
314      multi_heap_internal_lock(heap);
315      MULTI_HEAP_STDERR_PRINTF("Showing data for heap: %p \n", (void *)heap);
316      tlsf_walk_pool(tlsf_get_pool(heap->heap_data), multi_heap_dump_tlsf, NULL);
317      multi_heap_internal_unlock(heap);
318  }
319  
320  size_t multi_heap_free_size_impl(multi_heap_handle_t heap)
321  {
322      if (heap == NULL) {
323          return 0;
324      }
325  
326      return heap->free_bytes;
327  }
328  
329  size_t multi_heap_minimum_free_size_impl(multi_heap_handle_t heap)
330  {
331      if (heap == NULL) {
332          return 0;
333      }
334  
335      return heap->minimum_free_bytes;
336  }
337  
338  static void multi_heap_get_info_tlsf(void* ptr, size_t size, int used, void* user)
339  {
340      multi_heap_info_t *info = user;
341      
342      if(used) {
343          info->allocated_blocks++;
344      } else {
345          info->free_blocks++;
346          
347          if(size > info->largest_free_block ) {
348              info->largest_free_block = size;
349          }   
350      }
351      
352      info->total_blocks++; 
353  }
354  
355  void multi_heap_get_info_impl(multi_heap_handle_t heap, multi_heap_info_t *info)
356  {
357      memset(info, 0, sizeof(multi_heap_info_t));
358  
359      if (heap == NULL) {
360          return;
361      }
362  
363      multi_heap_internal_lock(heap);
364      tlsf_walk_pool(tlsf_get_pool(heap->heap_data), multi_heap_get_info_tlsf, info);
365      info->total_allocated_bytes = (heap->pool_size - tlsf_size()) - heap->free_bytes;
366      info->minimum_free_bytes = heap->minimum_free_bytes;
367      info->total_free_bytes = heap->free_bytes;
368      info->largest_free_block = info->largest_free_block ? 1 << (31 - __builtin_clz(info->largest_free_block)) : 0;
369      multi_heap_internal_unlock(heap);
370  }