Skip to content

Commit b9d7479

Browse files
committed
Add GC marking.
PullRequest: truffleruby/500
2 parents 94c15e8 + a98f280 commit b9d7479

File tree

9 files changed

+498
-112
lines changed

9 files changed

+498
-112
lines changed

lib/truffle/truffle/cext.rb

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1344,16 +1344,18 @@ def rb_data_object_wrap(ruby_class, data, mark, free)
13441344
data_holder = DataHolder.new(data)
13451345
hidden_variable_set object, :data_holder, data_holder
13461346
ObjectSpace.define_finalizer object, data_finalizer(free, data_holder) unless free.nil?
1347+
define_marker object, data_marker(mark, data_holder) unless mark.nil?
13471348
object
13481349
end
13491350

1350-
def rb_data_typed_object_wrap(ruby_class, data, data_type, free)
1351+
def rb_data_typed_object_wrap(ruby_class, data, data_type, mark, free)
13511352
ruby_class = Object if Truffle::Interop.null?(ruby_class)
13521353
object = BASIC_OBJECT_ALLOCATE.bind(ruby_class).call
13531354
data_holder = DataHolder.new(data)
13541355
hidden_variable_set object, :data_type, data_type
13551356
hidden_variable_set object, :data_holder, data_holder
13561357
ObjectSpace.define_finalizer object, data_finalizer(free, data_holder) unless free.nil?
1358+
define_marker object, data_marker(mark, data_holder) unless mark.nil?
13571359
object
13581360
end
13591361

@@ -1365,6 +1367,16 @@ def data_finalizer(free, data_holder)
13651367
}
13661368
end
13671369

1370+
def data_marker(mark, data_holder)
1371+
# In a separate method to avoid capturing the object
1372+
raise unless mark.respond_to?(:call)
1373+
proc { |obj|
1374+
create_mark_list
1375+
execute_with_mutex(mark, data_holder.data)
1376+
set_mark_list_on_object(obj)
1377+
}
1378+
end
1379+
13681380
def rb_ruby_verbose_ptr
13691381
$VERBOSE
13701382
end

src/main/c/cext/ruby.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2691,7 +2691,7 @@ VALUE rb_data_object_alloc_managed(VALUE klass, size_t size, RUBY_DATA_FUNC dmar
26912691

26922692
VALUE rb_data_typed_object_wrap(VALUE ruby_class, void *data, const rb_data_type_t *data_type) {
26932693
return (VALUE) polyglot_invoke(RUBY_CEXT, "rb_data_typed_object_wrap",
2694-
ruby_class, data, data_type, rb_tr_free_function(data_type->function.dfree));
2694+
ruby_class, data, data_type, data_type->function.dmark, rb_tr_free_function(data_type->function.dfree));
26952695
}
26962696

26972697
VALUE rb_data_typed_object_zalloc(VALUE ruby_class, size_t size, const rb_data_type_t *data_type) {

src/main/java/org/truffleruby/Layouts.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ public abstract class Layouts {
105105
public static final HiddenKey FROZEN_IDENTIFIER = new HiddenKey("frozen?");
106106
public static final HiddenKey ASSOCIATED_IDENTIFIER = new HiddenKey("associated");
107107
public static final HiddenKey FINALIZER_REF_IDENTIFIER = new HiddenKey("finalizerRef");
108+
public static final HiddenKey MARKED_OBJECTS_IDENTIFIER = new HiddenKey("marked_objects");
108109
public static final HiddenKey VALUE_WRAPPER_IDENTIFIER = new HiddenKey("value_wrapper");
109110

110111
// Generated layouts

src/main/java/org/truffleruby/RubyContext.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.truffleruby.core.CoreLibrary;
2929
import org.truffleruby.core.FinalizationService;
3030
import org.truffleruby.core.Hashing;
31+
import org.truffleruby.core.MarkingService;
3132
import org.truffleruby.core.array.ArrayOperations;
3233
import org.truffleruby.core.encoding.EncodingManager;
3334
import org.truffleruby.core.exception.CoreExceptions;
@@ -107,6 +108,7 @@ public class RubyContext {
107108
private final FeatureLoader featureLoader = new FeatureLoader(this);
108109
private final TraceManager traceManager;
109110
private final FinalizationService finalizationService = new FinalizationService(this);
111+
private final MarkingService markingService = new MarkingService(this);
110112
private final ObjectSpaceManager objectSpaceManager = new ObjectSpaceManager(this);
111113
private final SharedObjects sharedObjects = new SharedObjects(this);
112114
private final AtExitManager atExitManager = new AtExitManager(this);
@@ -564,6 +566,10 @@ public FinalizationService getFinalizationService() {
564566
return finalizationService;
565567
}
566568

569+
public MarkingService getMarkingService() {
570+
return markingService;
571+
}
572+
567573
public ObjectSpaceManager getObjectSpaceManager() {
568574
return objectSpaceManager;
569575
}

src/main/java/org/truffleruby/cext/CExtNodes.java

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.jcodings.Encoding;
3131
import org.jcodings.specific.UTF8Encoding;
3232
import org.truffleruby.Layouts;
33+
import org.truffleruby.RubyContext;
3334
import org.truffleruby.builtins.CoreClass;
3435
import org.truffleruby.builtins.CoreMethod;
3536
import org.truffleruby.builtins.CoreMethodArrayArgumentsNode;
@@ -75,6 +76,7 @@
7576
import org.truffleruby.language.control.JavaException;
7677
import org.truffleruby.language.control.RaiseException;
7778
import org.truffleruby.language.dispatch.CallDispatchHeadNode;
79+
import org.truffleruby.language.dispatch.DoesRespondDispatchHeadNode;
7880
import org.truffleruby.language.methods.DeclarationContext;
7981
import org.truffleruby.language.methods.InternalMethod;
8082
import org.truffleruby.language.objects.InitializeClassNode;
@@ -85,6 +87,8 @@
8587
import org.truffleruby.language.objects.ObjectIVarGetNodeGen;
8688
import org.truffleruby.language.objects.ObjectIVarSetNode;
8789
import org.truffleruby.language.objects.ObjectIVarSetNodeGen;
90+
import org.truffleruby.language.objects.WriteObjectFieldNode;
91+
import org.truffleruby.language.objects.WriteObjectFieldNodeGen;
8892
import org.truffleruby.language.supercall.CallSuperMethodNode;
8993
import org.truffleruby.parser.Identifiers;
9094

@@ -1295,4 +1299,121 @@ protected UnwrapNode createUnwrapNode() {
12951299
}
12961300
}
12971301

1302+
private static ThreadLocal<ArrayList<Object>> markList = new ThreadLocal<>();
1303+
1304+
@CoreMethod(names = "create_mark_list", onSingleton = true, required = 0)
1305+
public abstract static class NewMarkerList extends CoreMethodArrayArgumentsNode {
1306+
1307+
@Specialization
1308+
public DynamicObject createNewMarkList(VirtualFrame frmae) {
1309+
setThreadLocal();
1310+
return nil();
1311+
}
1312+
1313+
@TruffleBoundary
1314+
protected void setThreadLocal() {
1315+
markList.set(new ArrayList<>());
1316+
}
1317+
}
1318+
1319+
@CoreMethod(names = "rb_gc_mark", onSingleton = true, required = 1)
1320+
public abstract static class AddToMarkList extends CoreMethodArrayArgumentsNode {
1321+
1322+
@Specialization
1323+
public DynamicObject addToMarkList(VirtualFrame frmae, Object markedObject,
1324+
@Cached("create()") BranchProfile exceptionProfile,
1325+
@Cached("create()") BranchProfile noExceptionProfile,
1326+
@Cached("createUnwrapNode()") UnwrapNode unwrapNode) {
1327+
Object unwrappedValue = unwrapNode.execute(markedObject);
1328+
if (unwrappedValue != null) {
1329+
noExceptionProfile.enter();
1330+
getList().add(unwrappedValue);
1331+
}
1332+
// We do nothing here if the handle cannot be resolved. If we are marking an object
1333+
// which is only reachable via weak refs then the handles of objects it is iteself
1334+
// marking may have already been removed from the handle map. }
1335+
return nil();
1336+
}
1337+
1338+
@TruffleBoundary
1339+
protected ArrayList<Object> getList() {
1340+
return markList.get();
1341+
}
1342+
1343+
protected UnwrapNode createUnwrapNode() {
1344+
return UnwrapNodeGen.create();
1345+
}
1346+
}
1347+
1348+
@CoreMethod(names = "set_mark_list_on_object", onSingleton = true, required = 1)
1349+
public abstract static class SetMarkList extends CoreMethodArrayArgumentsNode {
1350+
1351+
@Specialization
1352+
public DynamicObject setMarkList(VirtualFrame frame, DynamicObject structOwwer,
1353+
@Cached("createWriter()") WriteObjectFieldNode writeMarkedNode) {
1354+
writeMarkedNode.write(structOwwer, getArray());
1355+
return nil();
1356+
}
1357+
1358+
@TruffleBoundary
1359+
protected Object[] getArray() {
1360+
return markList.get().toArray();
1361+
}
1362+
1363+
public WriteObjectFieldNode createWriter() {
1364+
return WriteObjectFieldNodeGen.create(Layouts.MARKED_OBJECTS_IDENTIFIER);
1365+
}
1366+
}
1367+
1368+
@CoreMethod(names = "define_marker", onSingleton = true, required = 2)
1369+
public abstract static class CreateMarkerNode extends CoreMethodArrayArgumentsNode {
1370+
1371+
@Child private DoesRespondDispatchHeadNode respondToCallNode = DoesRespondDispatchHeadNode.create();
1372+
1373+
@Specialization
1374+
public DynamicObject createMarker(VirtualFrame frame, DynamicObject object, DynamicObject marker,
1375+
@Cached("create()") BranchProfile errorProfile) {
1376+
if (respondToCallNode.doesRespondTo(frame, "call", marker)) {
1377+
addObjectToMarkingService(object, marker);
1378+
return nil();
1379+
} else {
1380+
errorProfile.enter();
1381+
throw new RaiseException(getContext(), coreExceptions().argumentErrorWrongArgumentType(marker, "callable", this));
1382+
}
1383+
}
1384+
1385+
@TruffleBoundary
1386+
protected void addObjectToMarkingService(DynamicObject object, DynamicObject marker) {
1387+
RubyContext aContext = getContext();
1388+
/*
1389+
* The code here has to be a little subtle. The marker must be associated with the
1390+
* object it will act on, but the lambda must not capture the object (and prevent
1391+
* garbage collection). So the marking function is a lambda that will take the
1392+
* object as an argument 'o' which will be provided when the marking function is
1393+
* called by the marking service.
1394+
*/
1395+
getContext().getMarkingService().addMarker(object, (o) -> aContext.send(marker, "call", o));
1396+
}
1397+
}
1398+
1399+
@CoreMethod(names = "push_preserving_frame", onSingleton = true, required = 0)
1400+
public abstract static class PushPreservingFrame extends CoreMethodArrayArgumentsNode {
1401+
1402+
@Specialization
1403+
public DynamicObject pushFrame(VirtualFrame frame) {
1404+
getContext().getMarkingService().pushStackPreservationFrame();
1405+
return nil();
1406+
}
1407+
}
1408+
1409+
@CoreMethod(names = "pop_preserving_frame", onSingleton = true, required = 0)
1410+
public abstract static class PopPreservingFrame extends CoreMethodArrayArgumentsNode {
1411+
1412+
@Specialization
1413+
public DynamicObject popFrame(VirtualFrame frame) {
1414+
getContext().getMarkingService().popStackPreservationFrame();
1415+
return nil();
1416+
}
1417+
}
1418+
12981419
}

src/main/java/org/truffleruby/cext/ValueWrapperManager.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ public synchronized long createNativeHandle(DynamicObject wrapper) {
9292
}
9393
Layouts.VALUE_WRAPPER.setHandle(wrapper, handleAddress);
9494
addToHandleMap(handleAddress, wrapper);
95+
context.getMarkingService().keepObject(wrapper);
9596
addFinalizer(wrapper, handlePointer);
9697
return handleAddress;
9798
}

0 commit comments

Comments
 (0)