Skip to content

Commit

Permalink
Replace Sleep with While Loop in WorkerPoolTest
Browse files Browse the repository at this point in the history
Summary:
Replaced a 5-second sleep used to wait for worker threads to become idle in the WorkerPoolTest with a while loop. This change includes a timeout of 5 seconds to check when there are fewer than 50 workers, making the test more responsive and reliable.

Changes:
- Removed the `Thread.sleep(5 * 1000L);` line that was used to wait until worker threads become idle.
- Added a while loop with a 5-second timeout to actively check for the condition of fewer than 50 active worker threads.
- Updated test annotations.

Benefits:
This change makes the test more robust by actively checking the condition instead of relying on a fixed sleep time. It also aligns with the modern testing approach and ensures the test does not hang indefinitely.
  • Loading branch information
CodeLtDave committed Aug 8, 2023
1 parent 668133e commit ff56cb6
Showing 1 changed file with 72 additions and 25 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2017 salesforce.com.
* Copyright (c) 2017 salesforce.com, 2023 Vector Informatik GmbH.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand All @@ -10,37 +10,68 @@
*
* Contributors:
* salesforce.com - initial API and implementation
* Vector Informatik GmbH - runtime and structure improvements
*
*******************************************************************************/
package org.eclipse.core.tests.runtime.jobs;

import static org.junit.Assert.assertTrue;

import java.util.concurrent.*;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import org.eclipse.core.internal.jobs.Worker;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.junit.Assert;
import org.junit.Test;

@SuppressWarnings("restriction")
public class WorkerPoolTest {
@Test
public void testIdleWorkerCap() throws Exception {
// See org.eclipse.core.internal.jobs.WorkerPool.MAX_THREADS
final int MAX_THREADS = 50;

// number of concurrent jobs
final int count = MAX_THREADS * 10;
/** See org.eclipse.core.internal.jobs.WorkerPool.MAX_THREADS */
private static final int MAX_ALLOWED_IDLE_WORKER_THREADS = 50;
/** Multiplier to create an excess of threads */
private static final int THREAD_MULTIPLIER = 10;
/** Maximum time in seconds before job start barrier timeout */
private static final int BARRIER_TIMEOUT_IN_SEC = 100;
/** Maximum time in milliseconds to before test failure */
private static final int WORKER_TIMEOUT_IN_MSEC = 5000;

// cyclic barrier for count worker threads + one test thread
final CyclicBarrier barrier = new CyclicBarrier(count + 1);
/**
* Tests the upper limit of idle worker threads allowed in the job system after
* scheduling a large number of concurrent jobs.
* <p>
* This test performs the following steps:
* <ul>
* <li>Initializes a cyclic barrier with a count based on a multiplier of the
* defined maximum worker threads.</li>
* <li>Schedules a number of jobs equal to the multiplier of the maximum worker
* threads. Each job waits on the barrier until all jobs are running.</li>
* <li>Monitors the number of active worker threads. Continuously polls until
* the count of active worker threads is less than or equal to the defined
* maximum or a timeout occurs.</li>
* </ul>
* </p>
*
* @throws Exception If the barrier timeout runs out
*
* @see <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=516609"> Bug
* 516609 </a>
*/
@Test
public void testIdleWorkerCap() throws Exception {
final int totalJobs = MAX_ALLOWED_IDLE_WORKER_THREADS * THREAD_MULTIPLIER;
final CyclicBarrier parallelJobStartBarrier = new CyclicBarrier(totalJobs + 1);

// start count concurrent jobs
for (int i = 0; i < count; i++) {
for (int i = 0; i < totalJobs; i++) {
new Job("testIdleWorkerCap-" + i) {
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
barrier.await();
parallelJobStartBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
return Status.CANCEL_STATUS;
}
Expand All @@ -50,22 +81,38 @@ protected IStatus run(IProgressMonitor monitor) {
}

// wait for jobs to reach the barrier
barrier.await(10, TimeUnit.SECONDS);
parallelJobStartBarrier.await(BARRIER_TIMEOUT_IN_SEC, TimeUnit.SECONDS);

long startTime = System.currentTimeMillis();

// this is the ugly part, wait until worker threads become idle
Thread.sleep(5 * 1000L);
// wait for workerThreadCount to sink below MAX_ALLOWED_IDLE_WORKER_THREADS
while (true) {
int workerThreadCount = getWorkerThreadCount();

if (workerThreadCount <= MAX_ALLOWED_IDLE_WORKER_THREADS) {
break; // Exit loop when the condition is met
}

// count worker threads, must be less than WorkerPool.MAX_THREADS
if (System.currentTimeMillis() - startTime > WORKER_TIMEOUT_IN_MSEC) {
Assert.fail("Timeout reached! Too many worker threads active: " + workerThreadCount + ", expected <= "
+ MAX_ALLOWED_IDLE_WORKER_THREADS);
}

Thread.yield();
}
}

private int getWorkerThreadCount() {
Thread[] threads = new Thread[Thread.activeCount() * 2];
int tcount = Thread.enumerate(threads);
assertTrue("Too many active threads: " + tcount, tcount < threads.length);
int wcount = 0;
for (int i = 0; i < tcount; i++) {
int enumeratedThreadCount = Thread.enumerate(threads);
assertTrue("Too many active threads: " + enumeratedThreadCount, enumeratedThreadCount < threads.length);

int workerThreadCount = 0;
for (int i = 0; i < enumeratedThreadCount; i++) {
if (threads[i] instanceof Worker) {
wcount++;
workerThreadCount++;
}
}
assertTrue("Too many worker threads active: " + wcount + ", must be <= " + MAX_THREADS, wcount <= MAX_THREADS);
return workerThreadCount;
}

}

0 comments on commit ff56cb6

Please sign in to comment.