update GC pseudocode

Ayke 2018-11-17 17:09:21 +01:00
parent 613f848ce1
commit 61a7bf871c
1 changed files with 33 additions and 32 deletions

@ -89,9 +89,6 @@ def gc_collect(): # implemented by ports
# add root pointers (platform-dependent)
gc_collect_root(roots)
# mark all reachable blocks
gc_mark()
# if there was a stack overflow, scan all memory for reachable blocks
gc_deal_with_stack_overflow()
@ -100,41 +97,48 @@ def gc_collect(): # implemented by ports
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)
# Test whether this could be a pointer.
if looks_like_pointer(root):
block = block_from_ptr(root)
if block.state == HEAD:
# Set the mark bit on this block.
block.state = MARK
# Shrink the stack to zero, hopefully without GC stack overflow.
gc_drain_stack()
# Mark all descendants of this root.
gc_mark_subtree(block)
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
# push it on the stack.
if is_heap_pointer(pointer):
# Get the block number this pointer points to.
block = BLOCK_FROM_PTR(pointer)
if block.state == HEAD:
block.state = MARK
# Is there space left on the GC stack?
if len(gc.stack) < MICROPY_ALLOC_GC_STACK_SIZE:
# Yes, add this block to the stack.
gc.stack.append(block)
else:
# Sadly, no. The mark phase can continue, but the whole memory will need
# to be scanned again (some marked blocks may have unmarked children).
gc.stack_overflow = True
def gc_mark_subtree(block):
# The passed in block must already been marked.
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()
while True:
# Check this block's children.
# 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)
for pointer in reversed(block.all_pointers_in_chain()[1:]):
if looks_like_pointer(pointer):
childblock = block_from_ptr(pointer)
if childblock.state == HEAD:
childblock.state = MARK
# Is there space left on the GC stack?
if len(gc.stack) < MICROPY_ALLOC_GC_STACK_SIZE:
# Yes, add this block to the stack.
gc.stack.append(block)
else:
# Sadly, no. The mark phase can continue, but the whole memory will need
# to be scanned again (some marked blocks may have unmarked children).
gc.stack_overflow = True
# Are there any blocks on the stack?
if len(gc.stack) == 0:
break # No, stack is empty, we're done.
# pop the next block off the stack
block = gc.stack.pop()
def gc_deal_with_stack_overflow():
# Keep scanning the whole memory until all marked blocks don't have unmarked
@ -143,16 +147,13 @@ def gc_deal_with_stack_overflow():
# 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()
gc_mark_subtree(block)
def gc_sweep():
# Free unmarked blocks and unmark marked blocks.