Skip to content

Commit 7b49099

Browse files
committed
Always call TruffleLanguage#initializeThread on the thread being initialized.
1 parent 100855c commit 7b49099

File tree

30 files changed

+1193
-466
lines changed

30 files changed

+1193
-466
lines changed

sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/LLVMContext.java

+31-28
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,27 @@
2929
*/
3030
package com.oracle.truffle.llvm.runtime;
3131

32+
import java.io.IOException;
33+
import java.nio.file.Path;
34+
import java.nio.file.Paths;
35+
import java.security.SecureRandom;
36+
import java.util.ArrayList;
37+
import java.util.Arrays;
38+
import java.util.Collections;
39+
import java.util.HashMap;
40+
import java.util.LinkedHashMap;
41+
import java.util.List;
42+
import java.util.Map;
43+
import java.util.concurrent.ConcurrentHashMap;
44+
import java.util.concurrent.locks.ReentrantLock;
45+
import java.util.logging.Level;
46+
import java.util.stream.Collectors;
47+
48+
import org.graalvm.collections.EconomicMap;
49+
import org.graalvm.collections.EconomicSet;
50+
import org.graalvm.collections.Equivalence;
51+
import org.graalvm.collections.Pair;
52+
3253
import com.oracle.truffle.api.Assumption;
3354
import com.oracle.truffle.api.CallTarget;
3455
import com.oracle.truffle.api.CompilerAsserts;
@@ -45,6 +66,7 @@
4566
import com.oracle.truffle.api.TruffleSafepoint;
4667
import com.oracle.truffle.api.nodes.ControlFlowException;
4768
import com.oracle.truffle.api.nodes.Node;
69+
import com.oracle.truffle.api.profiles.BranchProfile;
4870
import com.oracle.truffle.api.source.Source;
4971
import com.oracle.truffle.llvm.api.Toolchain;
5072
import com.oracle.truffle.llvm.runtime.IDGenerater.BitcodeID;
@@ -67,29 +89,6 @@
6789
import com.oracle.truffle.llvm.runtime.pointer.LLVMPointer;
6890
import com.oracle.truffle.llvm.runtime.pthread.LLVMPThreadContext;
6991

70-
import org.graalvm.collections.EconomicMap;
71-
import org.graalvm.collections.Pair;
72-
73-
import java.io.IOException;
74-
import java.nio.file.Path;
75-
import java.nio.file.Paths;
76-
import java.security.SecureRandom;
77-
import java.util.ArrayList;
78-
import java.util.Arrays;
79-
import java.util.Collections;
80-
import java.util.HashMap;
81-
import java.util.LinkedHashMap;
82-
import java.util.List;
83-
import java.util.Map;
84-
import java.util.concurrent.ConcurrentHashMap;
85-
import java.util.concurrent.locks.ReentrantLock;
86-
import java.util.logging.Level;
87-
import java.util.stream.Collectors;
88-
89-
import com.oracle.truffle.api.profiles.BranchProfile;
90-
import org.graalvm.collections.EconomicSet;
91-
import org.graalvm.collections.Equivalence;
92-
9392
public final class LLVMContext {
9493
public static final String SULONG_INIT_CONTEXT = "__sulong_init_context";
9594
public static final String SULONG_DISPOSE_CONTEXT = "__sulong_dispose_context";
@@ -552,7 +551,7 @@ private void cleanUpNoGuestCode() {
552551
}
553552

554553
Thread[] allThreads;
555-
try (TLSInitializerAccess access = getTLSInitializerAccess()) {
554+
try (TLSInitializerAccess access = getTLSInitializerAccess(true)) {
556555
allThreads = access.getAllRunningThreads();
557556
}
558557

@@ -1047,8 +1046,12 @@ public synchronized void unregisterThread(LLVMThread thread) {
10471046
public final class TLSInitializerAccess implements AutoCloseable {
10481047

10491048
@TruffleBoundary
1050-
private TLSInitializerAccess() {
1051-
threadInitLock.lock();
1049+
private TLSInitializerAccess(boolean interruptible) {
1050+
if (interruptible) {
1051+
TruffleSafepoint.setBlockedThreadInterruptible(null, ReentrantLock::lockInterruptibly, threadInitLock);
1052+
} else {
1053+
threadInitLock.lock();
1054+
}
10521055
}
10531056

10541057
@TruffleBoundary
@@ -1088,8 +1091,8 @@ public List<AggregateTLGlobalInPlaceNode> getThreadLocalGlobalInitializer() {
10881091
}
10891092
}
10901093

1091-
public TLSInitializerAccess getTLSInitializerAccess() {
1092-
return new TLSInitializerAccess();
1094+
public TLSInitializerAccess getTLSInitializerAccess(boolean interruptible) {
1095+
return new TLSInitializerAccess(interruptible);
10931096
}
10941097

10951098
@TruffleBoundary

sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/LLVMLanguage.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ public static LLVMLanguage get(Node node) {
416416
@Override
417417
protected void initializeThread(LLVMContext context, Thread thread) {
418418
getCapability(PlatformCapability.class).initializeThread(context, thread);
419-
try (TLSInitializerAccess access = context.getTLSInitializerAccess()) {
419+
try (TLSInitializerAccess access = context.getTLSInitializerAccess(true)) {
420420
// need to duplicate the thread local globals for this thread.
421421
for (AggregateTLGlobalInPlaceNode globalInitializer : access.getThreadLocalGlobalInitializer()) {
422422
// TODO: use the call target of AggregateTLGlobalInPlaceNode, rather than the node
@@ -869,7 +869,7 @@ protected void disposeThreadNoGuestCode(LLVMContext context, Thread thread) {
869869
freeThreadLocalGlobal(threadLocalValue);
870870
}
871871

872-
try (TLSInitializerAccess access = context.getTLSInitializerAccess()) {
872+
try (TLSInitializerAccess access = context.getTLSInitializerAccess(false)) {
873873
access.unregisterLiveThread(thread);
874874
}
875875
}

sulong/projects/com.oracle.truffle.llvm/src/com/oracle/truffle/llvm/initialization/InitializeGlobalNode.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
import java.util.ArrayList;
3535
import java.util.List;
3636

37+
import org.graalvm.collections.Pair;
38+
3739
import com.oracle.truffle.api.Truffle;
3840
import com.oracle.truffle.api.frame.VirtualFrame;
3941
import com.oracle.truffle.api.nodes.DirectCallNode;
@@ -62,8 +64,6 @@
6264
import com.oracle.truffle.llvm.runtime.types.Type;
6365
import com.oracle.truffle.llvm.runtime.types.Type.TypeOverflowException;
6466

65-
import org.graalvm.collections.Pair;
66-
6767
/**
6868
* {@link InitializeGlobalNode} initializes the value of all defined global symbols.
6969
*
@@ -99,7 +99,7 @@ public void execute(VirtualFrame frame, Pair<LLVMPointer, Long> roDataBase) {
9999
LLVMContext context = LLVMContext.get(this);
100100

101101
// try-with-resources is not PE-safe (blocklisted method Throwable.addSuppressed)
102-
TLSInitializerAccess access = context.getTLSInitializerAccess();
102+
TLSInitializerAccess access = context.getTLSInitializerAccess(true);
103103
try {
104104
Thread[] threads = access.getAllRunningThreads();
105105
for (Thread thread : threads) {

tools/src/com.oracle.truffle.tools.profiler.test/src/com/oracle/truffle/tools/profiler/test/NoTagSamplingTest.java

+14-22
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
package com.oracle.truffle.tools.profiler.test;
2626

2727
import static org.junit.Assert.assertEquals;
28+
import static org.junit.Assert.assertFalse;
2829
import static org.junit.Assert.assertTrue;
2930

3031
import java.util.List;
@@ -54,42 +55,32 @@ public class NoTagSamplingTest {
5455

5556
private static final String TEST_ROOT_NAME = LILRootNode.class.getName();
5657

57-
private static Semaphore await;
58+
private static Semaphore awaitStart;
59+
private static Semaphore awaitFinish;
5860

5961
@Test
6062
public void testNoTagSampling() throws InterruptedException, ExecutionException {
6163
ExecutorService singleThread = Executors.newSingleThreadExecutor();
62-
await = new Semaphore(0);
64+
awaitStart = new Semaphore(0);
65+
awaitFinish = new Semaphore(0);
6366
try (Context context = Context.create(NoTagLanguage.ID)) {
6467
CPUSampler sampler = CPUSampler.find(context.getEngine());
6568
Source source = Source.newBuilder(NoTagLanguage.ID, "", "").buildLiteral();
66-
Future<?> f = singleThread.submit(() -> {
67-
return context.eval(source).asInt();
68-
});
69-
70-
Map<Thread, List<StackTraceEntry>> sample = null;
71-
for (int i = 0; i < 10000; i++) { // times out after 10s
72-
sample = sampler.takeSample();
73-
if (!sample.isEmpty()) {
74-
break;
75-
}
76-
try {
77-
Thread.sleep(1);
78-
} catch (InterruptedException e) {
79-
throw new AssertionError(e);
80-
}
81-
}
69+
Future<?> f = singleThread.submit(() -> context.eval(source).asInt());
70+
71+
awaitStart.acquire();
72+
Map<Thread, List<StackTraceEntry>> sample = sampler.takeSample();
8273

8374
// wait for future
84-
await.release();
75+
awaitFinish.release();
8576
assertEquals(42, f.get());
86-
assertTrue(!sample.isEmpty());
77+
assertFalse(sample.isEmpty());
8778
List<StackTraceEntry> entries = sample.values().iterator().next();
8879
assertEquals(1, entries.size());
8980
assertEquals(TEST_ROOT_NAME, entries.get(0).getRootName());
9081

9182
singleThread.shutdown();
92-
singleThread.awaitTermination(10, TimeUnit.SECONDS);
83+
assertTrue(singleThread.awaitTermination(10, TimeUnit.SECONDS));
9384
}
9485

9586
}
@@ -107,7 +98,8 @@ public String getName() {
10798

10899
@Override
109100
public Object execute(VirtualFrame frame) {
110-
TruffleSafepoint.setBlockedThreadInterruptible(this, Semaphore::acquire, await);
101+
awaitStart.release();
102+
TruffleSafepoint.setBlockedThreadInterruptible(this, Semaphore::acquire, awaitFinish);
111103
return 42;
112104
}
113105
}

truffle/CHANGELOG.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ This changelog summarizes major changes between Truffle versions relevant to lan
1414
* GR-46293 The deprecated `com.oracle.truffle.api.library.DefaultExportProvider` and `com.oracle.truffle.api.library.EagerExportProvider` interfaces, deprecated since version 23.1, have now been removed. They are replaced by the `com.oracle.truffle.api.library.provider.DefaultExportProvider` and `com.oracle.truffle.api.library.provider.EagerExportProvider` interfaces. The implementations of these interfaces are automatically generated by the annotation processor and this change requires no source code modifications, only recompilation.
1515
* GR-59640 `InternalResource.versionHash(Env)` may now throw an `IOException` to indicate problems when reading the version from disk.
1616
* GR-54300 An inner context is now automatically closed when its corresponding creator `TruffleContext` is no longer strongly reachable. A reachable API `TruffleContext` will keep the associated creator `TruffleContext` reachable. However, it is still recommended not to rely on garbage collection for closing. Instead, use the try-with-resources pattern for explicit context management. For more information, refer to the [Automatic Close on GC documentation](https://github.com/oracle/graal/blob/master/truffle/docs/CloseOnGc.md).
17-
17+
* GR-30264 `TruffleLanguage#initializeThread(Object, Thread)` is now guaranteed to be executed on the thread that is being initialized. Additional changes:
18+
* Added `ThreadLocalAction#notifyBlocked(Access)` and `ThreadLocalAction#notifyUnblocked(Access)` to notify thread local actions that their processing has been blocked/unblocked due to a blocked call (see `ThreadLocalAction` documentation).
19+
* `TruffleSafepoint#poll(Node)` does not require a non-null location anymore. However, it is still recommended to always pass a location node, if available.
1820

1921
## Version 24.1.0
2022
* GR-43839 Added optional parameter to TruffleString.ByteIndexOfCodePointSetNode to choose whether the node may calculate the input string's precise code range.

truffle/docs/Safepoints.md

+21-8
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,16 @@ env.submitThreadLocal(null, new ThreadLocalAction(true /*side-effecting*/, true
5151
protected void perform(Access access) {
5252
assert access.getThread() == Thread.currentThread();
5353
}
54+
55+
@Override
56+
protected void notifyBlocked(Access access) {
57+
assert access.getThread() == Thread.currentThread();
58+
}
59+
60+
@Override
61+
protected void notifyUnblocked(Access access) {
62+
assert access.getThread() == Thread.currentThread();
63+
}
5464
});
5565

5666
```
@@ -86,21 +96,24 @@ graalvm/bin/js --engine.SafepointALot js-benchmarks/harness.js -- octane-deltabl
8696
Prints the following output to the log on context close:
8797

8898
```
89-
DeltaBlue: 540
90-
[engine] Safepoint Statistics
91-
--------------------------------------------------------------------------------------
92-
Thread Name Safepoints | Interval Avg Min Max
93-
--------------------------------------------------------------------------------------
94-
main 48384054 | 0.425 us 0.1 us 44281.1 us
95-
-------------------------------------------------------------------------------------
96-
All threads 48384054 | 0.425 us 0.1 us 42281.1 us
99+
DeltaBlue: 3037
100+
[engine] Safepoint Statistics
101+
-------------------------------------------------------------------------------------------------------------------------------------------------------
102+
Thread Name Safepoints | Interval Avg Min Max | Blocked Intervals Avg Min Max
103+
-------------------------------------------------------------------------------------------------------------------------------------------------------
104+
main 83187332 | 0,452 us 0,2 us 104938,8 us | 18 6,536 us 0,9 us 36,8 us
105+
-------------------------------------------------------------------------------------------------------------------------------------------------------
106+
All threads 83187332 | 0,452 us 0,2 us 104938,8 us | 18 6,536 us 0,9 us 36,8 us
97107
```
98108

99109
It is recommended for guest language implementations to try to stay below 1ms on average.
100110
Note that precise timing can depend on CPU and interruptions by the GC.
101111
Since GC times are included in the safepoint interval times, it is expected that the maximum is close to the maximum GC interruption time.
102112
Future versions of this tool will be able to exclude GC interruption times from this statistic.
103113

114+
The SafepointALot tool also does not interrupt blocking operations that typically wait for some resource to be available (e.g. IO, thread start).
115+
These blocked intervals are shown separately in the statistics. Ordinary thread local actions do interrupt blocking operations, and so the blocked intervals do not apply to them.
116+
104117
### Find missing safepoint polls
105118

106119
The option `TraceMissingSafepointPollInterval` helps to find missing safepoint polls, use it like:

0 commit comments

Comments
 (0)