Release memory for cancelled tasks earlier in TaskExecutor (#13609)

We can save some memory in failure scenarios here (and a tiny bit in
every case) by moving our started flag to the `FutureTask` and using the
callable outright. First of all, we save the wrapper callable, but that
also allows us to just `set(null)` on cancellation instead of waiting
for the task to run to set the `null`.
In case we have longer running tasks executing and it would take a while
to get to the cancelled tasks, this saves some memory and allows us to
return from the method earlier.
This commit is contained in:
Armin Braun 2024-08-21 21:25:09 +02:00 committed by GitHub
parent c52ddd83bf
commit b4a06770c9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -110,21 +110,23 @@ public final class TaskExecutor {
}
RunnableFuture<T> createTask(Callable<T> callable) {
AtomicBoolean startedOrCancelled = new AtomicBoolean(false);
return new FutureTask<>(
() -> {
if (startedOrCancelled.compareAndSet(false, true)) {
try {
return callable.call();
} catch (Throwable t) {
cancelAll();
throw t;
}
}
// task is cancelled hence it has no results to return. That's fine: they would be
// ignored anyway.
return null;
}) {
return new FutureTask<>(callable) {
private final AtomicBoolean startedOrCancelled = new AtomicBoolean(false);
@Override
public void run() {
if (startedOrCancelled.compareAndSet(false, true)) {
super.run();
}
}
@Override
protected void setException(Throwable t) {
super.setException(t);
cancelAll();
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
assert mayInterruptIfRunning == false
@ -136,7 +138,13 @@ public final class TaskExecutor {
wait for them to finish instead of throwing CancellationException. A cleaner way would have been to override FutureTask#get and
make it wait for cancelled tasks, but FutureTask#awaitDone is private. Tasks that are cancelled before they are started will be no-op.
*/
return startedOrCancelled.compareAndSet(false, true);
if (startedOrCancelled.compareAndSet(false, true)) {
// task is cancelled hence it has no results to return. That's fine: they would be
// ignored anyway.
set(null);
return true;
}
return false;
}
};
}