Skip to content

Commit a800519

Browse files
committed
[GR-54136] Synchronize on thread registration when created from guest Thread.start().
PullRequest: graal/21885
2 parents decf9f4 + 002d681 commit a800519

File tree

4 files changed

+70
-56
lines changed

4 files changed

+70
-56
lines changed

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,8 +1104,9 @@ public StaticObject[] getActiveThreads() {
11041104
return espressoEnv.getThreadRegistry().activeThreads();
11051105
}
11061106

1107-
public void registerThread(Thread host, StaticObject self) {
1108-
espressoEnv.getThreadRegistry().registerThread(host, self);
1107+
public void registerJavaThread(Thread host, StaticObject self) {
1108+
StaticObject guest = espressoEnv.getThreadRegistry().registerThread(host, self);
1109+
assert self == guest;
11091110
if (shouldReportVMEvents()) {
11101111
espressoEnv.getEventListener().threadStarted(self);
11111112
}

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoThreadLocalState.java

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import com.oracle.truffle.api.CompilerDirectives;
2828
import com.oracle.truffle.espresso.impl.ClassRegistry;
29+
import com.oracle.truffle.espresso.impl.Field;
2930
import com.oracle.truffle.espresso.runtime.staticobject.StaticObject;
3031
import com.oracle.truffle.espresso.vm.VM;
3132

@@ -244,28 +245,30 @@ assert getHost(currentPlatformThread) == Thread.currentThread() : //
244245
assert getHost(t) == Thread.currentThread() : //
245246
"Current thread fast access set by non-current thread";
246247

248+
EspressoContext ctx = EspressoContext.get(null);
249+
Field managedBit = ctx.getMeta().HIDDEN_ESPRESSO_MANAGED;
250+
247251
// Ensure we are not registering multiple guest threads for the same host thread.
248252
assert currentPlatformThread == null || currentPlatformThread == t : //
249253
/*- Report these threads names */
250254
getHost(currentPlatformThread).getName() + " vs " + getHost(t).getName() + "\n" +
251255
/*- Report these threads identities */
252-
"Guest identities" + System.identityHashCode(currentPlatformThread) + " vs " + System.identityHashCode(t) + "\n" +
256+
"Guest identities: " + System.identityHashCode(currentPlatformThread) + " vs " + System.identityHashCode(t) + "\n" +
253257
/*- Checks if our host threads are actually different, or if it is simply a renamed one. */
254-
"Host identities: " + System.identityHashCode(getHost(currentPlatformThread)) + " vs " + System.identityHashCode(getHost(t));
258+
"Host identities: " + System.identityHashCode(getHost(currentPlatformThread)) + " vs " + System.identityHashCode(getHost(t)) + "/n" +
259+
/*- Obtain the `managed` bits to know the origin of these threads */
260+
"Managed by espresso: " + managedBit.getBoolean(currentPlatformThread) + " vs " + managedBit.getBoolean(t);
255261
}
256262
// @formatter:on
257-
/*-
263+
/*
258264
* Current theory for GR-50089:
259265
*
260-
* For some reason, when creating a new guest thread, instead of spawning a new host thread
261-
* Truffle gives us back a previously created one that had completed.
262-
*
263-
* If that is the case, then, on failure, we will see in the report:
264-
* - Different guest names and/or guest identities
265-
* - Same host identities
266+
* Lack of synchronization in `ThreadAccess.createJavaThread()` makes it so the polyglot
267+
* thread is started and can reach `EspressoLanguage.initializeThread()` before the store to
268+
* the thread registry can be observed.
266269
*
267-
* This may be solved by unregistering a guest thread from the thread local state in
268-
* ThreadAccess.terminate().
270+
* Now that a `synchronize` block has been added to `EspressoThreadRegistry`, this assertion
271+
* should no longer trigger.
269272
*/
270273
return true;
271274
}

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/threads/EspressoThreadRegistry.java

Lines changed: 46 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -117,45 +117,50 @@ private void registerMainThread(Thread thread, StaticObject guest) {
117117
public final AtomicLong createdThreadCount = new AtomicLong();
118118
public final AtomicLong peakThreadCount = new AtomicLong();
119119

120-
public void registerThread(Thread host, StaticObject guest) {
121-
activeThreads.add(guest);
122-
123-
// Update java.lang.management counters.
124-
createdThreadCount.incrementAndGet();
125-
peakThreadCount.updateAndGet(new LongUnaryOperator() {
126-
@Override
127-
public long applyAsLong(long oldPeak) {
128-
return Math.max(oldPeak, activeThreads.size());
120+
public StaticObject registerThread(Thread host, StaticObject guest) {
121+
synchronized (activeThreadLock) {
122+
StaticObject existingThread = getGuestThreadFromHost(host);
123+
if (existingThread != null) {
124+
// There is already a guest thread for this host thread.
125+
return existingThread;
129126
}
130-
});
131127

132-
if (finalizerThreadId == -1) {
133-
if (getMeta().java_lang_ref_Finalizer$FinalizerThread == guest.getKlass()) {
134-
synchronized (activeThreadLock) {
128+
activeThreads.add(guest);
129+
130+
// Update java.lang.management counters.
131+
createdThreadCount.incrementAndGet();
132+
peakThreadCount.updateAndGet(new LongUnaryOperator() {
133+
@Override
134+
public long applyAsLong(long oldPeak) {
135+
return Math.max(oldPeak, activeThreads.size());
136+
}
137+
});
138+
139+
if (finalizerThreadId == -1) {
140+
if (getMeta().java_lang_ref_Finalizer$FinalizerThread == guest.getKlass()) {
135141
if (finalizerThreadId == -1) {
136142
CompilerDirectives.transferToInterpreterAndInvalidate();
137143
finalizerThreadId = getThreadId(host);
138144
guestFinalizerThread = guest;
139-
return;
145+
return guest;
140146
}
141147
}
142148
}
143-
}
144-
if (referenceHandlerThreadId == -1) {
145-
if (getMeta().java_lang_ref_Reference$ReferenceHandler == guest.getKlass()) {
146-
synchronized (activeThreadLock) {
149+
if (referenceHandlerThreadId == -1) {
150+
if (getMeta().java_lang_ref_Reference$ReferenceHandler == guest.getKlass()) {
147151
if (referenceHandlerThreadId == -1) {
148152
CompilerDirectives.transferToInterpreterAndInvalidate();
149153
referenceHandlerThreadId = getThreadId(host);
150154
guestReferenceHandlerThread = guest;
151-
return;
155+
return guest;
152156
}
153157
}
154158
}
155-
}
156-
pushThread(Math.toIntExact(getThreadId(host)), guest);
157-
if (host == Thread.currentThread()) {
158-
getContext().registerCurrentThread(guest);
159+
pushThread(Math.toIntExact(getThreadId(host)), guest);
160+
if (host == Thread.currentThread()) {
161+
getContext().registerCurrentThread(guest);
162+
}
163+
return guest;
159164
}
160165
}
161166

@@ -166,18 +171,18 @@ public long applyAsLong(long oldPeak) {
166171
*/
167172
@SuppressFBWarnings(value = "NN", justification = "Removing a thread from the active set is the state change we need.")
168173
public boolean unregisterThread(StaticObject thread) {
169-
if (!activeThreads.remove(thread)) {
170-
// Already unregistered
171-
return false;
172-
}
173-
logger.fine(() -> {
174-
String guestName = getThreadAccess().getThreadName(thread);
175-
long guestId = getThreadAccess().getThreadId(thread);
176-
return String.format("unregisterThread([GUEST:%s, %d])", guestName, guestId);
177-
});
178-
Thread hostThread = getThreadAccess().getHost(thread);
179-
int id = Math.toIntExact(getThreadId(hostThread));
180174
synchronized (activeThreadLock) {
175+
if (!activeThreads.remove(thread)) {
176+
// Already unregistered
177+
return false;
178+
}
179+
logger.fine(() -> {
180+
String guestName = getThreadAccess().getThreadName(thread);
181+
long guestId = getThreadAccess().getThreadId(thread);
182+
return String.format("unregisterThread([GUEST:%s, %d])", guestName, guestId);
183+
});
184+
Thread hostThread = getThreadAccess().getHost(thread);
185+
int id = Math.toIntExact(getThreadId(hostThread));
181186
if (id == mainThreadId) {
182187
mainThreadId = -1;
183188
guestMainThread = null;
@@ -264,16 +269,17 @@ public StaticObject createGuestThreadFromHost(Thread hostThread, Meta meta, VM v
264269
return null;
265270
}
266271
synchronized (activeThreadLock) {
267-
StaticObject exisitingThread = getGuestThreadFromHost(hostThread);
268-
if (exisitingThread != null) {
272+
StaticObject existingThread = getGuestThreadFromHost(hostThread);
273+
if (existingThread != null) {
269274
// already a live guest thread for this host thread
270-
return exisitingThread;
275+
return existingThread;
271276
}
272277
StaticObject effectiveThreadGroup = threadGroup;
273278
if (effectiveThreadGroup == null || StaticObject.isNull(effectiveThreadGroup)) {
274279
effectiveThreadGroup = getContext().getMainThreadGroup();
275280
}
276281
vm.attachThread(hostThread);
282+
277283
StaticObject guestThread = meta.java_lang_Thread.allocateInstance(getContext());
278284

279285
// Allow guest Thread.currentThread() to work.
@@ -282,7 +288,9 @@ public StaticObject createGuestThreadFromHost(Thread hostThread, Meta meta, VM v
282288
}
283289
getThreadAccess().setEETopAlive(guestThread);
284290
getThreadAccess().initializeHiddenFields(guestThread, hostThread, managedByEspresso);
285-
registerThread(hostThread, guestThread);
291+
// Associate host and guest.
292+
existingThread = registerThread(hostThread, guestThread);
293+
assert existingThread == guestThread;
286294
assert getThreadAccess().getCurrentGuestThread() != null;
287295

288296
if (name == null) {

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/threads/ThreadAccess.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -449,18 +449,20 @@ public boolean isManaged(StaticObject guest) {
449449
*/
450450
public Thread createJavaThread(StaticObject guest, DirectCallNode exit, DirectCallNode dispatch) {
451451
Thread host = getContext().getEnv().newTruffleThreadBuilder(new GuestRunnable(getContext(), guest, exit, dispatch)).build();
452-
initializeHiddenFields(guest, host, true);
453452
// Prepare host thread
454453
host.setDaemon(isDaemon(guest));
455454
host.setPriority(getPriority(guest));
455+
String guestName = getContext().getThreadAccess().getThreadName(guest);
456+
host.setName(guestName);
456457
if (isInterrupted(guest, false)) {
457458
host.interrupt();
458459
}
459-
String guestName = getContext().getThreadAccess().getThreadName(guest);
460-
host.setName(guestName);
460+
// Prepare guest thread
461+
initializeHiddenFields(guest, host, true);
461462
getThreadAccess().setEETopAlive(guest);
462-
// Make the thread known to the context
463-
getContext().registerThread(host, guest);
463+
// Associate host and guest
464+
getContext().registerJavaThread(host, guest);
465+
464466
// Thread must be runnable on returning from 'start', so we set it preemptively
465467
// here.
466468
getThreadAccess().initializeState(guest, ThreadState.DefaultStates.DEFAULT_RUNNABLE_STATE);

0 commit comments

Comments
 (0)