Skip to content

Commit 1f0b66e

Browse files
bourgesljbrbot
authored and
jbrbot
committed
JBR-8276: fixed CPlaformWindow.flushBuffers() to use LWCTooolkit.invokeAndWait() discarded when CGDisplayRegisterReconfigurationCallback() is in progress (ThreadUtilities.blockingThread + removed isWithinPowerTransition code)
- fixed CVDisplayLink management on wake-ups/sleep and display reconfiguration - restored opengl changes
1 parent b773b6f commit 1f0b66e

File tree

17 files changed

+684
-609
lines changed

17 files changed

+684
-609
lines changed

src/java.desktop/macosx/classes/sun/awt/CGraphicsEnvironment.java

+28-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2011, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -43,6 +43,7 @@
4343

4444
import sun.java2d.MacOSFlags;
4545
import sun.java2d.SunGraphicsEnvironment;
46+
import sun.java2d.metal.MTLGraphicsConfig;
4647
import sun.util.logging.PlatformLogger;
4748

4849
/**
@@ -152,7 +153,7 @@ public CGraphicsEnvironment() {
152153
}
153154

154155
/* Populate the device table */
155-
rebuildDevices();
156+
initDevices();
156157

157158
if (LogDisplay.ENABLED) {
158159
for (CGraphicsDevice gd : devices.values()) {
@@ -171,11 +172,7 @@ public static String getMtlShadersLibPath() {
171172
return mtlShadersLib;
172173
}
173174

174-
/**
175-
* Updates the list of devices and notify listeners.
176-
*/
177-
private void rebuildDevices() {
178-
initDevices();
175+
private void doNotifyListeners() {
179176
// Do not notify devices, this was already done in initDevices.
180177
displayChanger.notifyListeners();
181178
}
@@ -184,7 +181,7 @@ private void rebuildDevices() {
184181
* Called by the CoreGraphics Display Reconfiguration Callback.
185182
*
186183
* @param displayId CoreGraphics displayId
187-
* @param removed true if displayId was removed, false otherwise.
184+
* @param flags CGDisplayChangeSummaryFlags flags as integer
188185
*/
189186
void _displayReconfiguration(int displayId, int flags) {
190187
// See CGDisplayChangeSummaryFlags
@@ -200,13 +197,35 @@ void _displayReconfiguration(int displayId, int flags) {
200197
// monitors are not added nor removed, but when the video card is
201198
// switched to/from the discrete video card, so we should try to map the
202199
// old to the new devices.
203-
rebuildDevices();
200+
initDevices();
204201
if (log != null && log != LogDisplay.REMOVED) {
205202
CGraphicsDevice gd = devices.get(displayId);
206203
log.log(displayId, gd != null ? gd.getBounds() : "UNKNOWN", gd != null ? gd.getScaleFactor() : Double.NaN);
207204
}
208205
}
209206

207+
/**
208+
* Called by the CoreGraphics Display Reconfiguration Callback (once all displays processed = finished)
209+
*/
210+
void _displayReconfigurationFinished() {
211+
if (logger.isLoggable(PlatformLogger.Level.FINE)) {
212+
logger.fine("CGraphicsEnvironment._displayReconfigurationFinished(): enter");
213+
}
214+
try {
215+
doNotifyListeners();
216+
} catch (Exception e) {
217+
logger.severe("CGraphicsEnvironment._displayReconfigurationFinished: exception occurred: ", e);
218+
} finally {
219+
// notify the metal pipeline after processing listeners:
220+
if (CGraphicsEnvironment.usingMetalPipeline()) {
221+
MTLGraphicsConfig.displayReconfigurationDone();
222+
}
223+
}
224+
if (logger.isLoggable(PlatformLogger.Level.FINE)) {
225+
logger.fine("_displayReconfigurationFinished(): exit");
226+
}
227+
}
228+
210229
@Override
211230
@SuppressWarnings("removal")
212231
protected void finalize() throws Throwable {

src/java.desktop/macosx/classes/sun/java2d/metal/MTLGraphicsConfig.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -82,6 +82,8 @@ public final class MTLGraphicsConfig extends CGraphicsConfig
8282
private final Object disposerReferent = new Object();
8383
private final int maxTextureSize;
8484

85+
public static native void displayReconfigurationDone();
86+
8587
private static native long getMTLConfigInfo(int displayID, String mtlShadersLib);
8688

8789
/**

src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java

+37-49
Original file line numberDiff line numberDiff line change
@@ -1224,8 +1224,8 @@ private static int getInvokeLaterMode() {
12241224
final String invokeLaterArg = System.getProperty(invokeLaterKey);
12251225
final int result;
12261226
if (invokeLaterArg == null) {
1227-
// default = 'enabled' to avoid any potential freeze (safe) until better solution:
1228-
result = INVOKE_LATER_ENABLED;
1227+
// default = 'false':
1228+
result = INVOKE_LATER_DISABLED;
12291229
} else {
12301230
switch (invokeLaterArg.toLowerCase()) {
12311231
default:
@@ -1247,51 +1247,46 @@ private static int getInvokeLaterMode() {
12471247
return result;
12481248
}
12491249

1250-
@SuppressWarnings("removal")
1251-
private final static boolean INVOKE_LATER_USE_PWM = getInvokeLaterUsePWM();
1252-
1253-
private static boolean getInvokeLaterUsePWM() {
1254-
final String usePwmKey = "awt.mac.flushBuffers.pwm";
1255-
final String usePwmArg = System.getProperty(usePwmKey);
1256-
final boolean result;
1257-
if (usePwmArg == null) {
1258-
// default = 'false':
1259-
result = false;
1260-
} else {
1261-
result = "true".equalsIgnoreCase(usePwmArg);
1262-
logger.info("CPlatformWindow: property \"{0}={1}\", using usePWM={2}.",
1263-
usePwmKey, usePwmArg, result);
1264-
}
1265-
return result;
1266-
}
1267-
1250+
private final static long NANOS_PER_SEC = 1000000000L;
12681251
/* 10s period arround reference times (sleep/wake-up...)
12691252
* to ensure all displays are awaken properly */
1270-
private final static long NANOS_PER_SEC = 1000000000L;
12711253
private final static long STATE_CHANGE_PERIOD = 10L * NANOS_PER_SEC;
12721254

12731255
private final AtomicBoolean mirroringState = new AtomicBoolean(false);
12741256
/** per window timestamp of disabling mirroring */
12751257
private final AtomicLong mirroringDisablingTime = new AtomicLong(0L);
12761258

12771259
// Specific class needed to get obvious stack traces:
1278-
private final class EmptyRunnable implements Runnable {
1260+
private final static class EmptyRunnable implements Runnable {
1261+
1262+
private final String identifier;
1263+
1264+
EmptyRunnable(final String identifier) {
1265+
this.identifier = identifier;
1266+
}
1267+
12791268
@Override
12801269
public void run() {
12811270
// Posting an empty to flush the EventQueue without blocking the main thread
12821271
if (logger.isLoggable(PlatformLogger.Level.FINE)) {
1283-
logger.fine("CPlatformWindow.flushBuffers: run() invoked on {0}",
1284-
getIdentifier(target));
1272+
logger.fine("CPlatformWindow.flushBuffers: run() invoked on target: {0}", identifier);
12851273
}
12861274
}
12871275
};
1288-
private final EmptyRunnable emptyTask = new EmptyRunnable();
12891276

12901277
void flushBuffers() {
12911278
// Only 1 usage by deliverMoveResizeEvent():
12921279
// System-dependent appearance optimization.
12931280
// May be blocking so postpone this event processing:
12941281

1282+
// Test only to validate LWCToolkit self-defense against freezes:
1283+
final boolean checkLWCToolkitBlockingMainGuard = false;
1284+
1285+
if (!checkLWCToolkitBlockingMainGuard && LWCToolkit.isBlockingMainThread()) {
1286+
logger.fine("Blocked main thread, skip flushBuffers().");
1287+
return;
1288+
}
1289+
12951290
if (isVisible() && !nativeBounds.isEmpty() && !isFullScreenMode) {
12961291
// use the system property 'awt.mac.flushBuffers.invokeLater' to true/auto (default: auto)
12971292
// to avoid deadlocks caused by the LWCToolkit.invokeAndWait() call below:
@@ -1307,7 +1302,6 @@ void flushBuffers() {
13071302

13081303
// JBR-5497: force using invokeLater() when computer returns from sleep or displayChanged()
13091304
// (mirroring case especially) to avoid deadlocks until solved definitely:
1310-
13111305
boolean mirroring = false;
13121306
if (peer != null) {
13131307
final GraphicsDevice device = peer.getGraphicsConfiguration().getDevice();
@@ -1357,39 +1351,30 @@ void flushBuffers() {
13571351
useInvokeLater = true;
13581352
break;
13591353
}
1360-
if (!useInvokeLater && INVOKE_LATER_USE_PWM) {
1361-
// If the system property 'awt.mac.flushBuffers.pwm' is true,
1362-
// invokeLater is enforced during power transitions.
1363-
final boolean inTransition = LWCToolkit.isWithinPowerTransition();
1364-
if (inTransition) {
1365-
logger.fine("CPlatformWindow.flushBuffers[pwm]: inTransition = true");
1366-
useInvokeLater = true;
1367-
}
1368-
}
13691354
try {
1355+
final String identifier = logger.isLoggable(PlatformLogger.Level.FINE) ? getIdentifier(target) : null;
1356+
final EmptyRunnable emptyTask = new EmptyRunnable(identifier);
1357+
13701358
// check invokeAndWait: KO (operations require AWTLock and main thread)
1371-
// => use invokeLater as it is an empty event to force refresh ASAP
1359+
// => use invokeLater as it is an empty event to force refresh
13721360
if (useInvokeLater) {
13731361
LWCToolkit.invokeLater(emptyTask, target);
13741362
} else {
1363+
/* Ensure >3000ms = 3.666ms timeout to avoid any deadlock among
1364+
* appkit, EDT, Flusher & a11y threads, locks
1365+
* and various synchronization patterns... */
1366+
final double timeoutSeconds = 3.666; // seconds
1367+
13751368
if (logger.isLoggable(PlatformLogger.Level.FINE)) {
13761369
logger.fine("CPlatformWindow.flushBuffers: enter " +
1377-
"LWCToolkit.invokeAndWait(emptyTask) on target = {0}",
1378-
getIdentifier(target));
1370+
"LWCToolkit.invokeAndWait(emptyTask) on target = {0}", identifier);
13791371
}
13801372

1381-
/* Ensure >500ms = 666ms timeout to avoid any deadlock among
1382-
* appkit, EDT, Flusher & a11y threads, locks
1383-
* and various synchronization patterns... */
1384-
final double timeoutSeconds = 0.666; // seconds
1385-
1386-
// FUCK: appKit is calling this method !
13871373
LWCToolkit.invokeAndWait(emptyTask, target, timeoutSeconds);
13881374

13891375
if (logger.isLoggable(PlatformLogger.Level.FINE)) {
13901376
logger.fine("CPlatformWindow.flushBuffers: exit " +
1391-
"LWCToolkit.invokeAndWait(emptyTask) on target = {0}",
1392-
getIdentifier(target));
1377+
"LWCToolkit.invokeAndWait(emptyTask) on target = {0}", identifier);
13931378
}
13941379
}
13951380
} catch (InvocationTargetException ite) {
@@ -1442,6 +1427,10 @@ public void doDeliverMoveResizeEvent() {
14421427
/* native call by AWTWindow._deliverMoveResizeEvent() */
14431428
protected void deliverMoveResizeEvent(int x, int y, int width, int height,
14441429
boolean byUser) {
1430+
1431+
/* Test only to generate more appkit freezes */
1432+
final boolean bypassToHaveMoreFreezes = false;
1433+
14451434
AtomicBoolean ref = new AtomicBoolean();
14461435
execute(ptr -> {
14471436
ref.set(CWrapper.NSWindow.isZoomed(ptr));
@@ -1460,10 +1449,9 @@ protected void deliverMoveResizeEvent(int x, int y, int width, int height,
14601449
}
14611450

14621451
// System-dependent appearance optimization.
1463-
if ((byUser && !oldB.getSize().equals(nativeBounds.getSize()))
1452+
if (bypassToHaveMoreFreezes
1453+
|| (byUser && !oldB.getSize().equals(nativeBounds.getSize()))
14641454
|| isFullScreenAnimationOn) {
1465-
1466-
// May be blocking so postpone this event processing:
14671455
flushBuffers();
14681456
}
14691457
}

src/java.desktop/macosx/classes/sun/lwawt/macosx/LWCToolkit.java

+7-7
Original file line numberDiff line numberDiff line change
@@ -733,7 +733,7 @@ public void run() {
733733

734734
public static <T> T invokeAndWait(final Callable<T> callable,
735735
Component component) throws Exception {
736-
return invokeAndWait(callable, component, -1);
736+
return invokeAndWait(callable, component, 0);
737737
}
738738

739739
public static <T> T invokeAndWait(final Callable<T> callable, Component component, int timeoutSeconds) throws Exception {
@@ -785,7 +785,7 @@ public static void invokeAndWait(Runnable runnable, Component component)
785785
invokeAndWait(runnable, component, false, 0.0);
786786
}
787787

788-
/* 25.01.25: keep public methods with (int timeoutSeconds) */
788+
/* 25.04: keep public methods with (int timeoutSeconds) */
789789
@Deprecated(since = "25")
790790
public static void invokeAndWait(Runnable runnable, Component component, int timeoutSeconds)
791791
throws InvocationTargetException
@@ -800,7 +800,7 @@ public static void invokeAndWait(Runnable runnable, Component component, boolean
800800
invokeAndWait(runnable, component, processEvents, timeout);
801801
}
802802

803-
/* 25.01.25: added public methods with (double timeoutSeconds) to have timeouts between 0.0 and 1.0 */
803+
/* 25.04: added public methods with (double timeoutSeconds) to have more precise timeouts */
804804

805805
public static void invokeAndWait(Runnable runnable, Component component, double timeoutSeconds)
806806
throws InvocationTargetException
@@ -872,9 +872,9 @@ public static void invokeAndWait(Runnable runnable, Component component, boolean
872872

873873
private static native boolean isBlockingEventDispatchThread();
874874

875-
static native String getThreadTraceContexts();
875+
public static native boolean isBlockingMainThread();
876876

877-
static native boolean isWithinPowerTransition();
877+
static native String getThreadTraceContexts();
878878

879879
public static void invokeLater(Runnable event, Component component)
880880
throws InvocationTargetException {
@@ -1051,7 +1051,7 @@ public boolean canPopupOverlapTaskBar() {
10511051
static native long createAWTRunLoopMediator();
10521052
/**
10531053
* Method to run a nested run-loop. The nested loop is spinned in the javaRunLoop mode, so selectors sent
1054-
* by [JNFRunLoop performOnMainThreadWaiting] are processed.
1054+
* by [ThreadUtilities performOnMainThreadWaiting] are processed.
10551055
* @param mediator a native pointer to the mediator object created by createAWTRunLoopMediator
10561056
* @param processEvents if true - dispatches event while in the nested loop. Used in DnD.
10571057
* Additional attention is needed when using this feature as we short-circuit normal event
@@ -1065,7 +1065,7 @@ static void doAWTRunLoop(long mediator, boolean processEvents) {
10651065
}
10661066

10671067
/**
1068-
* Starts run-loop with the provided timeout. Use (<=0.0) for the infinite value.
1068+
* Starts run-loop with the provided timeout. Use (<= 0.0) for the infinite value.
10691069
*/
10701070
static boolean doAWTRunLoop(long mediator, boolean processEvents, double timeoutSeconds) {
10711071
if (log.isLoggable(PlatformLogger.Level.FINE)) {

0 commit comments

Comments
 (0)