Skip to content

Commit f640ede

Browse files
author
Alexander Zvegintsev
committed
8341382: EXCEPTION_ACCESS_VIOLATION in awt.dll after JDK-8185862
Reviewed-by: kizune, prr, vdyakov
1 parent 70c92d6 commit f640ede

6 files changed

Lines changed: 258 additions & 79 deletions

File tree

src/java.desktop/windows/native/libawt/windows/Devices.cpp

Lines changed: 97 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2001, 2026, 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
@@ -117,47 +117,78 @@ static BOOL IsValidMonitor(HMONITOR hMon)
117117
return TRUE;
118118
}
119119

120-
// Callback for CountMonitors below
121-
static BOOL WINAPI clb_fCountMonitors(HMONITOR hMon, HDC hDC, LPRECT rRect, LPARAM lpMonitorCounter)
120+
121+
// Callback for CollectMonitors below
122+
static BOOL WINAPI clb_fCollectMonitors(HMONITOR hMon, HDC hDC, LPRECT rRect, LPARAM lpMonitorData)
122123
{
123-
if (IsValidMonitor(hMon)) {
124-
(*((int *)lpMonitorCounter))++;
124+
MonitorData* pMonitorData = (MonitorData *)lpMonitorData;
125+
126+
if (!IsValidMonitor(hMon)) {
127+
return TRUE;
128+
}
129+
130+
if (pMonitorData->monitorCounter == pMonitorData->monitorLimit) {
131+
TRY;
132+
133+
int newMonitorLimit = pMonitorData->monitorLimit * 2;
134+
HMONITOR* newMonitors =
135+
(HMONITOR*)SAFE_SIZE_ARRAY_REALLOC(
136+
safe_Realloc, pMonitorData->hmpMonitors,
137+
newMonitorLimit, sizeof(HMONITOR)
138+
);
139+
pMonitorData->hmpMonitors = newMonitors;
140+
pMonitorData->monitorLimit = newMonitorLimit;
141+
142+
CATCH_BAD_ALLOC_RET(FALSE);
125143
}
126144

145+
pMonitorData->hmpMonitors[pMonitorData->monitorCounter] = hMon;
146+
pMonitorData->monitorCounter++;
147+
127148
return TRUE;
128149
}
129150

130-
int WINAPI CountMonitors(void)
151+
static HMONITOR* CollectMonitors(int* numScreens)
131152
{
132-
int monitorCounter = 0;
133-
::EnumDisplayMonitors(NULL, NULL, clb_fCountMonitors, (LPARAM)&monitorCounter);
134-
return monitorCounter;
135-
}
153+
const int initialMonitorLimit = 4;
136154

137-
// Callback for CollectMonitors below
138-
static BOOL WINAPI clb_fCollectMonitors(HMONITOR hMon, HDC hDC, LPRECT rRect, LPARAM lpMonitorData)
139-
{
140-
MonitorData* pMonitorData = (MonitorData *)lpMonitorData;
141-
if ((pMonitorData->monitorCounter < pMonitorData->monitorLimit) && (IsValidMonitor(hMon))) {
142-
pMonitorData->hmpMonitors[pMonitorData->monitorCounter] = hMon;
143-
pMonitorData->monitorCounter++;
155+
*numScreens = 0;
156+
157+
MonitorData data;
158+
data.monitorCounter = 0;
159+
data.monitorLimit = initialMonitorLimit;
160+
161+
TRY;
162+
163+
data.hmpMonitors = (HMONITOR*)SAFE_SIZE_ARRAY_ALLOC(safe_Malloc,
164+
initialMonitorLimit, sizeof(HMONITOR));
165+
CATCH_BAD_ALLOC_RET(NULL);
166+
167+
if (!::EnumDisplayMonitors(NULL, NULL, clb_fCollectMonitors, (LPARAM)&data)) {
168+
free(data.hmpMonitors);
169+
return NULL;
144170
}
145171

146-
return TRUE;
172+
*numScreens = data.monitorCounter;
173+
return data.hmpMonitors;
147174
}
148175

149-
static int WINAPI CollectMonitors(HMONITOR* hmpMonitors, int nNum)
176+
int WINAPI CountMonitors()
150177
{
151-
if (NULL != hmpMonitors) {
152-
MonitorData monitorData;
153-
monitorData.monitorCounter = 0;
154-
monitorData.monitorLimit = nNum;
155-
monitorData.hmpMonitors = hmpMonitors;
156-
::EnumDisplayMonitors(NULL, NULL, clb_fCollectMonitors, (LPARAM)&monitorData);
157-
return monitorData.monitorCounter;
158-
} else {
159-
return 0;
178+
int numScreens = 0;
179+
HMONITOR* monHds = CollectMonitors(&numScreens);
180+
free(monHds);
181+
return numScreens;
182+
}
183+
184+
static BOOL AreSameMonitorInfo(LPMONITORINFOEX oldInfo, LPMONITORINFOEX newInfo)
185+
{
186+
if (oldInfo == NULL || newInfo == NULL) {
187+
return FALSE;
160188
}
189+
190+
return oldInfo->dwFlags == newInfo->dwFlags
191+
&& ::lstrcmp(oldInfo->szDevice, newInfo->szDevice) == 0;
161192
}
162193

163194
BOOL WINAPI MonitorBounds(HMONITOR hmMonitor, RECT* rpBounds)
@@ -206,17 +237,26 @@ BOOL Devices::UpdateInstance(JNIEnv *env)
206237
{
207238
J2dTraceLn(J2D_TRACE_INFO, "Devices::UpdateInstance");
208239

209-
int numScreens = CountMonitors();
210-
HMONITOR *monHds = (HMONITOR *)SAFE_SIZE_ARRAY_ALLOC(safe_Malloc,
211-
numScreens, sizeof(HMONITOR));
212-
if (numScreens != CollectMonitors(monHds, numScreens)) {
240+
int numScreens = 0;
241+
HMONITOR *monHds = CollectMonitors(&numScreens);
242+
if (monHds == NULL) {
213243
J2dRlsTraceLn(J2D_TRACE_ERROR,
214-
"Devices::UpdateInstance: Failed to get all "\
244+
"Devices::UpdateInstance: Failed to get "\
215245
"monitor handles.");
216246
free(monHds);
217247
return FALSE;
218248
}
219249

250+
if (numScreens == 0) {
251+
CriticalSection::Lock l(arrayLock);
252+
if (theInstance != NULL) {
253+
J2dRlsTraceLn(J2D_TRACE_ERROR,
254+
"Devices::UpdateInstance: No valid monitor handles.");
255+
free(monHds);
256+
return FALSE;
257+
}
258+
}
259+
220260
Devices *newDevices = new Devices(numScreens);
221261
// This way we know that the array will not be disposed of
222262
// at least until we replaced it with a new one.
@@ -242,18 +282,26 @@ BOOL Devices::UpdateInstance(JNIEnv *env)
242282
theInstance = newDevices;
243283

244284
if (oldDevices) {
245-
// Invalidate the devices with indexes out of the new set of
246-
// devices. This doesn't cover all cases when the device
247-
// might should be invalidated (like if it's not the last device
248-
// that was removed), but it will have to do for now.
249285
int oldNumScreens = oldDevices->GetNumDevices();
250-
int newNumScreens = theInstance->GetNumDevices();
251-
J2dTraceLn(J2D_TRACE_VERBOSE, " Invalidating removed devices");
252-
for (int i = newNumScreens; i < oldNumScreens; i++) {
253-
// removed device, needs to be invalidated
286+
J2dTraceLn(J2D_TRACE_VERBOSE, " Invalidating changed devices");
287+
for (int i = 0; i < oldNumScreens; i++) {
288+
AwtWin32GraphicsDevice *oldDevice =
289+
oldDevices->GetDevice(i, FALSE);
290+
AwtWin32GraphicsDevice *newDevice =
291+
theInstance->GetDevice(i, FALSE);
292+
BOOL changed = (newDevice == NULL)
293+
|| !AreSameMonitorInfo(
294+
(LPMONITORINFOEX) oldDevice->GetMonitorInfo(),
295+
(LPMONITORINFOEX) newDevice->GetMonitorInfo());
296+
297+
if (!changed) {
298+
newDevice->TransferJavaDevice(env, oldDevice);
299+
continue;
300+
}
301+
254302
J2dTraceLn(J2D_TRACE_WARNING,
255-
"Devices::UpdateInstance: device removed: %d", i);
256-
oldDevices->GetDevice(i)->Invalidate(env);
303+
"Devices::UpdateInstance: device changed: %d", i);
304+
oldDevice->Invalidate(env);
257305
}
258306
// Now that we have a new array in place, remove this (possibly the
259307
// last) reference to the old instance.
@@ -346,6 +394,12 @@ AwtWin32GraphicsDevice *Devices::GetDevice(int index, BOOL adjust)
346394
J2dTraceLn(J2D_TRACE_INFO,
347395
"Devices::GetDevice index=%d adjust?=%d",
348396
index, adjust);
397+
if (numDevices <= 0) {
398+
J2dTraceLn(J2D_TRACE_WARNING,
399+
"Devices::GetDevice: "\
400+
"no devices, returning NULL.");
401+
return NULL;
402+
}
349403
if (index < 0 || index >= numDevices) {
350404
if (!adjust) {
351405
J2dTraceLn(J2D_TRACE_WARNING,

src/java.desktop/windows/native/libawt/windows/Devices.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2001, 2026, 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
@@ -47,8 +47,11 @@ static BOOL UpdateInstance(JNIEnv *env);
4747
class InstanceAccess {
4848
public:
4949
INLINE InstanceAccess() { devices = Devices::GetInstance(); }
50-
INLINE ~InstanceAccess() { devices->Release(); }
50+
INLINE ~InstanceAccess() { if (devices != NULL) devices->Release(); }
5151
Devices* operator->() { return devices; }
52+
INLINE AwtWin32GraphicsDevice* Device(int index, BOOL adjust = TRUE) {
53+
return devices == NULL ? NULL : devices->GetDevice(index, adjust);
54+
}
5255
private:
5356
Devices* devices;
5457
// prevent bad things like copying or getting address of

src/java.desktop/windows/native/libawt/windows/awt_Toolkit.cpp

Lines changed: 90 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1996, 2026, 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
@@ -65,7 +65,7 @@
6565
#include <java_awt_Toolkit.h>
6666
#include <java_awt_event_InputMethodEvent.h>
6767

68-
extern void initScreens(JNIEnv *env);
68+
extern BOOL initScreens(JNIEnv *env);
6969
extern "C" void awt_dnd_initialize();
7070
extern "C" void awt_dnd_uninitialize();
7171
extern "C" void awt_clipboard_uninitialize(JNIEnv *env);
@@ -157,6 +157,78 @@ extern "C" JNIEXPORT jboolean JNICALL AWTIsHeadless() {
157157
}
158158

159159
#define IDT_AWT_MOUSECHECK 0x101
160+
#define IDT_AWT_DISPLAYCHANGE 0x102
161+
162+
#define AWT_DISPLAYCHANGE_RETRY_DELAY 250
163+
#define AWT_DISPLAYCHANGE_RETRY_LIMIT 20
164+
165+
class DisplayChangeHandler {
166+
public:
167+
static BOOL Handle(JNIEnv *env, HWND hWnd) {
168+
// Reinitialize screens
169+
if (!initScreens(env)) {
170+
OnDisplayChangeFailed(hWnd);
171+
return FALSE;
172+
}
173+
174+
OnDisplayChangeSucceeded(hWnd);
175+
176+
// Notify Java side - call WToolkit.displayChanged()
177+
jclass clazz = env->FindClass("sun/awt/windows/WToolkit");
178+
DASSERT(clazz != NULL);
179+
if (!clazz) throw std::bad_alloc();
180+
env->CallStaticVoidMethod(clazz, AwtToolkit::displayChangeMID);
181+
182+
return !env->ExceptionCheck();
183+
}
184+
185+
static void Reset(HWND hWnd) {
186+
::KillTimer(hWnd, IDT_AWT_DISPLAYCHANGE);
187+
retryCount = 0;
188+
}
189+
190+
static void ScheduleFromSessionChange(HWND hWnd) {
191+
if (!recoveryPending) {
192+
return;
193+
}
194+
Reset(hWnd);
195+
Schedule(hWnd);
196+
}
197+
198+
private:
199+
static void OnDisplayChangeFailed(HWND hWnd) {
200+
recoveryPending = TRUE;
201+
Schedule(hWnd);
202+
}
203+
204+
static void OnDisplayChangeSucceeded(HWND hWnd) {
205+
recoveryPending = FALSE;
206+
Reset(hWnd);
207+
}
208+
209+
static void Schedule(HWND hWnd) {
210+
if (retryCount >= AWT_DISPLAYCHANGE_RETRY_LIMIT) {
211+
Reset(hWnd);
212+
J2dRlsTraceLn(J2D_TRACE_ERROR,
213+
"AwtToolkit: Display change retry limit exceeded.");
214+
return;
215+
}
216+
217+
retryCount++;
218+
if (::SetTimer(hWnd, IDT_AWT_DISPLAYCHANGE,
219+
AWT_DISPLAYCHANGE_RETRY_DELAY, NULL) == 0) {
220+
Reset(hWnd);
221+
J2dRlsTraceLn(J2D_TRACE_ERROR,
222+
"AwtToolkit: Failed to schedule display change retry.");
223+
}
224+
}
225+
226+
static int retryCount;
227+
static BOOL recoveryPending;
228+
};
229+
230+
int DisplayChangeHandler::retryCount = 0;
231+
BOOL DisplayChangeHandler::recoveryPending = FALSE;
160232

161233
static LPCTSTR szAwtToolkitClassName = TEXT("SunAwtToolkit");
162234

@@ -1004,6 +1076,14 @@ LRESULT CALLBACK AwtToolkit::WndProc(HWND hWnd, UINT message,
10041076
}
10051077

10061078
case WM_TIMER: {
1079+
if (wParam == IDT_AWT_DISPLAYCHANGE) {
1080+
if (DisplayChangeHandler::Handle(env, hWnd)) {
1081+
GetInstance().m_displayChanged = TRUE;
1082+
::PostMessage(HWND_BROADCAST, WM_PALETTEISCHANGING, NULL, NULL);
1083+
}
1084+
return 0;
1085+
}
1086+
10071087
// 6479820. Should check if a window is in manual resizing process: skip
10081088
// sending any MouseExit/Enter events while inside resize-loop.
10091089
// Note that window being in manual moving process could still
@@ -1245,18 +1325,11 @@ LRESULT CALLBACK AwtToolkit::WndProc(HWND hWnd, UINT message,
12451325
return tk.m_inputMethodData;
12461326
}
12471327
case WM_DISPLAYCHANGE: {
1248-
// Reinitialize screens
1249-
initScreens(env);
1250-
1251-
// Notify Java side - call WToolkit.displayChanged()
1252-
jclass clazz = env->FindClass("sun/awt/windows/WToolkit");
1253-
DASSERT(clazz != NULL);
1254-
if (!clazz) throw std::bad_alloc();
1255-
env->CallStaticVoidMethod(clazz, AwtToolkit::displayChangeMID);
1256-
1257-
GetInstance().m_displayChanged = TRUE;
1258-
1259-
::PostMessage(HWND_BROADCAST, WM_PALETTEISCHANGING, NULL, NULL);
1328+
DisplayChangeHandler::Reset(hWnd);
1329+
if (DisplayChangeHandler::Handle(env, hWnd)) {
1330+
GetInstance().m_displayChanged = TRUE;
1331+
::PostMessage(HWND_BROADCAST, WM_PALETTEISCHANGING, NULL, NULL);
1332+
}
12601333
break;
12611334
}
12621335
/* Session management */
@@ -1341,6 +1414,9 @@ LRESULT CALLBACK AwtToolkit::WndProc(HWND hWnd, UINT message,
13411414
activate
13421415
? JNI_TRUE
13431416
: JNI_FALSE, reason);
1417+
if (activate) {
1418+
DisplayChangeHandler::ScheduleFromSessionChange(hWnd);
1419+
}
13441420
}
13451421
break;
13461422
}

0 commit comments

Comments
 (0)