-
Bug
-
Resolution: Incomplete
-
P4
-
11, 17, 19, 20
-
generic
-
generic
ADDITIONAL SYSTEM INFORMATION :
MacOS 12.6 (21G115)
Java 19.0.1
A DESCRIPTION OF THE PROBLEM :
Waiting on CompletableFuture.get(timeout, timeunit) can take significantly longer than the provided timeout
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the provided test code. It schedules 5 jobs on the common execution pool. Each job inside creates another job and waits for its result like this with timeout. 4 Of the jobs take 1sec for execution and 2 sec as timeout. The 5th job takes 20sec for execution and 4 sec as timeout. So the get it's supposed to finish with TimeoutException after 4sec.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Code finishes after 4 seconds because of timeout expiration
ACTUAL -
Code takes 20sec to finish
---------- BEGIN SOURCE ----------
import java.util.ArrayList;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Supplier;
public class Main {
public static void main(String[] args) throws Exception {
ArrayList<CompletableFuture<Boolean>> futures = new ArrayList<>();
for (int i = 0; i <= 1; i++) {
futures.add(createJob(1000, 2000));
}
futures.add(createJob(20000, 4000));
long start = System.currentTimeMillis();
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get();
long total = System.currentTimeMillis() - start;
System.out.println("total: " + total);
if (total > 5000) {
throw new RuntimeException("total: " + total);
}
}
public static CompletableFuture<Boolean> createJob(final int sleepTIme, int jobTimeout) {
CompletableFuture<Boolean> job = CompletableFuture.supplyAsync(() -> {
try {
CompletableFuture<Boolean> finish = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(sleepTIme);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return false;
});
return finish.get(jobTimeout, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
throw new CompletionException(e);
} catch (ExecutionException | TimeoutException e) {
e.printStackTrace();
throw new CompletionException(e);
} catch (Throwable t) {
t.printStackTrace();
}
return true;
});
return job.exceptionally(e -> false);
}
}
---------- END SOURCE ----------
FREQUENCY : often
MacOS 12.6 (21G115)
Java 19.0.1
A DESCRIPTION OF THE PROBLEM :
Waiting on CompletableFuture.get(timeout, timeunit) can take significantly longer than the provided timeout
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the provided test code. It schedules 5 jobs on the common execution pool. Each job inside creates another job and waits for its result like this with timeout. 4 Of the jobs take 1sec for execution and 2 sec as timeout. The 5th job takes 20sec for execution and 4 sec as timeout. So the get it's supposed to finish with TimeoutException after 4sec.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Code finishes after 4 seconds because of timeout expiration
ACTUAL -
Code takes 20sec to finish
---------- BEGIN SOURCE ----------
import java.util.ArrayList;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Supplier;
public class Main {
public static void main(String[] args) throws Exception {
ArrayList<CompletableFuture<Boolean>> futures = new ArrayList<>();
for (int i = 0; i <= 1; i++) {
futures.add(createJob(1000, 2000));
}
futures.add(createJob(20000, 4000));
long start = System.currentTimeMillis();
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get();
long total = System.currentTimeMillis() - start;
System.out.println("total: " + total);
if (total > 5000) {
throw new RuntimeException("total: " + total);
}
}
public static CompletableFuture<Boolean> createJob(final int sleepTIme, int jobTimeout) {
CompletableFuture<Boolean> job = CompletableFuture.supplyAsync(() -> {
try {
CompletableFuture<Boolean> finish = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(sleepTIme);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return false;
});
return finish.get(jobTimeout, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
throw new CompletionException(e);
} catch (ExecutionException | TimeoutException e) {
e.printStackTrace();
throw new CompletionException(e);
} catch (Throwable t) {
t.printStackTrace();
}
return true;
});
return job.exceptionally(e -> false);
}
}
---------- END SOURCE ----------
FREQUENCY : often