use of com.oracle.truffle.api.TruffleSafepoint in project graal by oracle.
the class PolyglotContextImpl method finalizeContext.
private void finalizeContext(boolean notifyInstruments, boolean cancelOrExitOperation) {
// we need to run finalization at least twice in case a finalization run has
// initialized new contexts
TruffleSafepoint safepoint = TruffleSafepoint.getCurrent();
PolyglotThreadLocalActions.TL_HANDSHAKE.setChangeAllowActions(safepoint, true);
try {
boolean finalizationPerformed;
do {
finalizationPerformed = false;
// disposal/finalization
for (int i = contexts.length - 1; i >= 0; i--) {
PolyglotLanguageContext context = contexts[i];
if (context.isInitialized()) {
try {
finalizationPerformed |= context.finalizeContext(cancelOrExitOperation, notifyInstruments);
} finally {
if (!PolyglotThreadLocalActions.TL_HANDSHAKE.isAllowActions(safepoint)) {
safepoint.setAllowActions(true);
throw new IllegalStateException("TruffleSafepoint.setAllowActions is still disabled even though finalization completed. Make sure allow actions are reset in a finally block.");
}
}
}
}
} while (finalizationPerformed);
} finally {
PolyglotThreadLocalActions.TL_HANDSHAKE.setChangeAllowActions(safepoint, false);
}
}
use of com.oracle.truffle.api.TruffleSafepoint in project graal by oracle.
the class TruffleSafepointTest method testBlockedAndSafepoints.
/*
* This test case is inspired by the ruby use case of disabling side-effects and block to wake
* up to allow side-effecting effects a forced location.
*/
@Test
public void testBlockedAndSafepoints() {
forEachConfig((threads, events) -> {
Semaphore semaphore = new Semaphore(threads);
try (TestSetup setup = setupSafepointLoop(threads, (s, node) -> {
TruffleSafepoint safepoint = TruffleSafepoint.getCurrent();
lockCooperativelySafepoint(semaphore, node, safepoint);
return false;
})) {
try {
AtomicInteger eventCounter = new AtomicInteger();
List<Future<?>> threadLocals = new ArrayList<>();
for (int i = 0; i < events; i++) {
threadLocals.add(setup.env.submitThreadLocal(null, new ThreadLocalAction(true, false) {
@Override
protected void perform(Access access) {
assertNotSame(INVALID_NODE, access.getLocation());
assertSame(setup.target.getRootNode(), access.getLocation());
eventCounter.incrementAndGet();
}
}));
// this encourages race conditions between setting blocked and unlocking
if (i == Math.floorDiv(events, 2)) {
for (int j = 0; j < threads; j++) {
acquire(semaphore);
}
}
}
// wait for all events to complete so we can reliably assert the events
for (Future<?> f : threadLocals) {
waitOrFail(f);
}
assertEquals(events * threads, eventCounter.get());
} finally {
// let the threads complete in an orderly fashing
// events can happen in any order
semaphore.release(threads);
}
}
});
}
use of com.oracle.truffle.api.TruffleSafepoint in project graal by oracle.
the class TruffleSafepointTest method testConditionAndSafepoints.
@Test
public void testConditionAndSafepoints() {
forEachConfig((threads, events) -> {
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
AtomicBoolean done = new AtomicBoolean(false);
AtomicInteger inAwait = new AtomicInteger(0);
try (TestSetup setup = setupSafepointLoop(threads, (s, node) -> {
TruffleSafepoint safepoint = TruffleSafepoint.getCurrent();
// No lockInterruptibly()/setBlocked() here, `lock` is never held during a poll()
// It can also be required by language semantics if only the await() should be
// interruptible and not the lock().
lockBoundary(lock);
try {
while (!done.get()) {
safepoint.setBlockedWithException(node, Interrupter.THREAD_INTERRUPT, (c) -> {
// When await() is interrupted, it still needs to
// reacquire the lock before the InterruptedException
// can propagate. So we must unlock once we're out to
// let other threads reach the safepoint too.
inAwait.incrementAndGet();
try {
c.await();
} finally {
inAwait.decrementAndGet();
}
}, condition, lock::unlock, (t) -> lock.lock());
}
} finally {
unlockBoundary(lock);
}
// only run once
return true;
})) {
AtomicInteger eventCounter = new AtomicInteger();
// Wait all threads are inside await()
while (inAwait.get() < threads || lock.isLocked()) {
Thread.yield();
}
List<Future<?>> threadLocals = new ArrayList<>();
for (int i = 0; i < events; i++) {
threadLocals.add(setup.env.submitThreadLocal(null, new ThreadLocalAction(false, true) {
@Override
protected void perform(Access access) {
Assert.assertFalse(lock.isHeldByCurrentThread());
eventCounter.incrementAndGet();
}
}));
}
// wait for all events to complete so we can reliably assert the events
for (Future<?> f : threadLocals) {
waitOrFail(f);
}
assertEquals(events * threads, eventCounter.get());
// Wait all threads are in condition.await(), otherwise signalAll() doesn't work
while (inAwait.get() < threads || lock.isLocked()) {
Thread.yield();
}
lock.lock();
try {
done.set(true);
condition.signalAll();
} finally {
lock.unlock();
}
}
});
}
use of com.oracle.truffle.api.TruffleSafepoint in project graal by oracle.
the class TruffleSafepointTest method testSideEffecting.
@Test
public void testSideEffecting() {
forEachConfig((threads, events) -> {
AtomicBoolean stopped = new AtomicBoolean(false);
try (TestSetup setup = setupSafepointLoop(threads, (s, node) -> {
TruffleSafepoint config = TruffleSafepoint.getCurrent();
boolean prev = config.setAllowSideEffects(false);
try {
while (true) {
TruffleSafepoint.poll(node);
if (isStopped(stopped)) {
return true;
}
}
} finally {
config.setAllowSideEffects(prev);
}
})) {
AtomicInteger eventCounter = new AtomicInteger();
ActionCollector runnable = new ActionCollector(setup, eventCounter, true, false);
for (int i = 0; i < events; i++) {
setup.env.submitThreadLocal(null, runnable);
}
assertEquals(0, eventCounter.get());
stopped.set(true);
setup.stopAndAwait();
assertActionsAnyOrder(threads, events, runnable);
}
});
}
use of com.oracle.truffle.api.TruffleSafepoint in project graal by oracle.
the class BlockingSupport method enterBlockingRegion.
/**
* Enters a {@link Interruptible} operation on the current thread. This execution will have the
* following properties:
* <ul>
* <li>{@link TruffleSafepoint safepoints} will still be handled.</li>
* <li>Will throw a {@link GuestInterruptedException} if {@link #guestInterrupt(Thread, Object)}
* was called on this thread.</li>
* </ul>
* Furthermore, no host interruptions of the current thread will result in an
* {@link InterruptedException}.
*
* As such, there are only three ways to retrieve control from a call to this method:
* <ul>
* <li>The given {@linkplain Interruptible blockingRegion} naturally completes</li>
* <li>{@link #guestInterrupt(Thread, Object)} is called for this thread.</li>
* <li>An {@link ThreadLocalAction action} was submitted to this thread, that throws an
* exception that is not an {@link InterruptedException}.</li>
* </ul>
*
* @throws GuestInterruptedException if the current thread was guest-interrupted.
*/
@TruffleBoundary
public <U> void enterBlockingRegion(Interruptible<U> blockingRegion, Node location, U object) throws GuestInterruptedException {
TruffleSafepoint safepoint = TruffleSafepoint.getCurrent();
safepoint.setBlockedWithException(location, guestInterrupter, blockingRegion, object, null, guestInterrupter::afterInterrupt);
}
Aggregations