Skip to content

Commit

Permalink
Avoid throwing PolyglotEngineException.illegalState while holding an …
Browse files Browse the repository at this point in the history
…internal lock
  • Loading branch information
Jaroslav Tulach committed Jan 29, 2024
1 parent 9d05067 commit 82b14c3
Show file tree
Hide file tree
Showing 4 changed files with 253 additions and 166 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1150,7 +1150,11 @@ public Thread createThread(Object polyglotLanguageContext, Runnable runnable, Ob
threadContext = innerContext.getContext(threadContext.language);
}
PolyglotThread newThread = new PolyglotThread(threadContext, runnable, group, stackSize, beforeEnter, afterLeave);
threadContext.context.checkMultiThreadedAccess(newThread);
try {
threadContext.context.checkMultiThreadedAccess(newThread);
} catch (PolyglotThreadAccessException ex) {
throw ex.rethrow(threadContext.context);
}
return newThread;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -837,131 +837,145 @@ Object[] enterThreadChanged(boolean enterReverted, boolean pollSafepoint, boolea
assert !mustSucceed;
throw PolyglotEngineException.illegalState("Context cannot be entered in polyglot thread's beforeEnter or afterLeave notifications.");
}
boolean needsInitialization = false;
synchronized (this) {
PolyglotThreadInfo threadInfo = getCurrentThreadInfo();
PolyglotThreadAccessException threadAccessException = null;
LOOP: for (;;) {
if (threadAccessException != null) {
throw threadAccessException.rethrow(this);
}
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 (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() || state == State.CLOSED_INTERRUPTED) && !threadInfo.isActive()) {
Thread.interrupted();
notifyAll();
}
}
if ((state.isInterrupting() || state == State.CLOSED_INTERRUPTED) && !threadInfo.isActive()) {
Thread.interrupted();
notifyAll();
if (deactivateSafepoints && threadInfo != PolyglotThreadInfo.NULL) {
threadLocalActions.notifyThreadActivation(threadInfo, false);
}
}
if (deactivateSafepoints && threadInfo != PolyglotThreadInfo.NULL) {
threadLocalActions.notifyThreadActivation(threadInfo, false);
}

assert threadInfo != null;
if (!leaveAndEnter) {
checkClosedOrDisposing(mustSucceed);
if (threadInfo.isInLeaveAndEnter()) {
throw PolyglotEngineException.illegalState("Context cannot be entered inside leaveAndEnter.");
assert threadInfo != null;
if (!leaveAndEnter) {
checkClosedOrDisposing(mustSucceed);
if (threadInfo.isInLeaveAndEnter()) {
throw PolyglotEngineException.illegalState("Context cannot be entered inside leaveAndEnter.");
}
}
}

threadInfo = threads.get(current);
if (threadInfo == null) {
threadInfo = createThreadInfo(current, polyglotThreadFirstEnter);
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, false);

if (transitionToMultiThreading) {
// recheck all thread accesses
checkAllThreadAccesses(Thread.currentThread(), false);
}
threadInfo = threads.get(current);
if (threadInfo == null) {
try {
threadInfo = createThreadInfo(current, polyglotThreadFirstEnter);
} catch (PolyglotThreadAccessException ex) {
threadAccessException = ex;
continue LOOP;
}
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, false);

if (transitionToMultiThreading) {
try {
// recheck all thread accesses
checkAllThreadAccesses(Thread.currentThread(), false);
} catch (PolyglotThreadAccessException ex) {
threadAccessException = ex;
continue LOOP;
}
}

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 (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) {
threads.put(current, threadInfo);
}

if (needsInitialization) {
/*
* Do not enter the thread before initializing thread locals. Creation of
* thread locals might fail.
*/
initializeThreadLocals(threadInfo);
}
if (needsInitialization) {
/*
* Do not enter the thread before initializing thread locals. Creation of
* thread locals might fail.
*/
initializeThreadLocals(threadInfo);
}

prev = threadInfo.enterInternal();
if (leaveAndEnter) {
threadInfo.setLeaveAndEnterInterrupter(null);
notifyAll();
}
if (needsInitialization) {
this.threadLocalActions.notifyEnterCreatedThread();
}
if (closingThread != Thread.currentThread()) {
try {
threadInfo.notifyEnter(engine, this);
} catch (Throwable t) {
threadInfo.leaveInternal(prev);
throw t;
prev = threadInfo.enterInternal();
if (leaveAndEnter) {
threadInfo.setLeaveAndEnterInterrupter(null);
notifyAll();
}
}
enteredThread = threadInfo;
if (needsInitialization) {
this.threadLocalActions.notifyEnterCreatedThread();
}
if (closingThread != Thread.currentThread()) {
try {
threadInfo.notifyEnter(engine, this);
} catch (Throwable t) {
threadInfo.leaveInternal(prev);
throw t;
}
}
enteredThread = threadInfo;

// 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);
}
// 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(mustSucceed);
}
if (transitionToMultiThreading) {
// we need to verify that all languages give access
// to all threads in multi-threaded mode.
transitionToMultiThreaded(mustSucceed);
}

if (needsInitialization) {
initializeNewThread(enteredThread, mustSucceed);
}
if (needsInitialization) {
initializeNewThread(enteredThread, mustSucceed);
}

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));
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);
// never cache last thread on close or when closingThread
setCachedThreadInfo(threadInfo);
}
if (needsInitialization) {
EngineAccessor.INSTRUMENT.notifyThreadStarted(engine, creatorTruffleContext, current);
}
return prev;
}

return prev;
} finally {
/*
* We need to always poll the safepoint here in case we already submitted a thread
Expand Down Expand Up @@ -1023,12 +1037,12 @@ void setCachedThreadInfo(PolyglotThreadInfo info) {
}
}

synchronized void checkMultiThreadedAccess(PolyglotThread newThread) {
synchronized void checkMultiThreadedAccess(PolyglotThread newThread) throws PolyglotThreadAccessException {
boolean singleThread = singleThreaded ? !isActiveNotCancelled() : false;
checkAllThreadAccesses(newThread, singleThread);
}

private void checkAllThreadAccesses(Thread enteringThread, boolean singleThread) {
private void checkAllThreadAccesses(Thread enteringThread, boolean singleThread) throws PolyglotThreadAccessException {
assert Thread.holdsLock(this);
List<PolyglotLanguage> deniedLanguages = null;
for (PolyglotLanguageContext context : contexts) {
Expand Down Expand Up @@ -1350,7 +1364,7 @@ private void transitionToMultiThreaded(boolean mustSucceed) {
volatileStatementCounter.getAndAdd(-statementsExecuted);
}

private PolyglotThreadInfo createThreadInfo(Thread current, boolean polyglotThreadFirstEnter) {
private PolyglotThreadInfo createThreadInfo(Thread current, boolean polyglotThreadFirstEnter) throws PolyglotThreadAccessException {
assert Thread.holdsLock(this);
PolyglotThreadInfo threadInfo = new PolyglotThreadInfo(this, current, polyglotThreadFirstEnter);

Expand All @@ -1375,7 +1389,7 @@ private PolyglotThreadInfo createThreadInfo(Thread current, boolean polyglotThre
return threadInfo;
}

static RuntimeException throwDeniedThreadAccess(Thread current, boolean accessSingleThreaded, List<PolyglotLanguage> deniedLanguages) {
static RuntimeException throwDeniedThreadAccess(Thread current, boolean accessSingleThreaded, List<PolyglotLanguage> deniedLanguages) throws PolyglotThreadAccessException {
String message;
StringBuilder languagesString = new StringBuilder("");
for (PolyglotLanguage language : deniedLanguages) {
Expand All @@ -1389,7 +1403,7 @@ static RuntimeException throwDeniedThreadAccess(Thread current, boolean accessSi
} else {
message = String.format("Multi threaded access requested by thread %s but is not allowed for language(s) %s.", current, languagesString);
}
throw PolyglotEngineException.illegalState(message);
throw new PolyglotThreadAccessException(message);
}

public Object getBindings(String languageId) {
Expand Down
Loading

0 comments on commit 82b14c3

Please sign in to comment.