Update pseudocode to better reflect how the GC works.

Ayke 2018-01-20 17:23:16 +01:00
parent c75890ed75
commit 1358308750
1 changed files with 50 additions and 39 deletions

@ -79,43 +79,32 @@ The `gc_collect` function itself is implemented per port, so that it can easily
The Python pseudocode of the garbage collector (mark and sweep) would be the following:
```python
gc.stack_overflow = False
gc.stack = [] # array of blocks that are marked but not their children
gc.memory = ... # list of all blocks managed by the memory manager
MICROPY_ALLOC_GC_STACK_SIZE = 64 # default stack size (see py/mpconfig.h)
def gc_mark(): # gc_deal_with_stack_overflow
# Keep scanning the whole memory until all marked blocks don't have unmarked
# children.
# A 'stack overflow' happens if the GC stack cannot contain any more items.
# The stack is there to make the mark phase more efficient, i.e. it avoids
# having to scan the whole memory too often. But sometimes, the stack is too
# small and requires a full scan (like at the beginning of the mark phase).
gc.stack_overflow = True # indicates the whole memory needs to be scanned
while gc.stack_overflow:
gc.stack_overflow = False
gc.stack = []
for block in gc.memory:
# A block can have the states FREE, HEAD, TAIL or MARK, as set in the
# allocation table.
if block.state == MARK:
gc.stack.append(block)
gc_drain_stack()
def gc_collect(): # implemented by ports
# add root pointers (platform-dependent)
gc_collect_root(roots)
def gc_drain_stack():
# Try to reduce the stack, and don't return until it's empty. But with every
# reduction, multiple blocks may be added to the stack so this function may
# actually hit a stack overflow.
while len(gc.stack) > 0:
block = gc.stack.pop()
# Each block contains 4 memory words (on a 32-bit system with 16-byte
# blocks). These words may be pointers to blocks on the heap, but may also
# be other things like integers, parts of raw data (strings, bytecode, etc.)
# or pointers to memory outside of the heap.
for pointer in block:
may_add_to_stack(pointer)
# mark all reachable blocks
gc_mark()
# if there was a stack overflow, scan all memory for reachable blocks
gc_deal_with_stack_overflow()
# free all unreachable blocks
gc_sweep()
def gc_collect_root(roots):
for root in roots:
# Add this root to the (empty) stack. len(gc.stack) will be 1.
may_add_to_stack(root)
# Shrink the stack to zero, hopefully without GC stack overflow.
gc_drain_stack()
def may_add_to_stack(pointer): # VERIFY_MARK_AND_PUSH
# If this is a pointer and points to an unmarked block, mark it and try to
@ -134,6 +123,37 @@ def may_add_to_stack(pointer): # VERIFY_MARK_AND_PUSH
# to be scanned again (some marked blocks may have unmarked children).
gc.stack_overflow = True
def gc_drain_stack():
# Try to reduce the stack, and don't return until it's empty. But with every
# reduction, multiple blocks may be added to the stack so this function may
# actually hit a stack overflow.
while len(gc.stack) > 0:
block = gc.stack.pop()
# Each block contains 4 memory words (on a 32-bit system with 16-byte
# blocks). These words may be pointers to blocks on the heap, but may also
# be other things like integers, parts of raw data (strings, bytecode, etc.)
# or pointers to memory outside of the heap.
for pointer in block:
may_add_to_stack(pointer)
def gc_deal_with_stack_overflow():
# Keep scanning the whole memory until all marked blocks don't have unmarked
# children.
# A 'stack overflow' happens if the GC stack cannot contain any more items.
# The stack is there to make the mark phase more efficient, i.e. it avoids
# having to scan the whole memory too often. But sometimes, the stack is too
# small and requires a full scan (like at the beginning of the mark phase).
gc.stack_overflow = True # indicates the whole memory needs to be scanned
while gc.stack_overflow:
gc.stack_overflow = False
gc.stack = []
for block in gc.memory:
# A block can have the states FREE, HEAD, TAIL or MARK, as set in the
# allocation table.
if block.state == MARK:
gc.stack.append(block)
gc_drain_stack()
def gc_sweep():
# Free unmarked blocks and unmark marked blocks.
free_tail = False
@ -147,15 +167,6 @@ def gc_sweep():
elif block.state == MARK:
block.state = HEAD
free_tail = False
def gc_collect(): # implemented by ports
# add root pointers (platform-dependent)
# mark all reachable blocks
gc_mark()
# free all unreachable blocks
gc_sweep()
```
### Macros