Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions src/hx/cppia/CppiaModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,39 @@ class CppiaObject : public hx::CppiaLoadedModule_obj
};


/*
A loaded module is an ordinary GC object, so a generational collection stops traversing it once it
is old. What it holds can still change after that: a literal boxes itself on first use, the JIT
embeds that box in its code, a script static is assigned. None of those writes has a barrier, so a
minor collection freed the young object while the old module still pointed at it.

So every loaded module is marked on every collection, the way class statics are.
*/
void scriptMarkModules(hx::MarkContext *__inCtx)
{
if (!gAllCppiaModules.mPtr)
return;
for(int i=0;i<gAllCppiaModules->length;i++)
{
CppiaObject *module = static_cast<CppiaObject *>(gAllCppiaModules[i].mPtr);
if (module)
module->cppia->mark(__inCtx);
}
}

#ifdef HXCPP_VISIT_ALLOCS
void scriptVisitModules(hx::VisitContext *__inCtx)
{
if (!gAllCppiaModules.mPtr)
return;
for(int i=0;i<gAllCppiaModules->length;i++)
{
CppiaObject *module = static_cast<CppiaObject *>(gAllCppiaModules[i].mPtr);
if (module)
module->cppia->visit(__inCtx);
}
}
#endif


CppiaLoadedModule LoadCppia(const unsigned char *inData, int inDataLength)
Expand Down
10 changes: 10 additions & 0 deletions src/hx/gc/Immix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,10 @@ DECLARE_FAST_TLS_DATA(StackContext, tlsStackContext);

#ifdef HXCPP_SCRIPTABLE
extern void scriptMarkStack(hx::MarkContext *);
extern void scriptMarkModules(hx::MarkContext *);
#ifdef HXCPP_VISIT_ALLOCS
extern void scriptVisitModules(hx::VisitContext *);
#endif
#endif
}

Expand Down Expand Up @@ -4268,6 +4272,9 @@ class GlobalAllocator
VisitLocalAlloc(mLocalAllocs[i], inCtx);

hx::VisitClassStatics(inCtx);
#ifdef HXCPP_SCRIPTABLE
hx::scriptVisitModules(inCtx);
#endif

for(hx::RootSet::iterator i = hx::sgRootSet.begin(); i!=hx::sgRootSet.end(); ++i)
{
Expand Down Expand Up @@ -4712,6 +4719,9 @@ class GlobalAllocator
mMarker.init();

hx::MarkClassStatics(&mMarker);
#ifdef HXCPP_SCRIPTABLE
hx::scriptMarkModules(&mMarker);
#endif

{
hx::AutoMarkPush info(&mMarker,"Roots","root");
Expand Down
18 changes: 18 additions & 0 deletions test/cppia/Client.hx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,24 @@ class ClientFoo implements IFoo {
}
}

class ClientModuleValues {

static var kept:Array<String>;

// Read as Dynamic, the literal is boxed on first use, and the module keeps the box.
public static function literal():Dynamic {
var boxed:Dynamic = "a string literal";
return boxed;
}

// Assigned after load: the module holds the only reference.
public static function assigned():Dynamic {
if (kept == null)
kept = ["assigned after load"];
return kept;
}
}

class Client
{
public static var clientBool0 = true;
Expand Down
23 changes: 23 additions & 0 deletions test/cppia/cases/TestCommon.hx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package cases;

import cpp.cppia.Host;
import cpp.vm.Gc;
import cpp.vm.WeakRef;
import utest.Test;
import utest.Assert;

Expand Down Expand Up @@ -59,6 +61,27 @@ class TestCommon extends Test {
Assert.equals(2, Common.callbackSet, 'Bad cppia closure');
}

@:depends(testStatus)
function testModuleValuesCreatedWhenOld() {
final values = Type.resolveClass('ClientModuleValues');

// Full collections make the module old: a generational collection no longer traverses it.
for (_ in 0...3) {
Gc.run(true);
}
final created = createValues(values);
Gc.run(false);

for (value in created) {
Assert.notNull(value.get(), 'Freed a value that the old module holds');
}
}

// Only weak references to the values leave this frame, so that the stack does not keep them.
static function createValues(values:Dynamic):Array<WeakRef<Dynamic>> {
return [for (name in ['literal', 'assigned']) new WeakRef<Dynamic>(Reflect.callMethod(values, Reflect.field(values, name), []))];
}

@:depends(testStatus)
function testInterfaceCalling() {
final obj : IFoo = Type.createInstance(Type.resolveClass('ClientFoo'), []);
Expand Down
1 change: 1 addition & 0 deletions test/cppia/compile-host.hxml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
-m CppiaHost
HostExtendedRoot
-D scriptable
-D HXCPP_GC_GENERATIONAL
-D dll_export=host_classes.info
-L utest
--dce no
Expand Down