-
Type:
Bug
-
Resolution: Duplicate
-
Priority:
P4
-
None
-
Affects Version/s: 25
-
Component/s: core-libs
-
generic
-
generic
ADDITIONAL SYSTEM INFORMATION :
Windows 11
A DESCRIPTION OF THE PROBLEM :
I was benchmarking one of open-source libraries for database connection pool. I created a few hundreds of Virtual Threads, each one trying to:
1. Get the connection from the pool.
2. Simulate blocking calls by calling Thread.sleep(200).
3. Return connection back to the pool.
I noticed that the whole benchmark took longer than I anticipated, so I started investigation. It turns out that sometimes a Virtual Thread calling Thread.sleep doesn't wake up after the time I requested, but after multiples of this time (two times longer, three times longer, etc.).
I was able to reproduce the issue without this external library, confirming that the library itself is not the source of the problem. I wrote a simple benchmark, polling and returning resources through SynchronousQueue and encountered the same problem with Thread.sleep blocking for multiples of requested time (e.g 400ms, 600ms).
If I change the implementation to wait for platform threads which internally call Thread.sleep (the code I commented), the issue is solved, so it seems the problem is caused by some bug with waking up Virtual Threads from sleep.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the code I attached
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
There are no numbers printed - meaning that all Thread.sleep calls unblocked after less than 300ms
ACTUAL -
There are some numbers printed - meaning that some Thread.sleep calls unblocked after more than 300ms
---------- BEGIN SOURCE ----------
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
void main() {
int initialNumberOfResources = 10;
int numberOfThreads = 300;
var finishedPolling = new AtomicInteger(0);
var executorService = Executors.newVirtualThreadPerTaskExecutor();
// var platformThreadExecutor = Executors.newFixedThreadPool(numberOfThreads);
var tasks = new ArrayList<Future>();
var resources = new SynchronousQueue<Object>(true);
// Try to make 10 resources available
for (int i = 0; i < initialNumberOfResources; i++) {
executorService.submit(() -> {
try {
resources.offer(new Object(), 1, TimeUnit.HOURS);
} catch (InterruptedException e) {
System.exit(1);
}
});
}
// main logic: tries to get a resource from SynchronousQueue,
// simulates some work on the resource by calling Thread.sleep and then releases the resource to other threads through the SynchronousQueue
for (int i = 0; i < numberOfThreads; i++) {
tasks.add(executorService.submit(() -> {
try {
var resource = resources.poll(1, TimeUnit.HOURS);
finishedPolling.incrementAndGet();
long sleepStart = System.nanoTime();
Thread.sleep(200);
// platformThreadExecutor.submit(() -> {
// try {
// Thread.sleep(200);
// } catch (InterruptedException e) {
// e.printStackTrace();
// System.exit(1);
// }
// }).get();
long sleepEnd = System.nanoTime();
long sleepDurationInMs = (sleepEnd - sleepStart) / 1_000_000;
if (sleepDurationInMs > 300) { // if sleep duration is unusually long
IO.println(sleepDurationInMs);
}
// try to return resource to the queue as long as there are other working threads
while (finishedPolling.get() < numberOfThreads && !resources.offer(resource)) {
Thread.sleep(10);
}
} catch (InterruptedException e) {
e.printStackTrace();
System.exit(1);
}
}));
}
// clean up
tasks.forEach(task -> {
try {
task.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
System.exit(1);
}
});
// platformThreadExecutor.shutdownNow();
executorService.shutdownNow();
return;
}
---------- END SOURCE ----------
Windows 11
A DESCRIPTION OF THE PROBLEM :
I was benchmarking one of open-source libraries for database connection pool. I created a few hundreds of Virtual Threads, each one trying to:
1. Get the connection from the pool.
2. Simulate blocking calls by calling Thread.sleep(200).
3. Return connection back to the pool.
I noticed that the whole benchmark took longer than I anticipated, so I started investigation. It turns out that sometimes a Virtual Thread calling Thread.sleep doesn't wake up after the time I requested, but after multiples of this time (two times longer, three times longer, etc.).
I was able to reproduce the issue without this external library, confirming that the library itself is not the source of the problem. I wrote a simple benchmark, polling and returning resources through SynchronousQueue and encountered the same problem with Thread.sleep blocking for multiples of requested time (e.g 400ms, 600ms).
If I change the implementation to wait for platform threads which internally call Thread.sleep (the code I commented), the issue is solved, so it seems the problem is caused by some bug with waking up Virtual Threads from sleep.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the code I attached
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
There are no numbers printed - meaning that all Thread.sleep calls unblocked after less than 300ms
ACTUAL -
There are some numbers printed - meaning that some Thread.sleep calls unblocked after more than 300ms
---------- BEGIN SOURCE ----------
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
void main() {
int initialNumberOfResources = 10;
int numberOfThreads = 300;
var finishedPolling = new AtomicInteger(0);
var executorService = Executors.newVirtualThreadPerTaskExecutor();
// var platformThreadExecutor = Executors.newFixedThreadPool(numberOfThreads);
var tasks = new ArrayList<Future>();
var resources = new SynchronousQueue<Object>(true);
// Try to make 10 resources available
for (int i = 0; i < initialNumberOfResources; i++) {
executorService.submit(() -> {
try {
resources.offer(new Object(), 1, TimeUnit.HOURS);
} catch (InterruptedException e) {
System.exit(1);
}
});
}
// main logic: tries to get a resource from SynchronousQueue,
// simulates some work on the resource by calling Thread.sleep and then releases the resource to other threads through the SynchronousQueue
for (int i = 0; i < numberOfThreads; i++) {
tasks.add(executorService.submit(() -> {
try {
var resource = resources.poll(1, TimeUnit.HOURS);
finishedPolling.incrementAndGet();
long sleepStart = System.nanoTime();
Thread.sleep(200);
// platformThreadExecutor.submit(() -> {
// try {
// Thread.sleep(200);
// } catch (InterruptedException e) {
// e.printStackTrace();
// System.exit(1);
// }
// }).get();
long sleepEnd = System.nanoTime();
long sleepDurationInMs = (sleepEnd - sleepStart) / 1_000_000;
if (sleepDurationInMs > 300) { // if sleep duration is unusually long
IO.println(sleepDurationInMs);
}
// try to return resource to the queue as long as there are other working threads
while (finishedPolling.get() < numberOfThreads && !resources.offer(resource)) {
Thread.sleep(10);
}
} catch (InterruptedException e) {
e.printStackTrace();
System.exit(1);
}
}));
}
// clean up
tasks.forEach(task -> {
try {
task.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
System.exit(1);
}
});
// platformThreadExecutor.shutdownNow();
executorService.shutdownNow();
return;
}
---------- END SOURCE ----------
- duplicates
-
JDK-8370887 DelayScheduler.replace method may break the 4-ary heap in certain scenarios
-
- Resolved
-