Skip to content

Commit 2d79174

Browse files
committed
Call setlocale() only once per RubyLanguage instance
* Ideally it would be once per process and done in the launcher but that is difficult because we do not know the ruby home in the launcher before creating the RubyContext and setting the locale late seems more risky. We also need setlocale(LC_CTYPE, "") when run not from the launcher. * When run from the launcher (!embedded) there is only a single RubyLanguage so we setlocale() once per process. * If embedded we run setlocale(LC_CTYPE, "") once per RubyLanguage instance.
1 parent 1de2627 commit 2d79174

File tree

5 files changed

+29
-20
lines changed

5 files changed

+29
-20
lines changed

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
import com.oracle.truffle.api.strings.AbstractTruffleString;
4040
import com.oracle.truffle.api.strings.InternalByteArray;
4141
import com.oracle.truffle.api.strings.TruffleString;
42+
import org.graalvm.nativeimage.ImageInfo;
43+
import org.graalvm.nativeimage.ProcessProperties;
4244
import org.graalvm.options.OptionDescriptors;
4345
import org.prism.Parser;
4446
import org.truffleruby.annotations.SuppressFBWarnings;
@@ -125,6 +127,7 @@
125127
import org.truffleruby.shared.Metrics;
126128
import org.truffleruby.shared.TruffleRuby;
127129
import org.truffleruby.shared.options.OptionsCatalog;
130+
import org.truffleruby.signal.LibRubySignal;
128131
import org.truffleruby.stdlib.CoverageManager;
129132

130133
import com.oracle.truffle.api.Assumption;
@@ -458,6 +461,7 @@ public RubyContext createContext(Env env) {
458461
this.allocationReporter = env.lookup(AllocationReporter.class);
459462
this.options = new LanguageOptions(env, env.getOptions(), singleContext);
460463
setRubyHome(findRubyHome(env));
464+
setupLocale(env, rubyHome);
461465
loadLibYARPBindings();
462466
this.coreLoadPath = buildCoreLoadPath(this.options.CORE_LOAD_PATH);
463467
this.corePath = coreLoadPath + File.separator + "core" + File.separator;
@@ -539,6 +543,7 @@ protected boolean patchContext(RubyContext context, Env newEnv) {
539543

540544
synchronized (this) {
541545
setRubyHome(findRubyHome(newEnv));
546+
setupLocale(newEnv, rubyHome);
542547
loadLibYARPBindings();
543548
setupCleaner();
544549
}
@@ -819,6 +824,24 @@ private boolean isRubyHome(TruffleFile path) {
819824
lib.resolve("patches").isDirectory();
820825
}
821826

827+
private void setupLocale(Env env, String rubyHome) {
828+
// CRuby does setlocale(LC_CTYPE, "") because this is needed to get the locale encoding with nl_langinfo(CODESET).
829+
// This means every locale category except LC_CTYPE remains the initial "C".
830+
// LC_CTYPE is set according to environment variables (LC_ALL, LC_CTYPE, LANG).
831+
// HotSpot does setlocale(LC_ALL, "") and Native Image does nothing.
832+
// We match CRuby by doing setlocale(LC_ALL, "C") and setlocale(LC_CTYPE, "").
833+
// This also affects C functions that depend on the locale in C extensions, so best to follow CRuby here.
834+
// Change the strict minimum if embedded because setlocale() is process-wide.
835+
if (env.getOptions().get(OptionsCatalog.EMBEDDED_KEY)) {
836+
if (ImageInfo.inImageRuntimeCode()) {
837+
ProcessProperties.setLocale("LC_CTYPE", "");
838+
}
839+
} else {
840+
LibRubySignal.loadLibrary(rubyHome, Platform.LIB_SUFFIX);
841+
LibRubySignal.setupLocale();
842+
}
843+
}
844+
822845
private void loadLibYARPBindings() {
823846
String libyarpbindings = getRubyHome() + "/lib/libyarpbindings" + Platform.LIB_SUFFIX;
824847
Parser.loadLibrary(libyarpbindings);

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ public DataObjectFinalizationService(RubyLanguage language, ReferenceProcessor r
7171
this(language, referenceProcessor.processingQueue);
7272
}
7373

74+
@TruffleBoundary
7475
public DataObjectFinalizerReference addFinalizer(RubyContext context, Object object, Object finalizerCFunction,
7576
Object dataStruct) {
7677
final DataObjectFinalizerReference newRef = createRef(object, finalizerCFunction, dataStruct);

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ public ReferenceProcessor(RubyContext context) {
7878
this.context = context;
7979
}
8080

81+
@TruffleBoundary
8182
void processReferenceQueue(ReferenceProcessingService<?, ?> service) {
8283
if (processOnMainThread()) {
8384
drainReferenceQueues();

src/main/java/org/truffleruby/core/encoding/EncodingManager.java

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,8 @@
2020
import java.util.concurrent.ConcurrentHashMap;
2121

2222
import com.oracle.truffle.api.CompilerDirectives;
23-
import com.oracle.truffle.api.TruffleOptions;
2423
import com.oracle.truffle.api.interop.InteropException;
2524
import com.oracle.truffle.api.interop.InteropLibrary;
26-
import org.graalvm.nativeimage.ProcessProperties;
2725
import org.graalvm.shadowed.org.jcodings.Encoding;
2826
import org.graalvm.shadowed.org.jcodings.EncodingDB;
2927
import org.truffleruby.RubyContext;
@@ -39,8 +37,6 @@
3937

4038
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
4139
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
42-
import org.truffleruby.shared.Platform;
43-
import org.truffleruby.signal.LibRubySignal;
4440

4541
import static org.truffleruby.core.encoding.Encodings.INITIAL_NUMBER_OF_ENCODINGS;
4642

@@ -131,27 +127,12 @@ public void initializeDefaultEncodings(TruffleNFIPlatform nfi, NativeConfigurati
131127
}
132128

133129
private void initializeLocaleEncoding(TruffleNFIPlatform nfi, NativeConfiguration nativeConfiguration) {
134-
// CRuby does setlocale(LC_CTYPE, "") because this is needed to get the locale encoding with nl_langinfo(CODESET).
135-
// This means every locale category except LC_CTYPE remains the initial "C".
136-
// LC_CTYPE is set according to environment variables (LC_ALL, LC_CTYPE, LANG).
137-
// HotSpot does setlocale(LC_ALL, "") and Native Image does nothing.
138-
// We match CRuby by doing setlocale(LC_ALL, "C") and setlocale(LC_CTYPE, "").
139-
// This also affects C functions that depend on the locale in C extensions, so best to follow CRuby here.
140-
// Change the strict minimum if embedded because setlocale() is process-wide.
141-
if (context.getOptions().EMBEDDED) {
142-
if (TruffleOptions.AOT) {
143-
ProcessProperties.setLocale("LC_CTYPE", "");
144-
}
145-
} else {
146-
LibRubySignal.loadLibrary(language.getRubyHome(), Platform.LIB_SUFFIX);
147-
LibRubySignal.setupLocale();
148-
}
149-
150130
final String localeEncodingName;
151131
final String detector;
152132
if (nfi != null) {
153133
final int codeset = (int) nativeConfiguration.get("platform.langinfo.CODESET");
154134

135+
// nl_langinfo() needs setlocale(LC_CTYPE, "") before, which is done in RubyLanguage#setupLocale()
155136
// char *nl_langinfo(nl_item item);
156137
// nl_item is int on at least Linux and macOS
157138
final Object nl_langinfo = nfi.getFunction(context, "nl_langinfo", "(sint32):string");

src/options.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ LANGUAGE_OPTIONS:
4848
- CLASS_CACHE
4949
- ARRAY_DUP_CACHE
5050
- ARRAY_STRATEGY_CACHE
51+
# Other
5152
- EXPERIMENTAL_ENGINE_CACHING
5253
- RUN_TWICE
5354
- CHECK_CLONE_UNINITIALIZED_CORRECTNESS
@@ -85,6 +86,8 @@ EXPERT:
8586
LAZY_RUBYGEMS: [lazy-rubygems, boolean, ['RUBYGEMS && ', DEFAULT_LAZY], Load RubyGems lazily on first failing require]
8687

8788
# Embedding options
89+
# It would be tempting to make these language options, but that does not work because the pre-initialized context is created with (isNativeAccessAllowed=false, embedded=true)
90+
# and it must be compatible (to be reused) with the runtime context created from the truffleruby launcher, which is (isNativeAccessAllowed=true, embedded=false).
8891
EMBEDDED: [embedded, boolean, true, 'Set default options for an embedded use of TruffleRuby, rather than top-level use']
8992
NATIVE_PLATFORM: [platform-native, boolean, ['env.isNativeAccessAllowed() && ', true], Enables native calls via Truffle NFI for internal functionality]
9093
NATIVE_INTERRUPT: [platform-native-interrupt, boolean, NATIVE_PLATFORM, Use the SIGVTALRM signal to interrupt native blocking calls]

0 commit comments

Comments
 (0)