Commit 93c02fab authored by Rudi Chen's avatar Rudi Chen

No-op explicit deallocations and make GCs safe inside finalizers.

Make sure CPython functions like PyObject_Del do not free any objects.
It's better to let the GC free all objects because only the GC can
guarantee that there are no more references to an object.
parent 6ddd0f97
......@@ -53,7 +53,6 @@ extern "C" {
PyAPI_FUNC(void *) gc_compat_malloc(size_t) PYSTON_NOEXCEPT;
PyAPI_FUNC(void *) gc_compat_realloc(void *, size_t) PYSTON_NOEXCEPT;
PyAPI_FUNC(void) gc_compat_free(void *) PYSTON_NOEXCEPT;
PyAPI_FUNC(void *) PyMem_Malloc(size_t) PYSTON_NOEXCEPT;
PyAPI_FUNC(void *) PyMem_Realloc(void *, size_t) PYSTON_NOEXCEPT;
......@@ -81,7 +80,7 @@ PyAPI_FUNC(void) PyMem_Free(void *) PYSTON_NOEXCEPT;
: gc_compat_malloc((n) ? (n) : 1))
#define PyMem_REALLOC(p, n) ((size_t)(n) > (size_t)PY_SSIZE_T_MAX ? NULL \
: gc_compat_realloc((p), (n) ? (n) : 1))
#define PyMem_FREE gc_compat_free
#define PyMem_FREE PyMem_Free
#endif /* PYMALLOC_DEBUG */
......
......@@ -548,7 +548,7 @@ static void callPendingFinalizers() {
Box* box = pending_finalization_list.front();
pending_finalization_list.pop_front();
assert(isValidGCObject(box));
RELEASE_ASSERT(isValidGCObject(box), "objects to be finalized should still be alive");
if (isWeaklyReferenced(box)) {
// Callbacks for weakly-referenced objects with finalizers (if any), followed by call to finalizers.
......@@ -565,6 +565,7 @@ static void callPendingFinalizers() {
}
finalize(box);
RELEASE_ASSERT(isValidGCObject(box), "finalizing an object should not free the object");
}
if (!initially_empty) {
......@@ -758,6 +759,12 @@ void runCollection() {
global_heap.prepareForCollection();
// Finalizers might have been called since the last GC.
// Normally we invalidate the list everytime we call a batch of objects with finalizers.
// However, there are some edge cases where that isn't sufficient, such as a GC being triggered
// inside a finalizer call. To be safe, it's better to invalidate the list again.
invalidateOrderedFinalizerList();
markPhase();
// The sweep phase will not free weakly-referenced objects, so that we can inspect their
......
......@@ -45,11 +45,6 @@ extern "C" void* gc_compat_realloc(void* ptr, size_t sz) noexcept {
return gc_realloc(ptr, sz);
}
extern "C" void gc_compat_free(void* ptr) noexcept {
if (ptr)
gc_free(ptr);
}
// We may need to hook malloc as well. For now, these definitions serve
// as a reference on how to do that, and also can help with debugging malloc
// usage issues.
......
......@@ -1031,7 +1031,8 @@ extern "C" void* PyObject_Realloc(void* ptr, size_t sz) noexcept {
}
extern "C" void PyObject_Free(void* ptr) noexcept {
gc_compat_free(ptr);
// In Pyston, everything is GC'ed and we shouldn't explicitely free memory.
// Only the GC knows for sure that an object is no longer referenced.
}
extern "C" void* PyMem_Malloc(size_t sz) noexcept {
......@@ -1043,7 +1044,8 @@ extern "C" void* PyMem_Realloc(void* ptr, size_t sz) noexcept {
}
extern "C" void PyMem_Free(void* ptr) noexcept {
gc_compat_free(ptr);
// In Pyston, everything is GC'ed and we shouldn't explicitely free memory.
// Only the GC knows for sure that an object is no longer referenced.
}
extern "C" int PyOS_snprintf(char* str, size_t size, const char* format, ...) noexcept {
......
import gc
from testing_helpers import test_gc
# This tests the edge case where a garbage collection gets triggered inside
# a finalizer. Finalizers can allocate objects so this can definitely happen
# in practice.
indices = {}
class GCCaller(object):
def __del__(self):
gc.collect()
class ObjWithFinalizer(object):
def __init__(self, index):
self.index = index
def __del__(self):
global indices
indices[self.index] = True
def scope():
for _ in xrange(200):
for i in xrange(20):
obj = ObjWithFinalizer(i)
caller = GCCaller()
test_gc(scope)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment