use of com.oracle.truffle.api.ThreadLocalAction in project graal by oracle.
the class PolyglotContextImpl method enterThreadChanged.
/**
* Use to enter context if it's guaranteed to be called rarely and configuration flexibility is
* needed. Otherwise use {@link PolyglotEngineImpl#enter(PolyglotContextImpl)}.
*/
@TruffleBoundary
Object[] enterThreadChanged(boolean notifyEnter, boolean enterReverted, boolean pollSafepoint, boolean deactivateSafepoints) {
PolyglotThreadInfo enteredThread = null;
Object[] prev = null;
try {
Thread current = Thread.currentThread();
boolean needsInitialization = false;
synchronized (this) {
PolyglotThreadInfo threadInfo = getCurrentThreadInfo();
if (enterReverted && threadInfo.getEnteredCount() == 0) {
threadLocalActions.notifyThreadActivation(threadInfo, false);
if ((state.isCancelling() || state.isExiting() || state == State.CLOSED_CANCELLED || state == State.CLOSED_EXITED) && !threadInfo.isActive()) {
notifyThreadClosed(threadInfo);
}
if (state.isInterrupting() && !threadInfo.isActive()) {
Thread.interrupted();
notifyAll();
}
}
if (deactivateSafepoints && threadInfo != PolyglotThreadInfo.NULL) {
threadLocalActions.notifyThreadActivation(threadInfo, false);
}
checkClosed();
assert threadInfo != null;
threadInfo = threads.get(current);
if (threadInfo == null) {
threadInfo = createThreadInfo(current);
needsInitialization = true;
}
if (singleThreaded) {
/*
* If this is the only thread, then setting the cached thread info to NULL is no
* performance problem. If there is other thread that is just about to enter, we
* are making sure that it initializes multi-threading if this thread doesn't do
* it.
*/
setCachedThreadInfo(PolyglotThreadInfo.NULL);
}
boolean transitionToMultiThreading = isSingleThreaded() && hasActiveOtherThread(true);
if (transitionToMultiThreading) {
// recheck all thread accesses
checkAllThreadAccesses(Thread.currentThread(), false);
}
if (transitionToMultiThreading) {
/*
* We need to do this early (before initializeMultiThreading) as entering or
* local initialization depends on single thread per context.
*/
engine.singleThreadPerContext.invalidate();
singleThreaded = false;
}
if (needsInitialization) {
threads.put(current, threadInfo);
}
if (needsInitialization) {
/*
* Do not enter the thread before initializing thread locals. Creation of thread
* locals might fail.
*/
initializeThreadLocals(threadInfo);
}
prev = threadInfo.enterInternal();
if (notifyEnter) {
try {
threadInfo.notifyEnter(engine, this);
} catch (Throwable t) {
threadInfo.leaveInternal(prev);
throw t;
}
}
enteredThread = threadInfo;
if (needsInitialization) {
this.threadLocalActions.notifyEnterCreatedThread();
}
// new thread became active so we need to check potential active thread local
// actions and process them.
Set<ThreadLocalAction> activatedActions = null;
if (enteredThread.getEnteredCount() == 1 && !deactivateSafepoints) {
activatedActions = threadLocalActions.notifyThreadActivation(threadInfo, true);
}
if (transitionToMultiThreading) {
// we need to verify that all languages give access
// to all threads in multi-threaded mode.
transitionToMultiThreaded();
}
if (needsInitialization) {
initializeNewThread(current);
}
if (enteredThread.getEnteredCount() == 1 && !pauseThreadLocalActions.isEmpty()) {
for (Iterator<PauseThreadLocalAction> threadLocalActionIterator = pauseThreadLocalActions.iterator(); threadLocalActionIterator.hasNext(); ) {
PauseThreadLocalAction threadLocalAction = threadLocalActionIterator.next();
if (!threadLocalAction.isPause()) {
threadLocalActionIterator.remove();
} else {
if (activatedActions == null || !activatedActions.contains(threadLocalAction)) {
threadLocalActions.submit(new Thread[] { Thread.currentThread() }, PolyglotEngineImpl.ENGINE_ID, threadLocalAction, new HandshakeConfig(true, true, false, false));
}
}
}
}
// never cache last thread on close or when closingThread
setCachedThreadInfo(threadInfo);
}
if (needsInitialization) {
EngineAccessor.INSTRUMENT.notifyThreadStarted(engine, creatorTruffleContext, current);
}
return prev;
} finally {
/*
* We need to always poll the safepoint here in case we already submitted a thread local
* action for this thread. Not polling here would make dependencies of that event wait
* forever.
*/
if (pollSafepoint) {
try {
TruffleSafepoint.pollHere(this.uncachedLocation);
} catch (Throwable t) {
/*
* Just in case a safepoint makes the enter fail we need to leave the context
* again.
*/
if (enteredThread != null) {
this.leaveThreadChanged(prev, notifyEnter, true);
}
throw t;
}
}
}
}
use of com.oracle.truffle.api.ThreadLocalAction in project graal by oracle.
the class OptimizedOSRLoopNodeTest method testSafepointLocationInLoop.
/*
* Test that thread local actions never expose the OSR root node.
*/
@Theory
public void testSafepointLocationInLoop(OSRLoopFactory factory) {
TestRootNode rootNode = new TestRootNode(osrThreshold, factory, new TestRepeatingNode());
// avoid thread local action being processed right away
context.leave();
AtomicBoolean locationAssertEnabled = new AtomicBoolean(false);
AtomicInteger safepointCounter = new AtomicInteger(0);
Future<?> f = languageEnv.submitThreadLocal(null, new ThreadLocalAction(false, false, true) {
@Override
protected void perform(Access access) {
// recurring
if (locationAssertEnabled.get()) {
// we need to make sure we never observe the OSR root node here.
// we expect either the loop node or the root node of the loop node as location.
Assert.assertTrue(access.getLocation().toString(), access.getLocation() == rootNode || access.getLocation() == rootNode.loopNode);
Assert.assertSame(Truffle.getRuntime().getCurrentFrame().getCallTarget(), rootNode.getCallTarget());
safepointCounter.incrementAndGet();
}
}
});
context.enter();
// enter or close might trigger safepoints, but we have no guarantees on the location there
locationAssertEnabled.set(true);
try {
assertNotCompiled(rootNode.getOSRTarget());
// triggers
rootNode.getCallTarget().call(osrThreshold + 1);
assertCompiled(rootNode.getOSRTarget());
assertTrue(safepointCounter.get() > 0);
} finally {
f.cancel(true);
}
}
use of com.oracle.truffle.api.ThreadLocalAction in project graal by oracle.
the class PolyglotStackFramesRetriever method getStackFrames.
@SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
static FrameInstance[][] getStackFrames(PolyglotContextImpl context) {
Map<Thread, List<FrameInstance>> frameInstancesByThread = new ConcurrentHashMap<>();
Thread[] threads;
Future<Void> future;
synchronized (context) {
threads = context.getSeenThreads().keySet().toArray(new Thread[0]);
if (!context.state.isClosed()) {
future = context.threadLocalActions.submit(null, PolyglotEngineImpl.ENGINE_ID, new ThreadLocalAction(false, false) {
@Override
protected void perform(Access access) {
List<FrameInstance> frameInstances = new ArrayList<>();
Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor<>() {
@Override
public Object visitFrame(FrameInstance frameInstance) {
return frameInstances.add(frameInstance);
}
});
frameInstancesByThread.put(access.getThread(), frameInstances);
}
}, false);
} else {
future = CompletableFuture.completedFuture(null);
}
}
TruffleSafepoint.setBlockedThreadInterruptible(context.uncachedLocation, new TruffleSafepoint.Interruptible<Future<Void>>() {
@Override
public void apply(Future<Void> arg) throws InterruptedException {
try {
arg.get();
} catch (ExecutionException e) {
throw CompilerDirectives.shouldNotReachHere(e);
}
}
}, future);
FrameInstance[][] toRet = new FrameInstance[threads.length][];
for (int i = 0; i < threads.length; i++) {
Thread thread = threads[i];
List<FrameInstance> frameInstances = frameInstancesByThread.get(thread);
if (frameInstances != null) {
toRet[i] = frameInstances.toArray(new FrameInstance[0]);
} else {
toRet[i] = new FrameInstance[0];
}
}
return toRet;
}
use of com.oracle.truffle.api.ThreadLocalAction in project graal by oracle.
the class TruffleSafepointTest method testEventCancellation.
/*
* Test that future cancel actually cancels the event.
*/
@Test
public void testEventCancellation() {
forEachConfig((threads, events) -> {
try (TestSetup setup = setupSafepointLoop(threads, (s, node) -> {
sleepNanosBoundary(50000);
TruffleSafepoint.poll(node);
return false;
})) {
List<Future<Void>> futures = new ArrayList<>();
for (int i = 0; i < events; i++) {
Future<Void> f = (setup.env.submitThreadLocal(null, new ThreadLocalAction(false, false) {
@Override
protected void perform(Access innerAccess) {
}
}));
if (f.cancel(false)) {
assertTrue(f.isDone());
assertTrue(f.isCancelled());
}
futures.add(f);
}
for (Future<Void> future : futures) {
waitOrFail(future);
}
}
});
}
use of com.oracle.truffle.api.ThreadLocalAction in project graal by oracle.
the class TruffleSafepointTest method testEnterSlowPathFallback.
@Test
public void testEnterSlowPathFallback() throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(1);
try {
for (int itNo = 0; itNo < 1000; itNo++) {
CountDownLatch enterLeaveLoopLatch = new CountDownLatch(1);
AtomicReference<Env> envAtomicReference = new AtomicReference<>();
AtomicReference<Context> contextAtomicReference = new AtomicReference<>();
Future<?> testFuture = executorService.submit(() -> {
/*
* The context is closed in main thread. Closing it here in try-with-resources
* block would only make the test more complex.
*/
Context c = createTestContext();
contextAtomicReference.set(c);
c.initialize(ProxyLanguage.ID);
c.enter();
try {
envAtomicReference.set(LanguageContext.get(null).getEnv());
} finally {
c.leave();
}
enterLeaveLoopLatch.countDown();
TruffleContext truffleContext = envAtomicReference.get().getContext();
try {
for (int i = 0; i < 100000; i++) {
Object prev = truffleContext.enter(INVALID_NODE);
truffleContext.leave(INVALID_NODE, prev);
}
} catch (Throwable t) {
if (!"Context execution was cancelled.".equals(t.getMessage())) {
throw t;
}
}
});
enterLeaveLoopLatch.await();
Context c = contextAtomicReference.get();
Env env = envAtomicReference.get();
TruffleContext truffleContext = env.getContext();
/*
* The goal of this test is to check whether the slowpath fallback in thread enter
* guarantees that the thread local action is either polled when the context is
* entered, or the thread is deactivated.
*/
AtomicBoolean threadLocalActionPerformedWhenContextWasInactive = new AtomicBoolean();
Future<?> future = submitThreadLocalInternal(env, null, new ThreadLocalAction(false, false) {
@Override
protected void perform(Access access) {
if (!truffleContext.isActive()) {
threadLocalActionPerformedWhenContextWasInactive.set(true);
}
}
}, false);
c.close(true);
future.get();
testFuture.get();
assertFalse("Action was performed when inactive in iteration " + itNo, threadLocalActionPerformedWhenContextWasInactive.get());
}
} finally {
executorService.shutdownNow();
executorService.awaitTermination(100, TimeUnit.SECONDS);
}
}
Aggregations