Compare commits

...

1 Commits

Author SHA1 Message Date
Anton Tarasov
97d63a036e JBR-4106 [update_1] PyCharm hangs with 100% CPU usage on one core 2022-02-18 12:19:41 +03:00
3 changed files with 110 additions and 65 deletions

View File

@@ -23,7 +23,7 @@ import static helper.ToolkitTestHelper.*;
*/
@SuppressWarnings("ConstantConditions")
public class AWTThreadingTest {
static final int TIMEOUT_SEC = 1;
static final int TIMEOUT_SECONDS = 1;
static final AtomicInteger ITER_COUNTER = new AtomicInteger();
static final AtomicBoolean DUMP_STACK = new AtomicBoolean(false);
@@ -33,33 +33,32 @@ public class AWTThreadingTest {
public static void main(String[] args) {
DUMP_STACK.set(args.length > 0 && "dumpStack".equals(args[0]));
initTest(AWTThreadingTest.class, Thread.currentThread());
initTest(AWTThreadingTest.class);
test("certain threads superposition");
testCase("certain threads superposition", AWTThreadingTest::test);
test("random threads superposition");
testCase("random threads superposition", AWTThreadingTest::test);
System.out.println("Test PASSED");
}
static void test(String testCaseCaption) {
initTestCase(testCaseCaption);
static void test() {
ITER_COUNTER.set(0);
EventQueue.invokeLater(() -> startThread(FUTURE::isDone));
var timer = new TestTimer(TIMEOUT_SECONDS * 3, TimeUnit.SECONDS);
EventQueue.invokeLater(() -> startThread(() ->
FUTURE.isDone() ||
timer.hasFinished()));
try {
FUTURE.get(TIMEOUT_SEC * 3, TimeUnit.SECONDS);
} catch (TimeoutException ignored) {
// expected result
FUTURE.complete(true);
} catch (Exception e) {
throw new RuntimeException("Test FAILED!");
}
tryRun(() -> {
if (!FUTURE.get(TIMEOUT_SECONDS * 4, TimeUnit.SECONDS)) {
throw new RuntimeException("Test FAILED! (negative result)");
}
});
trycatch(THREAD::join);
tryRun(THREAD::join);
finishTestCase("(" + ITER_COUNTER + " iterations)");
System.out.println(ITER_COUNTER + " iterations passed");
}
static void startThread(Supplier<Boolean> shouldExitLoop) {
@@ -78,14 +77,14 @@ public class AWTThreadingTest {
//
CThreading.executeOnAppKit(() -> {
// We're on AppKit, wait for the 2nd invocation to be on the AWTThreading-pool thread.
if (TEST_CASE == 1) await(point_1, TIMEOUT_SEC);
if (TEST_CASE == 1) await(point_1, TIMEOUT_SECONDS);
trycatch(() -> LWCToolkit.invokeAndWait(() -> {
tryRun(() -> LWCToolkit.invokeAndWait(() -> {
// We're being dispatched on EDT.
if (TEST_CASE == 1) point_2.countDown();
// Wait for the 2nd invocation to be executed on AppKit.
if (TEST_CASE == 1) await(point_3, TIMEOUT_SEC);
if (TEST_CASE == 1) await(point_3, TIMEOUT_SECONDS);
}, FRAME));
invocations.countDown();
@@ -99,7 +98,7 @@ public class AWTThreadingTest {
if (TEST_CASE == 1) point_1.countDown();
// Wait for the 1st invocation to start NSRunLoop and be dispatched
if (TEST_CASE == 1) await(point_2, TIMEOUT_SEC);
if (TEST_CASE == 1) await(point_2, TIMEOUT_SECONDS);
// Perform in JavaRunLoopMode to be accepted by NSRunLoop started by LWCToolkit.invokeAndWait.
LWCToolkit.performOnMainThreadAndWait(() -> {
@@ -113,15 +112,17 @@ public class AWTThreadingTest {
invocations.countDown();
}));
await(invocations, TIMEOUT_SEC * 2);
}
await(invocations, TIMEOUT_SECONDS * 2);
} // while
FUTURE.complete(true);
});
THREAD.setDaemon(true);
THREAD.start();
}
static void await(CountDownLatch latch, int seconds) {
if (!trycatchAndReturn(() -> latch.await(seconds, TimeUnit.SECONDS), false)) {
if (!tryCall(() -> latch.await(seconds, TimeUnit.SECONDS), false)) {
FUTURE.completeExceptionally(new Throwable("Awaiting has timed out"));
}
}

View File

@@ -39,7 +39,7 @@ public class LWCToolkitInvokeAndWaitTest {
if (EDT_FAST_FREE_LATCH != null) {
// 1. wait for the invocation event to be dispatched
// 2. wait for EDT to become free
trycatch(EDT_FAST_FREE_LATCH::await);
tryRun(EDT_FAST_FREE_LATCH::await);
EDT_FAST_FREE_LATCH = null;
}
@@ -60,7 +60,7 @@ public class LWCToolkitInvokeAndWaitTest {
}
public static void main(String[] args) {
trycatch(() -> {
tryRun(() -> {
Logger log = LogManager.getLogManager().getLogger(AWTThreading.class.getName());
log.setUseParentHandlers(false);
log.addHandler(LOG_HANDLER);
@@ -71,53 +71,50 @@ public class LWCToolkitInvokeAndWaitTest {
Consumer<InvocationEvent> noop = e -> {};
initTest(LWCToolkitInvokeAndWaitTest.class, Thread.currentThread());
initTest(LWCToolkitInvokeAndWaitTest.class);
test("InvocationEvent is normally dispatched",
testCase("InvocationEvent is normally dispatched", () -> test(
"",
noop,
() -> System.out.println("I'm dispatched"));
() -> System.out.println("I'm dispatched")));
test("InvocationEvent is lost",
testCase("InvocationEvent is lost", () -> test(
"lost",
noop,
CONSUME_DISPATCHING);
CONSUME_DISPATCHING));
EDT_FAST_FREE_LATCH = new CountDownLatch(2);
test("InvocationEvent is lost (EDT becomes fast free)",
testCase("InvocationEvent is lost (EDT becomes fast free)", () -> test(
"lost",
// notify the invocationEvent has been dispatched
invocationEvent -> EDT_FAST_FREE_LATCH.countDown(),
CONSUME_DISPATCHING);
CONSUME_DISPATCHING));
test("InvocationEvent is disposed",
testCase("InvocationEvent is disposed", () -> test(
"disposed",
invocationEvent -> AWTAccessor.getInvocationEventAccessor().dispose(invocationEvent),
CONSUME_DISPATCHING);
CONSUME_DISPATCHING));
test("InvocationEvent is timed out (delayed before dispatching)",
testCase("InvocationEvent is timed out (delayed before dispatching)", () -> test(
"timed out",
invocationEvent -> sleep(INVOKE_TIMEOUT_SECONDS * 4),
CONSUME_DISPATCHING);
CONSUME_DISPATCHING));
test("InvocationEvent is timed out (delayed during dispatching)",
testCase("InvocationEvent is timed out (delayed during dispatching)", () -> test(
"timed out",
noop,
() -> sleep(INVOKE_TIMEOUT_SECONDS * 4));
() -> sleep(INVOKE_TIMEOUT_SECONDS * 4)));
System.out.println("Test PASSED");
}
static void test(String testCaseCaption,
String expectedInLog,
static void test(String expectedInLog,
Consumer<InvocationEvent> onBeforeDispatching,
Runnable onDispatching)
{
initTestCase(testCaseCaption);
EventQueue.invokeLater(() -> subTest(onBeforeDispatching, onDispatching));
trycatch(() -> {
tryRun(() -> {
if (!FUTURE.get(INVOKE_TIMEOUT_SECONDS * 2L, TimeUnit.SECONDS)) {
throw new RuntimeException("Test FAILED! (negative result)");
}
@@ -126,14 +123,12 @@ public class LWCToolkitInvokeAndWaitTest {
// let AppKit and EDT print all the logging
var latch = new CountDownLatch(1);
CThreading.executeOnAppKit(latch::countDown);
trycatch(latch::await);
trycatch(() -> EventQueue.invokeAndWait(() -> {}));
tryRun(latch::await);
tryRun(() -> EventQueue.invokeAndWait(EMPTY_RUNNABLE));
if (!LOG_HANDLER.testContains(expectedInLog)) {
throw new RuntimeException("Test FAILED! (not found in the log: \"" + expectedInLog + "\")");
}
finishTestCase("");
}
static void subTest(Consumer<InvocationEvent> onBeforeDispatching, Runnable onDispatching) {
@@ -155,7 +150,7 @@ public class LWCToolkitInvokeAndWaitTest {
super.dispatchEvent(event);
}
});
CThreading.executeOnAppKit(() -> trycatch(() -> {
CThreading.executeOnAppKit(() -> tryRun(() -> {
//
// Post an invocation from AppKit.
//
@@ -165,7 +160,7 @@ public class LWCToolkitInvokeAndWaitTest {
}
static void sleep(int seconds) {
trycatch(() -> Thread.sleep(seconds * 1000L));
tryRun(() -> Thread.sleep(seconds * 1000L));
}
static class TestLogHandler extends StreamHandler {

View File

@@ -4,57 +4,94 @@ package helper;
import javax.swing.*;
import java.awt.*;
import java.util.Optional;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
@SuppressWarnings("ConstantConditions")
public class ToolkitTestHelper {
public static final Runnable EMPTY_RUNNABLE = () -> {};
public static volatile CompletableFuture<Boolean> FUTURE;
public static volatile int TEST_CASE;
public static volatile JFrame FRAME;
private static volatile JLabel LABEL;
private static volatile Thread MAIN_THREAD;
public static void initTest(Class<?> testClass, Thread mainThread) {
assert mainThread.getName().toLowerCase().contains("main");
private static final Runnable UPDATE_LABEL = new Runnable() {
final Random rand = new Random();
@Override
public void run() {
LABEL.setForeground(new Color(rand.nextFloat(), 0, rand.nextFloat()));
LABEL.setText("(" + TEST_CASE + ")");
}
};
MAIN_THREAD = mainThread;
Thread.UncaughtExceptionHandler handler = mainThread.getUncaughtExceptionHandler();
mainThread.setUncaughtExceptionHandler((t, e) -> {
public static void initTest(Class<?> testClass) {
MAIN_THREAD = Thread.currentThread();
assert MAIN_THREAD.getName().toLowerCase().contains("main");
Thread.UncaughtExceptionHandler handler = MAIN_THREAD.getUncaughtExceptionHandler();
MAIN_THREAD.setUncaughtExceptionHandler((t, e) -> {
if (FRAME != null) FRAME.dispose();
handler.uncaughtException(t, e);
});
trycatch(() -> EventQueue.invokeAndWait(() -> {
tryRun(() -> EventQueue.invokeAndWait(() -> {
FRAME = new JFrame(testClass.getSimpleName());
LABEL = new JLabel("0");
LABEL.setFont(LABEL.getFont().deriveFont((float)30));
LABEL.setHorizontalAlignment(SwingConstants.CENTER);
FRAME.setLayout(new BorderLayout());
FRAME.add(LABEL, BorderLayout.CENTER);
FRAME.pack();
FRAME.getContentPane().setBackground(Color.green);
FRAME.setLocationRelativeTo(null);
FRAME.setSize(200, 200);
FRAME.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
FRAME.setVisible(true);
}));
Timer timer = new Timer(100, e -> UPDATE_LABEL.run());
timer.setRepeats(true);
timer.start();
new Thread(() -> {
try {
MAIN_THREAD.join();
} catch (InterruptedException ignored) {
}
if (FRAME != null) FRAME.dispose();
timer.stop();
}).start();
}
public static void initTestCase(String caseCaption) {
public static void testCase(String caseCaption, Runnable test) {
FUTURE = new CompletableFuture<>();
FUTURE.whenComplete((r, e) -> Optional.of(e).ifPresent(Throwable::printStackTrace));
//noinspection NonAtomicOperationOnVolatileField
System.out.println("\n(" + (++TEST_CASE) + ") TEST: " + caseCaption);
String prefix = "(" + (++TEST_CASE) + ")";
System.out.println("\n" + prefix + " TEST: " + caseCaption);
UPDATE_LABEL.run();
test.run();
System.out.println(prefix + " SUCCEEDED\n");
}
public static void finishTestCase(String message) {
System.out.println("(" + TEST_CASE + ") SUCCEEDED " + message + "\n");
}
public static void trycatch(ThrowableRunnable runnable) {
trycatchAndReturn(() -> {
public static void tryRun(ThrowableRunnable runnable) {
tryCall(() -> {
runnable.run();
return null;
}, null);
}
public static <T> T trycatchAndReturn(Callable<T> callable, T defValue) {
public static <T> T tryCall(Callable<T> callable, T defValue) {
try {
return callable.call();
} catch (Exception e) {
@@ -70,4 +107,16 @@ public class ToolkitTestHelper {
public interface ThrowableRunnable {
void run() throws Exception;
}
public static class TestTimer {
private final long finishTime;
public TestTimer(long timeFromNow, TimeUnit unit) {
finishTime = System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(timeFromNow, unit);
}
public boolean hasFinished() {
return System.currentTimeMillis() >= finishTime;
}
}
}