Skip to content

Commit 79caf83

Browse files
committed
[GR-17457] Refactor reference processing and data holders to be more lightweight.
PullRequest: truffleruby/3243
2 parents fe33756 + 30c85da commit 79caf83

29 files changed

+559
-237
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ Compatibility:
2222
Performance:
2323

2424
* Reimplement `Float#to_s` for better performance (#1584, @aardvark179).
25+
* Improve reference processing by making C object free functions and other finalizers more lightweight (@aardvark179).
26+
* Improve performance of `RSTRING_PTR` for interned strings (@aardvark179).
2527

2628
Changes:
2729

lib/truffle/truffle/cext.rb

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ def rb_type(value)
183183

184184
def rb_tr_cached_type(value, type)
185185
if type == T_NONE
186-
if Primitive.object_hidden_var_get(value, DATA_HOLDER)
186+
if Primitive.data_holder_is_holder?(Primitive.object_hidden_var_get(value, DATA_HOLDER))
187187
T_DATA
188188
else
189189
T_OBJECT
@@ -1432,44 +1432,35 @@ def rb_set_end_proc(func, data)
14321432
def rb_data_object_wrap(ruby_class, data, mark, free)
14331433
ruby_class = Object unless ruby_class
14341434
object = ruby_class.__send__(:__layout_allocate__)
1435-
data_holder = DataHolder.new(data)
1435+
data_holder = Primitive.data_holder_create(data)
14361436
Primitive.object_hidden_var_set object, DATA_HOLDER, data_holder
1437-
ObjectSpace.define_finalizer object, data_finalizer(free, data_holder) unless free.nil?
1438-
define_marker object, data_marker(mark, data_holder) unless mark.nil?
1437+
1438+
Primitive.object_space_define_data_finalizer object, free, data_holder unless Truffle::Interop.null?(free)
1439+
1440+
define_marker object, data_marker(mark, data_holder) unless Truffle::Interop.null?(mark)
1441+
14391442
object
14401443
end
14411444

14421445
def rb_data_typed_object_wrap(ruby_class, data, data_type, mark, free, size)
14431446
ruby_class = Object unless ruby_class
14441447
object = ruby_class.__send__(:__layout_allocate__)
1445-
data_holder = DataHolder.new(data)
1448+
data_holder = Primitive.data_holder_create(data)
14461449
Primitive.object_hidden_var_set object, DATA_TYPE, data_type
14471450
Primitive.object_hidden_var_set object, DATA_HOLDER, data_holder
1448-
Primitive.object_hidden_var_set object, DATA_MEMSIZER, data_sizer(size, data_holder) unless size.nil?
1451+
Primitive.object_hidden_var_set object, DATA_MEMSIZER, data_sizer(size, data_holder) unless Truffle::Interop.null?(size)
14491452

1450-
ObjectSpace.define_finalizer object, data_finalizer(free, data_holder) unless free.nil?
1453+
Primitive.object_space_define_data_finalizer object, free, data_holder unless Truffle::Interop.null?(free)
14511454

1452-
define_marker object, data_marker(mark, data_holder) unless mark.nil?
1455+
define_marker object, data_marker(mark, data_holder) unless Truffle::Interop.null?(mark)
14531456
object
14541457
end
14551458

1456-
# These data function are created in separate methods to ensure they
1457-
# will not accidentally capture the objects they operate on, which
1458-
# might prevent garbage collection.
1459-
def data_finalizer(free, data_holder)
1460-
raise unless free.respond_to?(:call)
1461-
proc {
1462-
data = data_holder.data
1463-
# This call does not require a frame as the finalisation processor will push one.
1464-
Primitive.call_with_c_mutex(free, [data]) unless Truffle::Interop.null?(data)
1465-
}
1466-
end
1467-
14681459
def data_marker(mark, data_holder)
14691460
raise unless mark.respond_to?(:call)
14701461
proc { |obj|
14711462
create_mark_list(obj)
1472-
data = data_holder.data
1463+
data = Primitive.data_holder_get_data(data_holder)
14731464
# This call is done without pushing a new frame as the marking service manages frames itself.
14741465
Primitive.call_with_c_mutex(mark, [data]) unless Truffle::Interop.null?(data)
14751466
set_mark_list_on_object(obj)
@@ -1479,7 +1470,7 @@ def data_marker(mark, data_holder)
14791470
def data_sizer(sizer, data_holder)
14801471
raise unless sizer.respond_to?(:call)
14811472
proc {
1482-
Primitive.call_with_c_mutex_and_frame(sizer, [data_holder.data], Primitive.caller_special_variables_if_available, nil)
1473+
Primitive.call_with_c_mutex_and_frame(sizer, [Primitive.data_holder_get_data(data_holder)], Primitive.caller_special_variables_if_available, nil)
14831474
}
14841475
end
14851476

lib/truffle/truffle/cext_structs.rb

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def RDATA_PTR(object)
2929
raise TypeError, "wrong argument type #{object.class} (expected T_DATA)"
3030
end
3131

32-
data_holder.data
32+
Primitive.data_holder_get_data(data_holder)
3333
end
3434

3535
def RBASIC(object)
@@ -48,15 +48,6 @@ def RFILE(file)
4848
end
4949
end
5050

51-
# Used by RData.
52-
class Truffle::CExt::DataHolder
53-
attr_accessor :data
54-
55-
def initialize(data)
56-
@data = data
57-
end
58-
end
59-
6051
# ruby.h: `struct RData` and struct `RTypedData`
6152
class Truffle::CExt::RData
6253
def initialize(object, data_holder)
@@ -77,7 +68,7 @@ def polyglot_members(internal)
7768
def polyglot_read_member(name)
7869
case name
7970
when 'data'
80-
@data_holder.data
71+
Primitive.data_holder_get_data(@data_holder)
8172
when 'type'
8273
type
8374
when 'typed_flag'
@@ -91,7 +82,7 @@ def polyglot_read_member(name)
9182

9283
def polyglot_write_member(name, value)
9384
raise Truffle::Interop::UnknownIdentifierException unless name == 'data'
94-
@data_holder.data = value
85+
Primitive.data_holder_set_data(@data_holder, value)
9586
end
9687

9788
def polyglot_remove_member(name)

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public abstract class Layouts {
2424
public static final HiddenKey OBJECT_LOCK = new HiddenKey("object_lock"); // ReentrantLock
2525
public static final HiddenKey ASSOCIATED_IDENTIFIER = new HiddenKey("associated"); // Pointer[]
2626
public static final HiddenKey FINALIZER_REF_IDENTIFIER = new HiddenKey("finalizerRef"); // FinalizerReference
27+
public static final HiddenKey DATA_OBJECT_FINALIZER_REF_IDENTIFIER = new HiddenKey("data_object_finalizerRef"); // FinalizerReference
2728
public static final HiddenKey MARKED_OBJECTS_IDENTIFIER = new HiddenKey("marked_objects"); // Object[]
2829
public static final HiddenKey VALUE_WRAPPER_IDENTIFIER = new HiddenKey("value_wrapper"); // ValueWrapper
2930
public static final HiddenKey ALLOCATION_TRACE_IDENTIFIER = new HiddenKey("allocation_trace"); // AllocationTrace

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.truffleruby.cext.ValueWrapperManager;
3939
import org.truffleruby.collections.SharedIndicesMap.ContextArray;
4040
import org.truffleruby.core.CoreLibrary;
41+
import org.truffleruby.core.DataObjectFinalizationService;
4142
import org.truffleruby.core.FinalizationService;
4243
import org.truffleruby.core.Hashing;
4344
import org.truffleruby.core.MarkingService;
@@ -56,13 +57,11 @@
5657
import org.truffleruby.core.objectspace.ObjectSpaceManager;
5758
import org.truffleruby.core.proc.ProcOperations;
5859
import org.truffleruby.core.proc.RubyProc;
59-
import org.truffleruby.core.rope.NativeRope;
6060
import org.truffleruby.core.symbol.RubySymbol;
6161
import org.truffleruby.core.thread.ThreadManager;
6262
import org.truffleruby.core.time.GetTimeZoneNode;
6363
import org.truffleruby.debug.MetricsProfiler;
6464
import org.truffleruby.language.CallStackManager;
65-
import org.truffleruby.core.string.ImmutableRubyString;
6665
import org.truffleruby.language.LexicalScope;
6766
import org.truffleruby.language.RubyBaseNode;
6867
import org.truffleruby.language.SafepointManager;
@@ -112,6 +111,7 @@ public class RubyContext {
112111
private final TraceManager traceManager;
113112
private final ReferenceProcessor referenceProcessor;
114113
private final FinalizationService finalizationService;
114+
private final DataObjectFinalizationService dataObjectFinalizationService;
115115
private final MarkingService markingService;
116116
private final ObjectSpaceManager objectSpaceManager = new ObjectSpaceManager();
117117
private final SharedObjects sharedObjects = new SharedObjects(this);
@@ -126,7 +126,6 @@ public class RubyContext {
126126
private final Map<Source, Integer> sourceLineOffsets = Collections.synchronizedMap(new WeakHashMap<>());
127127
/** (Symbol, refinements) -> Proc for Symbol#to_proc */
128128
public final Map<Pair<RubySymbol, Map<RubyModule, RubyModule[]>>, RootCallTarget> cachedSymbolToProcTargetsWithRefinements = new ConcurrentHashMap<>();
129-
private final Map<ImmutableRubyString, NativeRope> immutableNativeRopes = new ConcurrentHashMap<>();
130129
/** Default signal handlers for Ruby, only SIGINT and SIGALRM, see {@code core/main.rb} */
131130
public final ConcurrentMap<String, SignalHandler> defaultRubySignalHandlers = new ConcurrentHashMap<>();
132131

@@ -190,6 +189,7 @@ public RubyContext(RubyLanguage language, TruffleLanguage.Env env) {
190189
referenceProcessor = new ReferenceProcessor(this);
191190
finalizationService = new FinalizationService(referenceProcessor);
192191
markingService = new MarkingService(referenceProcessor);
192+
dataObjectFinalizationService = new DataObjectFinalizationService(language, referenceProcessor);
193193

194194
// We need to construct this at runtime
195195
random = createRandomInstance();
@@ -598,6 +598,10 @@ public FinalizationService getFinalizationService() {
598598
return finalizationService;
599599
}
600600

601+
public DataObjectFinalizationService getDataObjectFinalizationService() {
602+
return dataObjectFinalizationService;
603+
}
604+
601605
public MarkingService getMarkingService() {
602606
return markingService;
603607
}
@@ -714,10 +718,6 @@ public Map<Source, Integer> getSourceLineOffsets() {
714718
return sourceLineOffsets;
715719
}
716720

717-
public Map<ImmutableRubyString, NativeRope> getImmutableNativeRopes() {
718-
return immutableNativeRopes;
719-
}
720-
721721
private static SecureRandom createRandomInstance() {
722722
try {
723723
/* We want to use a non-blocking source because this is what MRI does (via /dev/urandom) and it's been found

src/main/java/org/truffleruby/RubyLanguage.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
import java.io.File;
1313
import java.io.IOException;
14-
import java.lang.ref.ReferenceQueue;
14+
import java.lang.ref.Cleaner;
1515
import java.util.Arrays;
1616
import java.util.Objects;
1717
import java.util.Optional;
@@ -36,7 +36,6 @@
3636
import org.truffleruby.cext.ValueWrapperManager;
3737
import org.truffleruby.collections.SharedIndicesMap;
3838
import org.truffleruby.collections.SharedIndicesMap.LanguageArray;
39-
import org.truffleruby.core.FinalizationService;
4039
import org.truffleruby.core.RubyHandle;
4140
import org.truffleruby.core.array.RubyArray;
4241
import org.truffleruby.core.basicobject.RubyBasicObject;
@@ -101,6 +100,7 @@
101100
import org.truffleruby.interop.RubyInnerContext;
102101
import org.truffleruby.interop.RubySourceLocation;
103102
import org.truffleruby.language.LexicalScope;
103+
import org.truffleruby.language.Nil;
104104
import org.truffleruby.language.RubyDynamicObject;
105105
import org.truffleruby.language.RubyEvalInteractiveRootNode;
106106
import org.truffleruby.language.RubyInlineParsingRequestNode;
@@ -182,6 +182,11 @@ public final class RubyLanguage extends TruffleLanguage<RubyContext> {
182182

183183
public static final TruffleLogger LOGGER = TruffleLogger.getLogger(TruffleRuby.LANGUAGE_ID);
184184

185+
/** This is a truly empty frame descriptor and should only by dummy root nodes which require no variables. Any other
186+
* root nodes should should use
187+
* {@link TranslatorEnvironment#newFrameDescriptorBuilder(org.truffleruby.parser.ParentFrameDescriptor, boolean)}. */
188+
public static final FrameDescriptor EMPTY_FRAME_DESCRIPTOR = new FrameDescriptor(Nil.INSTANCE);
189+
185190
/** We need an extra indirection added to ContextThreadLocal due to multiple Fibers of different Ruby Threads
186191
* sharing the same Java Thread when using the fiber pool. */
187192
public static final class ThreadLocalState {
@@ -212,9 +217,9 @@ public static final class ThreadLocalState {
212217
public final SymbolTable symbolTable;
213218
public final KeywordArgumentsDescriptorManager keywordArgumentsDescriptorManager = new KeywordArgumentsDescriptorManager();
214219
public final FrozenStringLiterals frozenStringLiterals;
220+
public final Cleaner cleaner = Cleaner.create();
221+
215222

216-
public final ReferenceQueue<Object> sharedReferenceQueue = new ReferenceQueue<>();
217-
public final FinalizationService sharedFinzationService = new FinalizationService(sharedReferenceQueue);
218223
public volatile ValueWrapperManager.HandleBlockWeakReference[] handleBlockSharedMap = new ValueWrapperManager.HandleBlockWeakReference[0];
219224
public final ValueWrapperManager.HandleBlockAllocator handleBlockAllocator = new ValueWrapperManager.HandleBlockAllocator();
220225

@@ -831,5 +836,4 @@ private static String buildCoreLoadPath(String coreLoadPath) {
831836
throw CompilerDirectives.shouldNotReachHere(e);
832837
}
833838
}
834-
835839
}

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

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
import org.truffleruby.cext.CExtNodesFactory.CallWithCExtLockNodeFactory;
3030
import org.truffleruby.cext.CExtNodesFactory.StringToNativeNodeGen;
3131
import org.truffleruby.cext.UnwrapNode.UnwrapCArrayNode;
32-
import org.truffleruby.collections.ConcurrentOperations;
3332
import org.truffleruby.core.CoreLibrary;
3433
import org.truffleruby.core.MarkingService.ExtensionCallStack;
3534
import org.truffleruby.core.MarkingServiceNodes;
@@ -62,7 +61,6 @@
6261
import org.truffleruby.core.proc.RubyProc;
6362
import org.truffleruby.core.rope.Bytes;
6463
import org.truffleruby.core.rope.CodeRange;
65-
import org.truffleruby.core.rope.LeafRope;
6664
import org.truffleruby.core.rope.NativeRope;
6765
import org.truffleruby.core.rope.Rope;
6866
import org.truffleruby.core.rope.RopeNodes;
@@ -710,7 +708,7 @@ public abstract static class RbStrNewNulNode extends CoreMethodArrayArgumentsNod
710708
@Specialization
711709
protected RubyString rbStrNewNul(int byteLength,
712710
@Cached StringNodes.MakeStringNode makeStringNode) {
713-
final Rope rope = NativeRope.newBuffer(getContext(), byteLength, byteLength);
711+
final Rope rope = NativeRope.newBuffer(getLanguage(), byteLength, byteLength);
714712

715713
return makeStringNode.fromRope(rope, Encodings.BINARY);
716714
}
@@ -768,7 +766,7 @@ protected RubyString rbStrResize(RubyString string, int newByteLength,
768766
nativeRope.clearCodeRange();
769767
return string;
770768
} else {
771-
final NativeRope newRope = nativeRope.resize(getContext(), newByteLength);
769+
final NativeRope newRope = nativeRope.resize(getLanguage(), newByteLength);
772770

773771
// Like MRI's rb_str_resize()
774772
newRope.clearCodeRange();
@@ -790,7 +788,7 @@ protected RubyString trStrCapaResize(RubyString string, int newCapacity,
790788
if (nativeRope.getCapacity() == newCapacity) {
791789
return string;
792790
} else {
793-
final NativeRope newRope = nativeRope.expandCapacity(getContext(), newCapacity);
791+
final NativeRope newRope = nativeRope.expandCapacity(getLanguage(), newCapacity);
794792
string.setRope(newRope);
795793
return string;
796794
}
@@ -1214,7 +1212,7 @@ protected NativeRope toNative(RubyString string,
12141212
nativeRope = (NativeRope) currentRope;
12151213
} else {
12161214
nativeRope = new NativeRope(
1217-
getContext(),
1215+
getLanguage(),
12181216
bytesNode.execute(currentRope),
12191217
currentRope.getEncoding(),
12201218
characterLengthNode.execute(currentRope),
@@ -1225,18 +1223,9 @@ protected NativeRope toNative(RubyString string,
12251223
return nativeRope;
12261224
}
12271225

1228-
@TruffleBoundary
12291226
@Specialization
12301227
protected NativeRope toNativeImmutable(ImmutableRubyString string) {
1231-
return ConcurrentOperations.getOrCompute(getContext().getImmutableNativeRopes(), string, s -> {
1232-
final LeafRope currentRope = s.rope;
1233-
return new NativeRope(
1234-
getContext(),
1235-
currentRope.getBytes(),
1236-
currentRope.getEncoding(),
1237-
currentRope.characterLength(),
1238-
currentRope.getCodeRange());
1239-
});
1228+
return string.getNativeRope(getLanguage());
12401229
}
12411230

12421231
}
@@ -1283,7 +1272,7 @@ protected boolean isNative(RubyString string) {
12831272
@TruffleBoundary
12841273
@Specialization
12851274
protected boolean isNative(ImmutableRubyString string) {
1286-
return getContext().getImmutableNativeRopes().containsKey(string);
1275+
return string.isNative();
12871276
}
12881277

12891278
}
@@ -1962,4 +1951,41 @@ protected RubyArray zlibGetCRCTable() {
19621951
return createArray(ZLibCRCTable.TABLE.clone());
19631952
}
19641953
}
1954+
1955+
@Primitive(name = "data_holder_create")
1956+
public abstract static class DataHolderCreate extends PrimitiveArrayArgumentsNode {
1957+
1958+
@Specialization
1959+
protected DataHolder create(Object address) {
1960+
return new DataHolder(address);
1961+
}
1962+
}
1963+
1964+
@Primitive(name = "data_holder_get_data")
1965+
public abstract static class DataHolderGetData extends PrimitiveArrayArgumentsNode {
1966+
1967+
@Specialization
1968+
protected Object getData(DataHolder data) {
1969+
return data.getPointer();
1970+
}
1971+
}
1972+
1973+
@Primitive(name = "data_holder_set_data")
1974+
public abstract static class DataHolderSetData extends PrimitiveArrayArgumentsNode {
1975+
1976+
@Specialization
1977+
protected Object setData(DataHolder data, Object address) {
1978+
data.setPointer(address);
1979+
return nil;
1980+
}
1981+
}
1982+
1983+
@Primitive(name = "data_holder_is_holder?")
1984+
public abstract static class DataHolderIsHolder extends PrimitiveArrayArgumentsNode {
1985+
1986+
@Specialization
1987+
protected boolean setData(Object dataHolder) {
1988+
return dataHolder instanceof DataHolder;
1989+
}
1990+
}
19651991
}

0 commit comments

Comments
 (0)