Files
micropython/py/gc.h
Damien George 74e945752b py/modweakref: Implement weakref module with ref and finalize classes.
This adds support for the standard `weakref` module, to make weak
references to Python objects and have callbacks for when an object is
reclaimed by the GC.

This feature was requested by PyScript, to allow control over the lifetime
of external proxy objects (distinct from JS<->Python proxies).

Addresses issue #646 (that's nearly a 12 year old issue!).

Functionality added here:
- `weakref.ref(object [, callback])` create a simple weak reference with
  optional callback to be called when the object is reclaimed by the GC
- `weakref.finalize(object, callback, /, *args, **kwargs)` create a
  finalize object that holds a weak reference to an object and allows more
  convenient callback usage and state change

The new module is enabled at the "everything" level.

The implementation aims to be as efficient as possible, by adding another
bit-per-block to the garbage collector, the WTB (weak table).  Similar to
the finalizer bit (FTB), if a GC block has its corresponding WTB bit set
then a weak reference to that block is held.  The details of that weak
reference are stored in a global map, `mp_weakref_map`, which maps weak
reference to ref/finalize objects, allowing the callbacks to be efficiently
found when the object is reclaimed.

With this feature enabled the overhead is:
- 1/128th of the available memory is used for the new WTB table (eg a 128k
  heap now needs an extra 1k for the WTB).
- Code size is increased.
- At garbage collection time, there is a small overhead to check if the
  collected objects had weak references.  This check is the same as the
  existing FTB finaliser scan, so shouldn't add much overhead.  If there
  are weak reference objects alive (ref/finalize objects) then additional
  time is taken to call the callbacks and do some accounting to clean up
  the used weak reference.

Signed-off-by: Damien George <damien@micropython.org>
2026-03-22 23:02:38 +11:00

93 lines
3.0 KiB
C

/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2013, 2014 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_PY_GC_H
#define MICROPY_INCLUDED_PY_GC_H
#include <stdbool.h>
#include <stddef.h>
#include "py/mpprint.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);
#if MICROPY_GC_SPLIT_HEAP_AUTO
// Port must implement this function to return the maximum available block of
// RAM to allocate a new heap area into using MP_PLAT_ALLOC_HEAP.
size_t gc_get_max_new_split(void);
#endif // MICROPY_GC_SPLIT_HEAP_AUTO
#endif // MICROPY_GC_SPLIT_HEAP
// These lock/unlock functions can be nested.
// They can be used to prevent the GC from allocating/freeing.
void gc_lock(void);
void gc_unlock(void);
bool gc_is_locked(void);
// A given port must implement gc_collect by using the other collect functions.
void gc_collect(void);
void gc_collect_start(void);
void gc_collect_root(void **ptrs, size_t len);
void gc_collect_end(void);
// Use this function to sweep the whole heap and run all finalisers
void gc_sweep_all(void);
// These functions are used to manage weakrefs.
void gc_weakref_mark(void *ptr);
void gc_weakref_about_to_be_freed(void *ptr);
void gc_weakref_sweep(void);
enum {
GC_ALLOC_FLAG_HAS_FINALISER = 1,
};
void *gc_alloc(size_t n_bytes, unsigned int alloc_flags);
void gc_free(void *ptr); // does not call finaliser
size_t gc_nbytes(const void *ptr);
void *gc_realloc(void *ptr, size_t n_bytes, bool allow_move);
typedef struct _gc_info_t {
size_t total;
size_t used;
size_t free;
size_t max_free;
size_t num_1block;
size_t num_2block;
size_t max_block;
#if MICROPY_GC_SPLIT_HEAP_AUTO
size_t max_new_split;
#endif
} gc_info_t;
void gc_info(gc_info_t *info);
void gc_dump_info(const mp_print_t *print);
void gc_dump_alloc_table(const mp_print_t *print);
#endif // MICROPY_INCLUDED_PY_GC_H