Commit ee247d3e authored by Kevin Modzelewski's avatar Kevin Modzelewski

Use the ICInvalidator to invalidate embedded references to BoxedFunctions when they get freed

parent c3fc031e
......@@ -457,6 +457,7 @@ struct _typeobject {
char _dep_getattrs[56]; // FIXME: this is hardcoding the size of this particular implementation of std::unordered_map
char _ics[32];
void* _gcvisit_func;
void* _dtor;
int _attrs_offset;
bool _flags[2];
};
......
......@@ -300,7 +300,10 @@ static void _doFree(GCAllocation* al) {
if (al->kind_id == GCKind::PYTHON) {
Box* b = (Box*)al->user_data;
ASSERT(b->cls->tp_dealloc == NULL, "%s", getTypeName(b));
if (b->cls->simple_destructor)
b->cls->simple_destructor(b);
}
}
......
......@@ -298,8 +298,8 @@ void BoxedClass::freeze() {
BoxedClass::BoxedClass(BoxedClass* base, gcvisit_func gc_visit, int attrs_offset, int instance_size,
bool is_user_defined)
: BoxVar(0), gc_visit(gc_visit), attrs_offset(attrs_offset), is_constant(false), is_user_defined(is_user_defined),
is_pyston_class(true) {
: BoxVar(0), gc_visit(gc_visit), simple_destructor(NULL), attrs_offset(attrs_offset), is_constant(false),
is_user_defined(is_user_defined), is_pyston_class(true) {
// Zero out the CPython tp_* slots:
memset(&tp_name, 0, (char*)(&tp_version_tag + 1) - (char*)(&tp_name));
......@@ -2400,6 +2400,7 @@ Box* callFunc(BoxedFunctionBase* func, CallRewriteArgs* rewrite_args, ArgPassSpe
} else {
rewrite_args->obj->addGuard((intptr_t)func);
}
rewrite_args->rewriter->addDependenceOn(func->dependent_ics);
}
}
......
......@@ -304,7 +304,6 @@ extern "C" BoxedFunctionBase::BoxedFunctionBase(CLFunction* f, std::initializer_
assert(f->num_defaults == ndefaults);
}
// This probably belongs in dict.cpp?
extern "C" void functionGCHandler(GCVisitor* v, Box* b) {
boxGCHandler(v, b);
......@@ -324,6 +323,14 @@ extern "C" void functionGCHandler(GCVisitor* v, Box* b) {
}
}
static void functionDtor(Box* b) {
assert(isSubclass(b->cls, function_cls) || isSubclass(b->cls, builtin_function_or_method_cls));
BoxedFunctionBase* self = static_cast<BoxedFunctionBase*>(b);
self->dependent_ics.invalidateAll();
self->dependent_ics.~ICInvalidator();
}
BoxedModule::BoxedModule(const std::string& name, const std::string& fn) : fn(fn) {
this->giveAttr("__name__", boxString(name));
this->giveAttr("__file__", boxString(fn));
......@@ -1144,6 +1151,8 @@ void setupRuntime() {
builtin_function_or_method_cls
= new BoxedHeapClass(object_cls, &functionGCHandler, offsetof(BoxedBuiltinFunctionOrMethod, attrs),
sizeof(BoxedBuiltinFunctionOrMethod), false, "builtin_function_or_method");
function_cls->simple_destructor = builtin_function_or_method_cls->simple_destructor = functionDtor;
instancemethod_cls = new BoxedHeapClass(object_cls, &instancemethodGCHandler, 0, sizeof(BoxedInstanceMethod), false,
"instancemethod");
list_cls = new BoxedHeapClass(object_cls, &listGCHandler, 0, sizeof(BoxedList), false, "list");
......
......@@ -196,6 +196,13 @@ public:
gcvisit_func gc_visit;
// A "simple" destructor -- one that is allowed to be called at any point after the object is dead.
// In particular, this means that it can't touch any Python objects or other gc-managed memory,
// since it will be in an undefined state.
// (Context: in Python destructors are supposed to be called in topological order, due to reference counting.
// We don't support that yet, but still want some simple ability to run code when an object gets freed.)
void (*simple_destructor)(Box*);
// Offset of the HCAttrs object or 0 if there are no hcattrs.
// Analogous to tp_dictoffset
const int attrs_offset;
......@@ -447,6 +454,8 @@ public:
int ndefaults;
GCdArray* defaults;
ICInvalidator dependent_ics;
// Accessed via member descriptor
Box* modname; // __module__
......
# Regression test: this triggers a bug in the way we guard for boxedfunctions and their closures.
# In particular, we guard on the specific value of a BoxedFunction to say that it is the same, but
# it's possible for the function to get destructed and then a new (and different) boxedfunction to
# get allocated in the same address, which would pass the guard.
import gc
def f():
x = []
def inner():
x.append(1)
inner()
for i in xrange(30000):
if i % 1000 == 0:
print i
f()
if i % 50 == 0:
gc.collect()
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