8353950: Clipboard interaction on Windows is unstable
8332271: Reading data from the clipboard from multiple threads crashes the JVM Reviewed-by: abhiscxk, dnguyen
This commit is contained in:
parent
bcf860703d
commit
92be7821f5
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -204,9 +204,10 @@ public abstract class SunClipboard extends Clipboard
|
||||
byte[] data = null;
|
||||
Transferable localeTransferable = null;
|
||||
|
||||
try {
|
||||
openClipboard(null);
|
||||
|
||||
try {
|
||||
|
||||
long[] formats = getClipboardFormats();
|
||||
Long lFormat = DataTransferer.getInstance().
|
||||
getFlavorsForFormats(formats, getDefaultFlavorTable()).get(flavor);
|
||||
@ -318,12 +319,7 @@ public abstract class SunClipboard extends Clipboard
|
||||
* @since 1.5
|
||||
*/
|
||||
protected long[] getClipboardFormatsOpenClose() {
|
||||
try {
|
||||
openClipboard(null);
|
||||
return getClipboardFormats();
|
||||
} finally {
|
||||
closeClipboard();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -356,15 +352,7 @@ public abstract class SunClipboard extends Clipboard
|
||||
flavorListeners.add(listener);
|
||||
|
||||
if (numberOfFlavorListeners++ == 0) {
|
||||
long[] currentFormats = null;
|
||||
try {
|
||||
openClipboard(null);
|
||||
currentFormats = getClipboardFormats();
|
||||
} catch (final IllegalStateException ignored) {
|
||||
} finally {
|
||||
closeClipboard();
|
||||
}
|
||||
this.currentFormats = currentFormats;
|
||||
this.currentFormats = getClipboardFormats();
|
||||
|
||||
registerClipboardViewerChecked();
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -29,7 +29,9 @@ import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.awt.datatransfer.UnsupportedFlavorException;
|
||||
import java.io.IOException;
|
||||
import java.lang.System.Logger.Level;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import sun.awt.datatransfer.DataTransferer;
|
||||
import sun.awt.datatransfer.SunClipboard;
|
||||
@ -51,8 +53,12 @@ final class WClipboard extends SunClipboard {
|
||||
|
||||
private boolean isClipboardViewerRegistered;
|
||||
|
||||
private final ReentrantLock clipboardLocked = new ReentrantLock();
|
||||
|
||||
WClipboard() {
|
||||
super("System");
|
||||
// Register java side of the clipboard with the native side
|
||||
registerClipboard();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -104,18 +110,42 @@ final class WClipboard extends SunClipboard {
|
||||
|
||||
/**
|
||||
* Call the Win32 OpenClipboard function. If newOwner is non-null,
|
||||
* we also call EmptyClipboard and take ownership.
|
||||
* we also call EmptyClipboard and take ownership. If this method call
|
||||
* succeeds, it must be followed by a call to {@link #closeClipboard()}.
|
||||
*
|
||||
* @throws IllegalStateException if the clipboard has not been opened
|
||||
*/
|
||||
@Override
|
||||
public native void openClipboard(SunClipboard newOwner) throws IllegalStateException;
|
||||
public void openClipboard(SunClipboard newOwner) throws IllegalStateException {
|
||||
if (!clipboardLocked.tryLock()) {
|
||||
throw new IllegalStateException("Failed to acquire clipboard lock");
|
||||
}
|
||||
try {
|
||||
openClipboard0(newOwner);
|
||||
} catch (IllegalStateException ex) {
|
||||
clipboardLocked.unlock();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the Win32 CloseClipboard function if we have clipboard ownership,
|
||||
* does nothing if we have not ownership.
|
||||
*/
|
||||
@Override
|
||||
public native void closeClipboard();
|
||||
public void closeClipboard() {
|
||||
if (clipboardLocked.isLocked()) {
|
||||
try {
|
||||
closeClipboard0();
|
||||
} finally {
|
||||
clipboardLocked.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private native void openClipboard0(SunClipboard newOwner) throws IllegalStateException;
|
||||
private native void closeClipboard0();
|
||||
|
||||
/**
|
||||
* Call the Win32 SetClipboardData function.
|
||||
*/
|
||||
@ -157,16 +187,12 @@ final class WClipboard extends SunClipboard {
|
||||
return;
|
||||
}
|
||||
|
||||
long[] formats = null;
|
||||
try {
|
||||
openClipboard(null);
|
||||
formats = getClipboardFormats();
|
||||
} catch (IllegalStateException exc) {
|
||||
// do nothing to handle the exception, call checkChange(null)
|
||||
} finally {
|
||||
closeClipboard();
|
||||
}
|
||||
long[] formats = getClipboardFormats();
|
||||
checkChange(formats);
|
||||
} catch (Throwable ex) {
|
||||
System.getLogger(WClipboard.class.getName()).log(Level.DEBUG, "Failed to process handleContentsChanged", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -214,4 +240,6 @@ final class WClipboard extends SunClipboard {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private native void registerClipboard();
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -69,9 +69,8 @@ void AwtClipboard::RegisterClipboardViewer(JNIEnv *env, jobject jclipboard) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (theCurrentClipboard == NULL) {
|
||||
theCurrentClipboard = env->NewGlobalRef(jclipboard);
|
||||
}
|
||||
DASSERT(AwtClipboard::theCurrentClipboard != NULL);
|
||||
DASSERT(env->IsSameObject(AwtClipboard::theCurrentClipboard, jclipboard));
|
||||
|
||||
jclass cls = env->GetObjectClass(jclipboard);
|
||||
AwtClipboard::handleContentsChangedMID =
|
||||
@ -128,11 +127,13 @@ Java_sun_awt_windows_WClipboard_init(JNIEnv *env, jclass cls)
|
||||
* Signature: (Lsun/awt/windows/WClipboard;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_windows_WClipboard_openClipboard(JNIEnv *env, jobject self,
|
||||
Java_sun_awt_windows_WClipboard_openClipboard0(JNIEnv *env, jobject self,
|
||||
jobject newOwner)
|
||||
{
|
||||
TRY;
|
||||
|
||||
DASSERT(AwtClipboard::theCurrentClipboard != NULL);
|
||||
DASSERT(newOwner == NULL || env->IsSameObject(AwtClipboard::theCurrentClipboard, newOwner));
|
||||
DASSERT(::GetOpenClipboardWindow() != AwtToolkit::GetInstance().GetHWnd());
|
||||
|
||||
if (!::OpenClipboard(AwtToolkit::GetInstance().GetHWnd())) {
|
||||
@ -142,10 +143,6 @@ Java_sun_awt_windows_WClipboard_openClipboard(JNIEnv *env, jobject self,
|
||||
}
|
||||
if (newOwner != NULL) {
|
||||
AwtClipboard::GetOwnership();
|
||||
if (AwtClipboard::theCurrentClipboard != NULL) {
|
||||
env->DeleteGlobalRef(AwtClipboard::theCurrentClipboard);
|
||||
}
|
||||
AwtClipboard::theCurrentClipboard = env->NewGlobalRef(newOwner);
|
||||
}
|
||||
|
||||
CATCH_BAD_ALLOC;
|
||||
@ -157,7 +154,7 @@ Java_sun_awt_windows_WClipboard_openClipboard(JNIEnv *env, jobject self,
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_windows_WClipboard_closeClipboard(JNIEnv *env, jobject self)
|
||||
Java_sun_awt_windows_WClipboard_closeClipboard0(JNIEnv *env, jobject self)
|
||||
{
|
||||
TRY;
|
||||
|
||||
@ -297,23 +294,25 @@ Java_sun_awt_windows_WClipboard_getClipboardFormats
|
||||
{
|
||||
TRY;
|
||||
|
||||
DASSERT(::GetOpenClipboardWindow() == AwtToolkit::GetInstance().GetHWnd());
|
||||
unsigned int cFormats = 128; // Allocate enough space to hold all
|
||||
unsigned int pcFormatsOut = 0;
|
||||
unsigned int lpuiFormats[128] = { 0 };
|
||||
|
||||
jsize nFormats = ::CountClipboardFormats();
|
||||
jlongArray formats = env->NewLongArray(nFormats);
|
||||
VERIFY(::GetUpdatedClipboardFormats(lpuiFormats, 128, &pcFormatsOut));
|
||||
|
||||
jlongArray formats = env->NewLongArray(pcFormatsOut);
|
||||
if (formats == NULL) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
if (nFormats == 0) {
|
||||
if (pcFormatsOut == 0) {
|
||||
return formats;
|
||||
}
|
||||
jboolean isCopy;
|
||||
jlong *lFormats = env->GetLongArrayElements(formats, &isCopy),
|
||||
*saveFormats = lFormats;
|
||||
UINT num = 0;
|
||||
|
||||
for (jsize i = 0; i < nFormats; i++, lFormats++) {
|
||||
*lFormats = num = ::EnumClipboardFormats(num);
|
||||
for (unsigned int i = 0; i < pcFormatsOut; i++, lFormats++) {
|
||||
*lFormats = lpuiFormats[i];
|
||||
}
|
||||
|
||||
env->ReleaseLongArrayElements(formats, saveFormats, 0);
|
||||
@ -478,4 +477,16 @@ Java_sun_awt_windows_WClipboard_getClipboardData
|
||||
CATCH_BAD_ALLOC_RET(NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_awt_windows_WClipboard
|
||||
* Method: registerClipboard
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_windows_WClipboard_registerClipboard(JNIEnv *env, jobject self)
|
||||
{
|
||||
DASSERT(AwtClipboard::theCurrentClipboard == NULL);
|
||||
AwtClipboard::theCurrentClipboard = env->NewGlobalRef(self);
|
||||
}
|
||||
|
||||
} /* extern "C" */
|
||||
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
@test
|
||||
@bug 8332271
|
||||
@summary tests that concurrent access to the clipboard does not crash the JVM
|
||||
@key headful
|
||||
@requires (os.family == "windows")
|
||||
@run main ConcurrentClipboardAccessTest
|
||||
*/
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.datatransfer.Clipboard;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
|
||||
public class ConcurrentClipboardAccessTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
Thread clipboardLoader1 = new Thread(new ClipboardLoader());
|
||||
clipboardLoader1.setDaemon(true);
|
||||
clipboardLoader1.start();
|
||||
Thread clipboardLoader2 = new Thread(new ClipboardLoader());
|
||||
clipboardLoader2.setDaemon(true);
|
||||
clipboardLoader2.start();
|
||||
long start = System.currentTimeMillis();
|
||||
while (true) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
long now = System.currentTimeMillis();
|
||||
if ((now - start) > (10L * 1000L)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Test is considered successful if the concurrent repeated reading
|
||||
// from clipboard succeeds for the allotted time and the JVM does not
|
||||
// crash.
|
||||
System.out.println("Shutdown normally");
|
||||
}
|
||||
|
||||
public static class ClipboardLoader implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final Clipboard systemClipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||
while (true) {
|
||||
try {
|
||||
if (systemClipboard.isDataFlavorAvailable(DataFlavor.stringFlavor)) {
|
||||
systemClipboard.getData(DataFlavor.stringFlavor);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user