Skip to content

Commit f75f027

Browse files
committed
use a lock instead of a second AtomicBoolean to guard the scheduledActions list while some thread is processing it + don't force interpreter transfer, just end of PE when we trigger the actions
1 parent 20ee2bb commit f75f027

File tree

1 file changed

+34
-10
lines changed
  • graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime

1 file changed

+34
-10
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/AsyncHandler.java

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@
4646
import java.util.concurrent.ScheduledExecutorService;
4747
import java.util.concurrent.TimeUnit;
4848
import java.util.concurrent.atomic.AtomicBoolean;
49+
import java.util.concurrent.locks.Lock;
50+
import java.util.concurrent.locks.ReentrantLock;
4951
import java.util.function.Supplier;
5052

5153
import com.oracle.graal.python.PythonLanguage;
@@ -55,6 +57,7 @@
5557
import com.oracle.truffle.api.CompilerDirectives;
5658
import com.oracle.truffle.api.Truffle;
5759
import com.oracle.truffle.api.TruffleLanguage;
60+
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
5861
import com.oracle.truffle.api.frame.VirtualFrame;
5962
import com.oracle.truffle.api.nodes.Node.Child;
6063
import com.oracle.truffle.api.nodes.RootNode;
@@ -90,9 +93,9 @@ default int frameIndex() {
9093
}
9194

9295
private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2);
93-
private ConcurrentLinkedQueue<AsyncAction> scheduledActions = new ConcurrentLinkedQueue<>();
94-
private AtomicBoolean hasScheduledAction = new AtomicBoolean(false);
95-
private AtomicBoolean executingActions = new AtomicBoolean(false);
96+
private final ConcurrentLinkedQueue<AsyncAction> scheduledActions = new ConcurrentLinkedQueue<>();
97+
private final AtomicBoolean hasScheduledAction = new AtomicBoolean(false);
98+
private final Lock executingScheduledActions = new ReentrantLock();
9699
private static final int ASYNC_ACTION_DELAY = 15; // chosen by a fair D20 dice roll
97100

98101
private class AsyncRunnable implements Runnable {
@@ -105,8 +108,15 @@ public AsyncRunnable(Supplier<AsyncAction> actionSupplier) {
105108
public void run() {
106109
AsyncAction asyncAction = actionSupplier.get();
107110
if (asyncAction != null) {
108-
scheduledActions.add(asyncAction);
109-
hasScheduledAction.set(true);
111+
// If there's thread executing scheduled actions right now,
112+
// we wait until adding the next work item
113+
executingScheduledActions.lock();
114+
try {
115+
scheduledActions.add(asyncAction);
116+
hasScheduledAction.set(true);
117+
} finally {
118+
executingScheduledActions.unlock();
119+
}
110120
}
111121
}
112122
}
@@ -144,10 +154,23 @@ void registerAction(Supplier<AsyncAction> actionSupplier) {
144154
}
145155

146156
void triggerAsyncActions() {
147-
if (executingActions.compareAndSet(false, true)) {
148-
if (hasScheduledAction.compareAndSet(true, false)) {
149-
CompilerDirectives.transferToInterpreter();
150-
// TODO: (tfel) - for now all async actions are slow path
157+
if (hasScheduledAction.compareAndSet(true, false)) {
158+
processAsyncActions();
159+
}
160+
}
161+
162+
@TruffleBoundary
163+
private void processAsyncActions() {
164+
// We'll likely be able to get the lock and start working through the async actions. But
165+
// there could be a race between the atomic switch of the hasScheduledAction flag, an
166+
// AsyncRunnable thread and the acquisition of the lock, so a second thread may end up
167+
// clearing the flag again. In that case, both would get here, but only will get the lock
168+
// and the other will skip over this and return. In any case, by the time a thread has
169+
// this lock and is handling async actions, nothing new will be pushed to the
170+
// scheduledActions queue, so we won't have a race between finishing the while loop and
171+
// returning from this method.
172+
if (executingScheduledActions.tryLock()) {
173+
try {
151174
ConcurrentLinkedQueue<AsyncAction> actions = scheduledActions;
152175
AsyncAction action;
153176
while ((action = actions.poll()) != null) {
@@ -169,8 +192,9 @@ void triggerAsyncActions() {
169192
}
170193
}
171194
}
195+
} finally {
196+
executingScheduledActions.unlock();
172197
}
173-
executingActions.set(false);
174198
}
175199
}
176200
}

0 commit comments

Comments
 (0)