Skip to content

Commit 999d5ef

Browse files
committed
Move marking to C call exit to reduce VALUE lifetimes.
1 parent 47b44a4 commit 999d5ef

File tree

11 files changed

+169
-236
lines changed

11 files changed

+169
-236
lines changed

lib/truffle/truffle/cext.rb

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ module Truffle::CExt
1818
DATA_TYPE = Primitive.object_hidden_var_create :data_type
1919
DATA_HOLDER = Primitive.object_hidden_var_create :data_holder
2020
DATA_MEMSIZER = Primitive.object_hidden_var_create :data_memsizer
21+
DATA_MARKER = Primitive.object_hidden_var_create :data_marker
2122
RB_TYPE = Primitive.object_hidden_var_create :rb_type
2223
ALLOCATOR_FUNC = Primitive.object_hidden_var_create :allocator_func
2324
RB_IO_STRUCT = Primitive.object_hidden_var_create :rb_io_struct
@@ -1456,6 +1457,11 @@ def rb_set_end_proc(func, data)
14561457
at_exit { Primitive.call_with_c_mutex_and_frame(func, [data], Primitive.caller_special_variables_if_available, nil) }
14571458
end
14581459

1460+
def define_marker(object, marker)
1461+
Primitive.object_hidden_var_set object, DATA_MARKER, marker
1462+
Primitive.cext_mark_object_on_call_exit(object) unless Truffle::Interop.null?(marker)
1463+
end
1464+
14591465
def rb_data_object_wrap(ruby_class, data, mark, free)
14601466
ruby_class = Object unless ruby_class
14611467
object = ruby_class.__send__(:__layout_allocate__)
@@ -1464,7 +1470,7 @@ def rb_data_object_wrap(ruby_class, data, mark, free)
14641470

14651471
Primitive.object_space_define_data_finalizer object, free, data_holder unless Truffle::Interop.null?(free)
14661472

1467-
define_marker object, data_marker(mark, data_holder) unless Truffle::Interop.null?(mark)
1473+
define_marker object, mark
14681474

14691475
object
14701476
end
@@ -1479,19 +1485,21 @@ def rb_data_typed_object_wrap(ruby_class, data, data_type, mark, free, size)
14791485

14801486
Primitive.object_space_define_data_finalizer object, free, data_holder unless Truffle::Interop.null?(free)
14811487

1482-
define_marker object, data_marker(mark, data_holder) unless Truffle::Interop.null?(mark)
1488+
define_marker object, mark
1489+
14831490
object
14841491
end
14851492

1486-
def data_marker(mark, data_holder)
1487-
raise unless mark.respond_to?(:call)
1488-
proc { |obj|
1493+
def run_marker(obj)
1494+
mark = Primitive.object_hidden_var_get obj, DATA_MARKER
1495+
unless Truffle::Interop.null?(mark)
14891496
create_mark_list(obj)
1497+
data_holder = Primitive.object_hidden_var_get obj, DATA_HOLDER
14901498
data = Primitive.data_holder_get_data(data_holder)
14911499
# This call is done without pushing a new frame as the marking service manages frames itself.
14921500
Primitive.call_with_c_mutex(mark, [data]) unless Truffle::Interop.null?(data)
14931501
set_mark_list_on_object(obj)
1494-
}
1502+
end
14951503
end
14961504

14971505
def data_sizer(sizer, data_holder)

lib/truffle/truffle/cext_structs.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ def polyglot_members(internal)
6868
def polyglot_read_member(name)
6969
case name
7070
when 'data'
71+
Primitive.cext_mark_object_on_call_exit(@object) unless Primitive.object_hidden_var_get(@object, Truffle::CExt::DATA_MARKER).nil?
7172
Primitive.data_holder_get_data(@data_holder)
7273
when 'type'
7374
type

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ public RubyContext(RubyLanguage language, TruffleLanguage.Env env) {
189189
featureLoader = new FeatureLoader(this, language);
190190
referenceProcessor = new ReferenceProcessor(this);
191191
finalizationService = new FinalizationService(referenceProcessor);
192-
markingService = new MarkingService(referenceProcessor);
192+
markingService = new MarkingService();
193193
dataObjectFinalizationService = new DataObjectFinalizationService(language, referenceProcessor);
194194

195195
// We need to construct this at runtime

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

Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@
108108
import org.truffleruby.language.objects.MetaClassNode;
109109
import org.truffleruby.language.objects.WriteObjectFieldNode;
110110
import org.truffleruby.language.supercall.CallSuperMethodNode;
111-
import org.truffleruby.language.yield.CallBlockNode;
112111
import org.truffleruby.parser.IdentifierType;
113112

114113
import com.oracle.truffle.api.CompilerDirectives;
@@ -167,6 +166,8 @@ private static long getNativeStringCapacity(Pointer pointer) {
167166
public abstract static class CallWithCExtLockAndFrameNode extends PrimitiveArrayArgumentsNode {
168167

169168
@Child protected CallWithCExtLockNode callCextNode = CallWithCExtLockNodeFactory.create(RubyNode.EMPTY_ARRAY);
169+
@Child protected MarkingServiceNodes.RunMarkOnExitNode runMarksNode = MarkingServiceNodes.RunMarkOnExitNode
170+
.create();
170171

171172
@Specialization
172173
protected Object callWithCExtLockAndFrame(
@@ -175,7 +176,11 @@ protected Object callWithCExtLockAndFrame(
175176
final boolean keywordsGiven = RubyArguments.getDescriptor(frame) instanceof KeywordArgumentsDescriptor;
176177
extensionStack.push(keywordsGiven, specialVariables, block);
177178
try {
178-
return callCextNode.execute(receiver, argsArray);
179+
try {
180+
return callCextNode.execute(receiver, argsArray);
181+
} finally {
182+
runMarksNode.execute(extensionStack);
183+
}
179184
} finally {
180185
extensionStack.pop();
181186
}
@@ -185,13 +190,17 @@ protected Object callWithCExtLockAndFrame(
185190
@Primitive(name = "call_with_c_mutex_and_frame_and_unwrap")
186191
public abstract static class CallWithCExtLockAndFrameAndUnwrapNode extends PrimitiveArrayArgumentsNode {
187192

193+
@Child protected MarkingServiceNodes.RunMarkOnExitNode runMarksNode = MarkingServiceNodes.RunMarkOnExitNode
194+
.create();
195+
188196
@Specialization(limit = "getCacheLimit()")
189197
protected Object callWithCExtLockAndFrame(
190198
VirtualFrame frame, Object receiver, RubyArray argsArray, Object specialVariables, Object block,
191199
@CachedLibrary("receiver") InteropLibrary receivers,
192200
@Cached ArrayToObjectArrayNode arrayToObjectArrayNode,
193201
@Cached TranslateInteropExceptionNode translateInteropExceptionNode,
194202
@Cached ConditionProfile ownedProfile,
203+
@Cached MarkingServiceNodes.RunMarkOnExitNode runMarksNode,
195204
@Cached UnwrapNode unwrapNode) {
196205
final ExtensionCallStack extensionStack = getLanguage().getCurrentFiber().extensionCallStack;
197206
final boolean keywordsGiven = RubyArguments.getDescriptor(frame) instanceof KeywordArgumentsDescriptor;
@@ -210,13 +219,18 @@ protected Object callWithCExtLockAndFrame(
210219
return unwrapNode.execute(
211220
InteropNodes.execute(receiver, args, receivers, translateInteropExceptionNode));
212221
} finally {
222+
runMarksNode.execute(extensionStack);
213223
if (!owned) {
214224
MutexOperations.unlockInternal(lock);
215225
}
216226
}
217227
} else {
218-
return unwrapNode
219-
.execute(InteropNodes.execute(receiver, args, receivers, translateInteropExceptionNode));
228+
try {
229+
return unwrapNode.execute(
230+
InteropNodes.execute(receiver, args, receivers, translateInteropExceptionNode));
231+
} finally {
232+
runMarksNode.execute(extensionStack);
233+
}
220234
}
221235

222236
} finally {
@@ -361,6 +375,18 @@ protected Object publicSendWithoutLock(
361375
}
362376
}
363377

378+
@Primitive(name = "cext_mark_object_on_call_exit")
379+
public abstract static class MarkObjectOnCallExit extends PrimitiveArrayArgumentsNode {
380+
381+
@Specialization
382+
protected Object markOnCallExit(Object object,
383+
@Cached WrapNode wrapNode,
384+
@Cached MarkingServiceNodes.QueueForMarkOnExitNode markOnExitNode) {
385+
markOnExitNode.execute(wrapNode.execute(object));
386+
return nil;
387+
}
388+
}
389+
364390
@CoreMethod(names = "cext_start_new_handle_block", onSingleton = true)
365391
public abstract static class StartNewHandleBlockNode extends CoreMethodArrayArgumentsNode {
366392
@Specialization
@@ -1677,6 +1703,7 @@ public abstract static class NewMarkerList extends CoreMethodArrayArgumentsNode
16771703
protected Object createNewMarkList(RubyDynamicObject object,
16781704
@CachedLibrary("object") DynamicObjectLibrary objectLibrary) {
16791705
getContext().getMarkingService().startMarking(
1706+
getLanguage().getCurrentThread().getCurrentFiber().extensionCallStack,
16801707
(Object[]) objectLibrary.getOrDefault(object, Layouts.MARKED_OBJECTS_IDENTIFIER, null));
16811708
return nil;
16821709
}
@@ -1692,7 +1719,8 @@ protected Object addToMarkList(Object markedObject,
16921719
ValueWrapper wrappedValue = toWrapperNode.execute(markedObject);
16931720
if (wrappedValue != null) {
16941721
noExceptionProfile.enter();
1695-
getContext().getMarkingService().addMark(wrappedValue);
1722+
getContext().getMarkingService()
1723+
.addMark(getLanguage().getCurrentThread().getCurrentFiber().extensionCallStack, wrappedValue);
16961724
}
16971725
// We do nothing here if the handle cannot be resolved. If we are marking an object
16981726
// which is only reachable via weak refs then the handles of objects it is itself
@@ -1729,29 +1757,12 @@ protected Object setMarkList(RubyDynamicObject structOwner,
17291757
writeMarkedNode.execute(
17301758
structOwner,
17311759
Layouts.MARKED_OBJECTS_IDENTIFIER,
1732-
getContext().getMarkingService().finishMarking());
1760+
getContext().getMarkingService()
1761+
.finishMarking(getLanguage().getCurrentThread().getCurrentFiber().extensionCallStack));
17331762
return nil;
17341763
}
17351764
}
17361765

1737-
@CoreMethod(names = "define_marker", onSingleton = true, required = 2)
1738-
public abstract static class CreateMarkerNode extends CoreMethodArrayArgumentsNode {
1739-
1740-
@TruffleBoundary
1741-
@Specialization
1742-
protected Object createMarker(RubyDynamicObject object, RubyProc marker) {
1743-
/* The code here has to be a little subtle. The marker must be associated with the object it will act on,
1744-
* but the lambda must not capture the object (and prevent garbage collection). So the marking function is a
1745-
* lambda that will take the object as an argument 'o' which will be provided when the marking function is
1746-
* called by the marking service. */
1747-
getContext()
1748-
.getMarkingService()
1749-
.addMarker(object, (o) -> CallBlockNode.getUncached().yield(marker, o));
1750-
return nil;
1751-
}
1752-
1753-
}
1754-
17551766
@CoreMethod(names = "rb_thread_check_ints", onSingleton = true, required = 0)
17561767
public abstract static class CheckThreadInterrupt extends CoreMethodArrayArgumentsNode {
17571768

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

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,6 @@ public void freeAllBlocksInMap(RubyLanguage language) {
154154
}
155155

156156
public void cleanup(RubyContext context, HandleBlockHolder holder) {
157-
context.getMarkingService().queueForMarking(holder.handleBlock);
158157
holder.handleBlock = null;
159158
}
160159

@@ -214,7 +213,7 @@ public static class HandleBlock {
214213
private static final Set<HandleBlock> keepAlive = ConcurrentHashMap.newKeySet();
215214

216215
private final long base;
217-
@SuppressWarnings("rawtypes") private final ValueWrapper[] wrappers;
216+
private final ValueWrapperWeakReference[] wrappers;
218217
private int count;
219218

220219
@SuppressWarnings("unused") private Cleanable cleanable;
@@ -232,7 +231,7 @@ public HandleBlock(RubyContext context, RubyLanguage language, ValueWrapperManag
232231
keepAlive(this);
233232
}
234233
this.base = base;
235-
this.wrappers = new ValueWrapper[BLOCK_SIZE];
234+
this.wrappers = new ValueWrapperWeakReference[BLOCK_SIZE];
236235
this.count = 0;
237236
this.cleanable = language.cleaner.register(this, HandleBlock.makeCleaner(manager, base, allocator));
238237
}
@@ -259,7 +258,7 @@ public int getIndex() {
259258

260259
public ValueWrapper getWrapper(long handle) {
261260
int offset = (int) (handle & OFFSET_MASK) >> TAG_BITS;
262-
return wrappers[offset];
261+
return wrappers[offset].get();
263262
}
264263

265264
public boolean isFull() {
@@ -269,7 +268,7 @@ public boolean isFull() {
269268
public long setHandleOnWrapper(ValueWrapper wrapper) {
270269
long handle = getBase() + count * Pointer.SIZE;
271270
wrapper.setHandle(handle, this);
272-
wrappers[count] = wrapper;
271+
wrappers[count] = new ValueWrapperWeakReference(wrapper);
273272
count++;
274273
return handle;
275274
}
@@ -285,6 +284,12 @@ public static final class HandleBlockWeakReference extends WeakReference<HandleB
285284
}
286285
}
287286

287+
public static final class ValueWrapperWeakReference extends WeakReference<ValueWrapper> {
288+
ValueWrapperWeakReference(ValueWrapper referent) {
289+
super(referent);
290+
}
291+
}
292+
288293
public static class HandleBlockHolder {
289294
protected HandleBlock handleBlock = null;
290295
protected HandleBlock sharedHandleBlock = null;
@@ -333,9 +338,6 @@ protected long allocateHandle(ValueWrapper wrapper, RubyContext context, RubyLan
333338
}
334339

335340
if (block == null || block.isFull()) {
336-
if (block != null) {
337-
context.getMarkingService().queueForMarking(block);
338-
}
339341
if (shared) {
340342
block = context.getValueWrapperManager().addToSharedBlockMap(context, language);
341343
holder.sharedHandleBlock = block;
@@ -361,9 +363,6 @@ public static HandleBlock allocateNewBlock(RubyContext context, RubyLanguage lan
361363
HandleBlockHolder holder = getBlockHolder(context, language);
362364
HandleBlock block = holder.handleBlock;
363365

364-
if (block != null) {
365-
context.getMarkingService().queueForMarking(block);
366-
}
367366
block = context.getValueWrapperManager().addToBlockMap(context, language);
368367

369368
holder.handleBlock = block;

src/main/java/org/truffleruby/core/GCNodes.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ public abstract static class GCStartPrimitiveNode extends PrimitiveArrayArgument
4242
@TruffleBoundary
4343
@Specialization
4444
protected Object vmGCStart() {
45-
getContext().getMarkingService().queueMarking();
4645
System.gc();
4746
return nil;
4847
}
@@ -64,8 +63,6 @@ public abstract static class GCForce extends PrimitiveArrayArgumentsNode {
6463
@TruffleBoundary
6564
@Specialization
6665
protected Object force() {
67-
getContext().getMarkingService().queueMarking();
68-
6966
// NOTE(norswap, 16 Apr 20): We could have used a WeakReference here, but the hope is that the extra
7067
// indirection will prevent the compiler to optimize this method away (assuming JIT compilation, and
7168
// the fact that such an optimizaton is permitted and possible, both of which are unlikely).

src/main/java/org/truffleruby/core/MarkerReference.java

Lines changed: 0 additions & 27 deletions
This file was deleted.

0 commit comments

Comments
 (0)