py/gc: Allow the GC heap to be split over multiple memory areas.

This commit adds a new option MICROPY_GC_SPLIT_HEAP (disabled by default)
which, when enabled, allows the GC heap to be split over multiple memory
areas/regions.  The first area is added with gc_init() and subsequent areas
can be added with gc_add().  New areas can be added at runtime.  Areas are
stored internally as a linked list, and calls to gc_alloc() can be
satisfied from any area.

This feature has the following use-cases (among others):
- The ESP32 has a fragmented OS heap, so to use all (or more) of it the
  GC heap must be split.
- Other MCUs may have disjoint RAM regions and are now able to use them
  all for the GC heap.
- The user could explicitly increase the size of the GC heap.
- Support a dynamic heap while running on an OS, adding more heap when
  necessary.
This commit is contained in:
Ayke van Laethem 2018-01-24 02:09:58 +01:00 committed by Damien George
parent 5dbb822ca4
commit bcc827d695
4 changed files with 497 additions and 322 deletions

769
py/gc.c

File diff suppressed because it is too large Load Diff

View File

@ -28,9 +28,15 @@
#include <stdbool.h>
#include <stddef.h>
#include "py/mpconfig.h"
void gc_init(void *start, void *end);
#if MICROPY_GC_SPLIT_HEAP
// Used to add additional memory areas to the heap.
void gc_add(void *start, void *end);
#endif
// These lock/unlock functions can be nested.
// They can be used to prevent the GC from allocating/freeing.
void gc_lock(void);

View File

@ -606,6 +606,11 @@
#define MICROPY_ENABLE_GC (0)
#endif
// Whether the garbage-collected heap can be split over multiple memory areas.
#ifndef MICROPY_GC_SPLIT_HEAP
#define MICROPY_GC_SPLIT_HEAP (0)
#endif
// Hook to run code during time consuming garbage collector operations
#ifndef MICROPY_GC_HOOK_LOOP
#define MICROPY_GC_HOOK_LOOP

View File

@ -71,12 +71,11 @@ typedef struct _mp_sched_item_t {
mp_obj_t arg;
} mp_sched_item_t;
// This structure hold information about the memory allocation system.
typedef struct _mp_state_mem_t {
#if MICROPY_MEM_STATS
size_t total_bytes_allocated;
size_t current_bytes_allocated;
size_t peak_bytes_allocated;
// This structure holds information about a single contiguous area of
// memory reserved for the memory manager.
typedef struct _mp_state_mem_area_t {
#if MICROPY_GC_SPLIT_HEAP
struct _mp_state_mem_area_t *next;
#endif
byte *gc_alloc_table_start;
@ -87,8 +86,30 @@ typedef struct _mp_state_mem_t {
byte *gc_pool_start;
byte *gc_pool_end;
size_t gc_last_free_atb_index;
} mp_state_mem_area_t;
// This structure holds a single stacked block and the area it is on. Used
// during garbage collection.
typedef struct {
#if MICROPY_GC_SPLIT_HEAP
mp_state_mem_area_t *area;
#endif
size_t block;
} mp_gc_stack_item_t;
// This structure hold information about the memory allocation system.
typedef struct _mp_state_mem_t {
#if MICROPY_MEM_STATS
size_t total_bytes_allocated;
size_t current_bytes_allocated;
size_t peak_bytes_allocated;
#endif
mp_state_mem_area_t area;
int gc_stack_overflow;
MICROPY_GC_STACK_ENTRY_TYPE gc_stack[MICROPY_ALLOC_GC_STACK_SIZE];
mp_gc_stack_item_t gc_stack[MICROPY_ALLOC_GC_STACK_SIZE];
// This variable controls auto garbage collection. If set to 0 then the
// GC won't automatically run when gc_alloc can't find enough blocks. But
@ -100,7 +121,9 @@ typedef struct _mp_state_mem_t {
size_t gc_alloc_threshold;
#endif
size_t gc_last_free_atb_index;
#if MICROPY_GC_SPLIT_HEAP
mp_state_mem_area_t *gc_last_free_area;
#endif
#if MICROPY_PY_GC_COLLECT_RETVAL
size_t gc_collected;