py: Make gc.enable/disable just control auto-GC; alloc is still allowed.

gc.enable/disable are now the same as CPython: they just control whether
automatic garbage collection is enabled or not.  If disabled, you can
still allocate heap memory, and initiate a manual collection.
This commit is contained in:
Damien George 2014-10-31 21:30:46 +00:00
parent 4029f51842
commit 109c1de015
10 changed files with 42 additions and 19 deletions

View File

@ -4,23 +4,22 @@
.. module:: gc .. module:: gc
:synopsis: control the garbage collector :synopsis: control the garbage collector
Functions Functions
--------- ---------
.. function:: enable()
Enable automatic garbage collection.
.. function:: disable()
Disable automatic garbage collection. Heap memory can still be allocated,
and garbage collection can still be initiated manually using :meth:`gc.collect`.
.. function:: collect() .. function:: collect()
Run a garbage collection. Run a garbage collection.
.. function:: disable()
Disable the garbage collector.
.. function:: enable()
Enable the garbage collector.
.. function:: mem_alloc() .. function:: mem_alloc()
Return the number of bytes of heap RAM that are allocated. Return the number of bytes of heap RAM that are allocated.

View File

@ -28,6 +28,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include "mpconfig.h" #include "mpconfig.h"
#include "misc.h" #include "misc.h"
@ -68,7 +69,8 @@ STATIC mp_uint_t *gc_pool_end;
STATIC int gc_stack_overflow; STATIC int gc_stack_overflow;
STATIC mp_uint_t gc_stack[STACK_SIZE]; STATIC mp_uint_t gc_stack[STACK_SIZE];
STATIC mp_uint_t *gc_sp; STATIC mp_uint_t *gc_sp;
STATIC mp_uint_t gc_lock_depth; STATIC uint16_t gc_lock_depth;
uint16_t gc_auto_collect_enabled;
STATIC mp_uint_t gc_last_free_atb_index; STATIC mp_uint_t gc_last_free_atb_index;
// ATB = allocation table byte // ATB = allocation table byte
@ -163,6 +165,9 @@ void gc_init(void *start, void *end) {
// unlock the GC // unlock the GC
gc_lock_depth = 0; gc_lock_depth = 0;
// allow auto collection
gc_auto_collect_enabled = 1;
DEBUG_printf("GC layout:\n"); DEBUG_printf("GC layout:\n");
DEBUG_printf(" alloc table at %p, length " UINT_FMT " bytes, " UINT_FMT " blocks\n", gc_alloc_table_start, gc_alloc_table_byte_len, gc_alloc_table_byte_len * BLOCKS_PER_ATB); DEBUG_printf(" alloc table at %p, length " UINT_FMT " bytes, " UINT_FMT " blocks\n", gc_alloc_table_start, gc_alloc_table_byte_len, gc_alloc_table_byte_len * BLOCKS_PER_ATB);
#if MICROPY_ENABLE_FINALISER #if MICROPY_ENABLE_FINALISER
@ -375,7 +380,7 @@ void *gc_alloc(mp_uint_t n_bytes, bool has_finaliser) {
mp_uint_t end_block; mp_uint_t end_block;
mp_uint_t start_block; mp_uint_t start_block;
mp_uint_t n_free = 0; mp_uint_t n_free = 0;
int collected = 0; int collected = !gc_auto_collect_enabled;
for (;;) { for (;;) {
// look for a run of n_blocks available blocks // look for a run of n_blocks available blocks

View File

@ -32,6 +32,11 @@ void gc_lock(void);
void gc_unlock(void); void gc_unlock(void);
bool gc_is_locked(void); bool gc_is_locked(void);
// 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
// you can still allocate/free memory and also explicitly call gc_collect.
extern uint16_t gc_auto_collect_enabled;
// A given port must implement gc_collect by using the other collect functions. // A given port must implement gc_collect by using the other collect functions.
void gc_collect(void); void gc_collect(void);
void gc_collect_start(void); void gc_collect_start(void);

View File

@ -27,6 +27,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdint.h>
#include "mpconfig.h" #include "mpconfig.h"
#include "misc.h" #include "misc.h"

View File

@ -24,15 +24,13 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include <stdint.h>
#include "mpconfig.h" #include "mpconfig.h"
#include "misc.h" #include "misc.h"
#include "qstr.h" #include "qstr.h"
#include "obj.h" #include "obj.h"
#include "builtin.h"
#include "runtime.h" #include "runtime.h"
#include "objlist.h"
#include "objtuple.h"
#include "objstr.h"
#include "gc.h" #include "gc.h"
#if MICROPY_PY_GC && MICROPY_ENABLE_GC #if MICROPY_PY_GC && MICROPY_ENABLE_GC
@ -56,7 +54,7 @@ MP_DEFINE_CONST_FUN_OBJ_0(gc_collect_obj, py_gc_collect);
/// \function disable() /// \function disable()
/// Disable the garbage collector. /// Disable the garbage collector.
STATIC mp_obj_t gc_disable(void) { STATIC mp_obj_t gc_disable(void) {
gc_lock(); gc_auto_collect_enabled = 0;
return mp_const_none; return mp_const_none;
} }
MP_DEFINE_CONST_FUN_OBJ_0(gc_disable_obj, gc_disable); MP_DEFINE_CONST_FUN_OBJ_0(gc_disable_obj, gc_disable);
@ -64,11 +62,16 @@ MP_DEFINE_CONST_FUN_OBJ_0(gc_disable_obj, gc_disable);
/// \function enable() /// \function enable()
/// Enable the garbage collector. /// Enable the garbage collector.
STATIC mp_obj_t gc_enable(void) { STATIC mp_obj_t gc_enable(void) {
gc_unlock(); gc_auto_collect_enabled = 1;
return mp_const_none; return mp_const_none;
} }
MP_DEFINE_CONST_FUN_OBJ_0(gc_enable_obj, gc_enable); MP_DEFINE_CONST_FUN_OBJ_0(gc_enable_obj, gc_enable);
STATIC mp_obj_t gc_isenabled(void) {
return MP_BOOL(gc_auto_collect_enabled);
}
MP_DEFINE_CONST_FUN_OBJ_0(gc_isenabled_obj, gc_isenabled);
/// \function mem_free() /// \function mem_free()
/// Return the number of bytes of available heap RAM. /// Return the number of bytes of available heap RAM.
STATIC mp_obj_t gc_mem_free(void) { STATIC mp_obj_t gc_mem_free(void) {
@ -92,6 +95,7 @@ STATIC const mp_map_elem_t mp_module_gc_globals_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR_collect), (mp_obj_t)&gc_collect_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_collect), (mp_obj_t)&gc_collect_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_disable), (mp_obj_t)&gc_disable_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_disable), (mp_obj_t)&gc_disable_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_enable), (mp_obj_t)&gc_enable_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_enable), (mp_obj_t)&gc_enable_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_isenabled), (mp_obj_t)&gc_isenabled_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_mem_free), (mp_obj_t)&gc_mem_free_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_mem_free), (mp_obj_t)&gc_mem_free_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_mem_alloc), (mp_obj_t)&gc_mem_alloc_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_mem_alloc), (mp_obj_t)&gc_mem_alloc_obj },
}; };

View File

@ -28,6 +28,7 @@
#include <stdarg.h> #include <stdarg.h>
#include <assert.h> #include <assert.h>
#include <stdio.h> #include <stdio.h>
#include <stdint.h>
#include "mpconfig.h" #include "mpconfig.h"
#include "nlr.h" #include "nlr.h"

View File

@ -26,6 +26,7 @@
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
#include <stdint.h>
#include "mpconfig.h" #include "mpconfig.h"
#include "misc.h" #include "misc.h"

View File

@ -470,6 +470,7 @@ Q(gc)
Q(collect) Q(collect)
Q(disable) Q(disable)
Q(enable) Q(enable)
Q(isenabled)
Q(mem_free) Q(mem_free)
Q(mem_alloc) Q(mem_alloc)
#endif #endif

View File

@ -20,7 +20,12 @@ def h():
g(i) # default arg (second one) g(i) # default arg (second one)
g(i, i) # 2 args g(i, i) # 2 args
# call h with heap allocation disabled # call h with heap allocation disabled and all memory used up
gc.disable() gc.disable()
try:
while True:
'a'.lower # allocates 1 cell for boundmeth
except MemoryError:
pass
h() h()
gc.enable() gc.enable()

View File

@ -25,6 +25,7 @@
*/ */
#include <stdio.h> #include <stdio.h>
#include <stdint.h>
#include "mpconfig.h" #include "mpconfig.h"
#include "misc.h" #include "misc.h"