publicstaticvoid main(String[] args) throws InterruptedException { if (!com.android.art.flags.Flags.virtualThreadImplV1()) { return;
} // Exit if the thread throws any exception. Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
System.err.println("thread: " + t.getName());
e.printStackTrace(System.err);
System.exit(1);
});
testNSleepingThreads(2);
testNSleepingThreads(10);
testNSleepingThreads(100); // TODO: Re-enable this test when this test is stable enough in the CI. // testNSleepingThreads(500); // testNSleepingThreads(1000); // testNSleepingThreads(3000);
}
debugPrintln("Start creating test case for " + numOfThreads + " threads sleeping for " +
sleepDurationMs + "ms at " + timeNow()); try (SleepingVirtualThreadTestCase test = new SleepingVirtualThreadTestCase(numOfThreads, sleepDurationMs)) {
debugPrintln("End creating test case for " + numOfThreads + " at " + timeNow());
test.run();
}
}
privatestaticvoid debugPrintln(String msg) { if (DEBUG) {
System.out.println(msg);
}
}
// Use a separate counter because mFinishingCarriers.size() is slow with // --gcstress and --gcverify. privatefinal AtomicInteger mFinishingCarriersCounter;
public SleepingVirtualThreadTestCase(int numOfThreads, long sleepDurationMs) {
mNumOfThreads = numOfThreads;
mSleepDurationMs = sleepDurationMs;
mTimeOutMs = mSleepDurationMs * TIMEOUT_MULTIPLIER;
mRunningTasksCounter = new AtomicInteger(0);
mPendingTasks = new ConcurrentLinkedQueue<>();
mFinishingCarriers = new ConcurrentLinkedQueue<>();
mFinishingCarriersCounter = new AtomicInteger(0);
}
publicvoid run() throws InterruptedException {
debugPrintln("Start inserting tasks at " + timeNow()); for (int i = 0; i < mNumOfThreads; i++) {
Runnable task = () -> { Thread t = startSleepingThread(mSleepDurationMs, mTimer,
mRunningTasksCounter, mPendingTasks, mFinishingCarriers,
mFinishingCarriersCounter);
VirtualThreadContext vtContext = t.getVirtualThreadContext();
debugPrintln("Thread " + vtContext.id + " started at " + timeNow());
};
mPendingTasks.add(task);
}
debugPrintln("End inserting tasks at " + timeNow());
long startTime = nowForElapsedTimeMillis();
debugPrintln("Started at " + timeNow());
int iterations = 0; while (true) { int finishCarriersCount = mFinishingCarriersCounter.get(); if (finishCarriersCount >= mNumOfThreads) { break;
} int runningTaskCount = mRunningTasksCounter.get(); long duration = nowForElapsedTimeMillis() - startTime; if (duration > mTimeOutMs) { thrownew IllegalStateException("Timeout : " + duration + " ms, " + "Expect " + mNumOfThreads + " threads joining, but only " +
finishCarriersCount + " threads joined. " + "Num of running tasks: " + runningTaskCount + ", Num of pending tasks: " + mPendingTasks.size() + ", iterations: " + iterations + ", at: " + timeNow());
} // It's okay to check and increment the threshold non-atomically because // the counter is incremented only on this thread. while (runningTaskCount < CARRIER_THREADS_LIMIT) {
Runnable task = mPendingTasks.poll(); if (task == null) { break;
}
runningTaskCount = mRunningTasksCounter.incrementAndGet();
task.run();
}
iterations++;
}
long duration = nowForElapsedTimeMillis() - startTime;
debugPrintln("Waiting for " + mNumOfThreads + " threads to join after " +
duration + "ms at " + timeNow()); while (!mFinishingCarriers.isEmpty()) { Thread t = mFinishingCarriers.poll(); if (t == null) { continue;
}
t.join(JOIN_TIMEOUT_MS);
}
int pendingCount = mPendingTasks.size(); if (pendingCount != 0) { thrownew IllegalStateException("Expected zero pending tasks, but got " +
pendingCount);
}
duration = nowForElapsedTimeMillis() - startTime;
debugPrintln(mNumOfThreads + " threads joined after " + duration + "ms at " +
timeNow());
}
privatestaticvoid parkVirtual(long sleepDurationMs, Timer timer,
AtomicInteger runningTasksCounter, ConcurrentLinkedQueue<Runnable> pendingTasks,
ConcurrentLinkedQueue<Thread> finishingCarriers,
AtomicInteger finishingCarriersCounter) { long startTime = nowForElapsedTimeMillis(); Thread carrier = JLA.currentCarrierThread();
VirtualThreadContext vtContext = carrier.getVirtualThreadContext(); long virtualThreadId = vtContext.id; // The following runs the 3 tasks // 1. Wait for this carrier thread to join // (and this virtual thread to park successfully) on the timer thread. // 2. After a delay, mark this virtual thread ready to be unparked by the scheduler. // 3. When capacity is available, the main thread unparks this virtual thread.
TimerTask unparkingTask = new TimerTask() {
@Override publicvoid run() {
pendingTasks.add(() -> { Thread th = Thread.unparkVirtual(vtContext);
finishingCarriers.add(th);
finishingCarriersCounter.incrementAndGet();
debugPrintln("Virtual thread is unparked: " + virtualThreadId + " at " + timeNow());
});
}
};
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.