@@ -161,16 +161,40 @@ void triggerAsyncActions() {
161
161
}
162
162
}
163
163
164
+ /**
165
+ * It's fine that there is a race between checking the hasScheduledAction flag and processing
166
+ * actions, we use the executingScheduledActions lock to ensure that only one thread is
167
+ * processing and that no asynchronous handler thread would set it again while we're processing.
168
+ * While the nice scenario would be any variation of:
169
+ *
170
+ * <ul>
171
+ * <li>Thread2 - acquireLock, pushWork, setFlag, releaseLock</li>
172
+ * <li>Thread1 - checkFlag, acquireLock, resetFlag, processActions, releaseLock</li>
173
+ * </ul>
174
+ *
175
+ * <ul>
176
+ * <li>Thread1 - checkFlag</li>
177
+ * <li>Thread2 - acquireLock, pushWork, setFlag, releaseLock</li>
178
+ * <li>Thread1 - acquireLock, resetFlag, processActions, releaseLock</li>
179
+ * </ul>
180
+ *
181
+ * it's also fine if we get into a race for example like this:
182
+ *
183
+ * <ul>
184
+ * <li>Thread2 - acquireLock, pushWork, setFlag</li>
185
+ * <li>Thread1 - checkFlag, tryAcquireLock, bail out</li>
186
+ * <li>Thread2 - releaseLock</li>
187
+ * </ul>
188
+ *
189
+ * because Thread1 is sure to check the flag again soon enough, and very likely much sooner than
190
+ * the {@value #ASYNC_ACTION_DELAY} ms delay between successive runs of the async handler
191
+ * threads (Thread2 in this example). Of course, there can be more than one handler thread, but
192
+ * it's unlikely that there are so many that it would completely saturate the ability to process
193
+ * async actions on the main thread, because there's only one per "type" of async thing (e.g. 1
194
+ * for weakref finalizers, 1 for signals, 1 for destructors).
195
+ */
164
196
@ TruffleBoundary
165
197
private void processAsyncActions () {
166
- // We'll likely be able to get the lock and start working through the async actions. But
167
- // there could be a race between the atomic switch of the hasScheduledAction flag, an
168
- // AsyncRunnable thread and the acquisition of the lock, so a second thread may end up
169
- // clearing the flag again. In that case, both would get here, but only will get the lock
170
- // and the other will skip over this and return. In any case, by the time a thread has
171
- // this lock and is handling async actions, nothing new will be pushed to the
172
- // scheduledActions queue, so we won't have a race between finishing the while loop and
173
- // returning from this method.
174
198
if (executingScheduledActions .tryLock ()) {
175
199
hasScheduledAction = false ;
176
200
try {
0 commit comments