mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-25 10:49:41 +01:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b382484ee |
@@ -1,16 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
|
||||
while getopts ":t" o; do
|
||||
case "${o}" in
|
||||
t)
|
||||
t="With Teamcity tests info"
|
||||
TC_PRINT=1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift $((OPTIND-1))
|
||||
|
||||
NEWFILEPATH=$1
|
||||
CONFIGID=$2
|
||||
BUILDID=$3
|
||||
@@ -18,7 +7,6 @@ TOKEN=$4
|
||||
#
|
||||
# Get the size of new artifact
|
||||
#
|
||||
|
||||
unameOut="$(uname -s)"
|
||||
case "${unameOut}" in
|
||||
Linux*)
|
||||
@@ -28,11 +16,7 @@ case "${unameOut}" in
|
||||
NEWFILESIZE=$(stat -f%z "$NEWFILEPATH")
|
||||
;;
|
||||
CYGWIN*)
|
||||
NEWFILESIZE=$(stat -c%s$4
|
||||
#
|
||||
# Get the size of new artifact
|
||||
#
|
||||
"$NEWFILEPATH")
|
||||
NEWFILESIZE=$(stat -c%s "$NEWFILEPATH")
|
||||
;;
|
||||
MINGW*)
|
||||
NEWFILESIZE=$(stat -c%s "$NEWFILEPATH")
|
||||
@@ -42,7 +26,6 @@ case "${unameOut}" in
|
||||
exit 1
|
||||
esac
|
||||
FILENAME=$(basename ${NEWFILEPATH})
|
||||
|
||||
#
|
||||
# Get pattern of artifact name
|
||||
# Base filename pattern: <BUNDLE_TYPE>-<JDK_VERSION>-<OS>-<ARCH>-b<BUILD>.tar.gz: jbr_dcevm-17.0.2-osx-x64-b1234.tar.gz
|
||||
@@ -51,40 +34,16 @@ FILENAME=$(basename ${NEWFILEPATH})
|
||||
|
||||
BUNDLE_TYPE=jbrsdk
|
||||
OS_ARCH_PATTERN=""
|
||||
FILE_EXTENSION=tar.gz
|
||||
|
||||
re='(jbr[a-z_]*).*-[0-9_\.]+-(.+)-b[0-9]+(.+)'
|
||||
re='(jbr[a-z_]*).+[0-9_\.]+-(.+)-b.+\.tar\.gz'
|
||||
if [[ $FILENAME =~ $re ]]; then
|
||||
BUNDLE_TYPE=${BASH_REMATCH[1]}
|
||||
OS_ARCH_PATTERN=${BASH_REMATCH[2]}
|
||||
FILE_EXTENSION=${BASH_REMATCH[3]}
|
||||
fi
|
||||
|
||||
if [ $TC_PRINT -eq 1 ]; then
|
||||
testname_file_ext=`echo $FILE_EXTENSION | sed 's/\./_/g'`
|
||||
testname=$BUNDLE_TYPE"_"$OS_ARCH_PATTERN$testname_file_ext
|
||||
echo \#\#teamcity[testStarted name=\'$testname\']
|
||||
fi
|
||||
|
||||
|
||||
echo "BUNDLE_TYPE: " $BUNDLE_TYPE
|
||||
echo "OS_ARCH_PATTERN: " $OS_ARCH_PATTERN
|
||||
echo "FILE_EXTENSION: " $FILE_EXTENSION
|
||||
echo "New size of $FILENAME = $NEWFILESIZE bytes."
|
||||
|
||||
|
||||
function test_failed_msg() {
|
||||
if [ $3 -eq 1 ]; then
|
||||
echo \#\#teamcity[testFailed name=\'$1\' message=\'$2\']
|
||||
fi
|
||||
}
|
||||
|
||||
function test_finished_msg() {
|
||||
if [ $2 -eq 1 ]; then
|
||||
echo \#\#teamcity[testFinished name=\'$1\']
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
# Get previous successful build ID
|
||||
# Example:
|
||||
@@ -103,11 +62,8 @@ if [[ $CURL_RESPONSE =~ $re ]]; then
|
||||
ID=${BASH_REMATCH[1]}
|
||||
echo "BUILD Number: ${BASH_REMATCH[2]}"
|
||||
else
|
||||
msg="ERROR: can't find previous build"
|
||||
echo $msg
|
||||
echo "ERROR: can't find previous build"
|
||||
echo $CURL_RESPONSE
|
||||
test_failed_msg $testname $msg $TC_PRINT
|
||||
test_finished_msg $testname $TC_PRINT
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -122,7 +78,7 @@ echo "Atrifacts of previous build of $CONFIGID :"
|
||||
echo $CURL_RESPONSE
|
||||
|
||||
# Find binary size (in response) with reg exp
|
||||
re='name=\"('$BUNDLE_TYPE'[^\"]+'${OS_ARCH_PATTERN}'[^\"]+'${FILE_EXTENSION}')\" size=\"([0-9]+)\"'
|
||||
re='name=\"('$BUNDLE_TYPE'[^\"]+'${OS_ARCH_PATTERN}'[^\"]+\.tar\.gz)\" size=\"([0-9]+)\"'
|
||||
|
||||
if [[ $CURL_RESPONSE =~ $re ]]; then
|
||||
OLDFILENAME=${BASH_REMATCH[1]}
|
||||
@@ -133,20 +89,14 @@ if [[ $CURL_RESPONSE =~ $re ]]; then
|
||||
let allowedSize=OLDFILESIZE+OLDFILESIZE/20 # use 5% threshold
|
||||
echo "Allowed size = $allowedSize"
|
||||
if [[ "$NEWFILESIZE" -gt "$allowedSize" ]]; then
|
||||
msg="ERROR: new size is significally greater than prev size (need to investigate)"
|
||||
echo $msg
|
||||
test_failed_msg $testname $msg $TC_PRINT
|
||||
test_finished_msg $testname $TC_PRINT
|
||||
echo "ERROR: new size is significally greater than prev size (need to investigate)"
|
||||
exit 1
|
||||
else
|
||||
echo "PASSED"
|
||||
test_finished_msg $testname $TC_PRINT
|
||||
fi
|
||||
else
|
||||
msg="ERROR: can't find string with size in xml response:"
|
||||
echo $msg
|
||||
echo "ERROR: can't find string with size in xml response:"
|
||||
echo $CURL_RESPONSE
|
||||
test_failed_msg $testname $msg $TC_PRINT
|
||||
test_finished_msg $testname $TC_PRINT
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
@@ -95,24 +95,8 @@ AC_DEFUN([FLAGS_SETUP_DEBUG_SYMBOLS],
|
||||
# info flags for toolchains unless we know they work.
|
||||
# See JDK-8207057.
|
||||
ASFLAGS_DEBUG_SYMBOLS=""
|
||||
|
||||
# Debug prefix mapping if supported by compiler
|
||||
DEBUG_PREFIX_CFLAGS=
|
||||
|
||||
# Debug symbols
|
||||
if test "x$TOOLCHAIN_TYPE" = xgcc; then
|
||||
if test "x$ALLOW_ABSOLUTE_PATHS_IN_OUTPUT" = "xfalse"; then
|
||||
# Check if compiler supports -fdebug-prefix-map. If so, use that to make
|
||||
# the debug symbol paths resolve to paths relative to the workspace root.
|
||||
workspace_root_trailing_slash="${WORKSPACE_ROOT%/}/"
|
||||
DEBUG_PREFIX_CFLAGS="-fdebug-prefix-map=${workspace_root_trailing_slash}="
|
||||
FLAGS_COMPILER_CHECK_ARGUMENTS(ARGUMENT: [${DEBUG_PREFIX_CFLAGS}],
|
||||
IF_FALSE: [
|
||||
DEBUG_PREFIX_CFLAGS=
|
||||
]
|
||||
)
|
||||
fi
|
||||
|
||||
CFLAGS_DEBUG_SYMBOLS="-g"
|
||||
ASFLAGS_DEBUG_SYMBOLS="-g"
|
||||
elif test "x$TOOLCHAIN_TYPE" = xclang; then
|
||||
@@ -124,11 +108,6 @@ AC_DEFUN([FLAGS_SETUP_DEBUG_SYMBOLS],
|
||||
CFLAGS_DEBUG_SYMBOLS="-Z7"
|
||||
fi
|
||||
|
||||
if test "x$DEBUG_PREFIX_CFLAGS" != x; then
|
||||
CFLAGS_DEBUG_SYMBOLS="$CFLAGS_DEBUG_SYMBOLS $DEBUG_PREFIX_CFLAGS"
|
||||
ASFLAGS_DEBUG_SYMBOLS="$ASFLAGS_DEBUG_SYMBOLS $DEBUG_PREFIX_CFLAGS"
|
||||
fi
|
||||
|
||||
AC_SUBST(CFLAGS_DEBUG_SYMBOLS)
|
||||
AC_SUBST(ASFLAGS_DEBUG_SYMBOLS)
|
||||
])
|
||||
|
||||
55
make/autoconf/version-numbers
Normal file
55
make/autoconf/version-numbers
Normal file
@@ -0,0 +1,55 @@
|
||||
#
|
||||
# Copyright (c) 2011, 2019, 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. Oracle designates this
|
||||
# particular file as subject to the "Classpath" exception as provided
|
||||
# by Oracle in the LICENSE file that accompanied this code.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
# Default version, product, and vendor information to use,
|
||||
# unless overridden by configure
|
||||
|
||||
DEFAULT_VERSION_FEATURE=15
|
||||
DEFAULT_VERSION_INTERIM=0
|
||||
DEFAULT_VERSION_UPDATE=0
|
||||
DEFAULT_VERSION_PATCH=0
|
||||
DEFAULT_VERSION_EXTRA1=0
|
||||
DEFAULT_VERSION_EXTRA2=0
|
||||
DEFAULT_VERSION_EXTRA3=0
|
||||
DEFAULT_VERSION_DATE=2020-09-15
|
||||
DEFAULT_VERSION_CLASSFILE_MAJOR=59 # "`$EXPR $DEFAULT_VERSION_FEATURE + 44`"
|
||||
DEFAULT_VERSION_CLASSFILE_MINOR=0
|
||||
DEFAULT_ACCEPTABLE_BOOT_VERSIONS="14 15"
|
||||
DEFAULT_JDK_SOURCE_TARGET_VERSION=15
|
||||
DEFAULT_PROMOTED_VERSION_PRE=
|
||||
|
||||
LAUNCHER_NAME=openjdk
|
||||
PRODUCT_NAME=OpenJDK
|
||||
PRODUCT_SUFFIX="Runtime Environment"
|
||||
JDK_RC_PLATFORM_NAME=Platform
|
||||
COMPANY_NAME=N/A
|
||||
HOTSPOT_VM_DISTRO="Dynamic Code Evolution"
|
||||
VENDOR_URL=https://openjdk.java.net/
|
||||
VENDOR_URL_BUG=https://bugreport.java.com/bugreport/
|
||||
VENDOR_URL_VM_BUG=https://bugreport.java.com/bugreport/crash.jsp
|
||||
|
||||
# Might need better names for these
|
||||
MACOSX_BUNDLE_NAME_BASE="OpenJDK"
|
||||
MACOSX_BUNDLE_ID_BASE="net.java.openjdk"
|
||||
@@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
# Copyright (c) 2011, 2021, 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
|
||||
@@ -389,12 +389,6 @@ define SetupCompileNativeFileBody
|
||||
$1_OBJ_DEPS := $$($1_SRC_FILE) $$($$($1_BASE)_COMPILE_VARDEPS_FILE) \
|
||||
$$($$($1_BASE)_EXTRA_DEPS) $$($1_VARDEPS_FILE)
|
||||
$1_COMPILE_OPTIONS := $$($1_FLAGS) $(CC_OUT_OPTION)$$($1_OBJ) $$($1_SRC_FILE)
|
||||
# For reproducible builds with gcc ensure random symbol generation is seeded deterministically
|
||||
ifeq ($(TOOLCHAIN_TYPE), gcc)
|
||||
ifeq ($$(ENABLE_REPRODUCIBLE_BUILD), true)
|
||||
$1_COMPILE_OPTIONS += -frandom-seed="$$($1_FILENAME)"
|
||||
endif
|
||||
endif
|
||||
|
||||
$$($1_OBJ_JSON): $$($1_OBJ_DEPS)
|
||||
$$(call WriteCompileCommandsFragment, $$@, $$(PWD), $$($1_SRC_FILE), \
|
||||
@@ -1149,19 +1143,6 @@ define SetupNativeCompilationBody
|
||||
endif
|
||||
endif
|
||||
|
||||
# Debuginfo of ASM objects always embeds the absolute object path,
|
||||
# as ASM debuginfo paths do not get prefix mapped.
|
||||
# So for reproducible builds use relative paths to ensure a reproducible
|
||||
# debuginfo and libs, when creating debug symbols.
|
||||
ifeq ($$(ENABLE_REPRODUCIBLE_BUILD), true)
|
||||
ifeq ($(call isTargetOs, linux), true)
|
||||
ifeq ($$($1_COMPILE_WITH_DEBUG_SYMBOLS), true)
|
||||
$1_LINK_OBJS_RELATIVE := true
|
||||
$1_ALL_OBJS_RELATIVE := $$(patsubst $$(OUTPUTDIR)/%, %, $$($1_ALL_OBJS))
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
$1_TARGET_DEPS := $$($1_ALL_OBJS) $$($1_RES) $$($1_MANIFEST) \
|
||||
$$($1_REAL_MAPFILE) $$($1_VARDEPS_FILE)
|
||||
|
||||
|
||||
@@ -58,9 +58,6 @@ ifeq ($(call check-jvm-feature, compiler2), true)
|
||||
|
||||
ADLC_CFLAGS += -I$(TOPDIR)/src/hotspot/share
|
||||
|
||||
# Add file macro mappings
|
||||
ADLC_CFLAGS += $(FILE_MACRO_CFLAGS)
|
||||
|
||||
$(eval $(call SetupNativeCompilation, BUILD_ADLC, \
|
||||
NAME := adlc, \
|
||||
TYPE := EXECUTABLE, \
|
||||
|
||||
@@ -84,7 +84,7 @@ ifneq ($(call check-jvm-feature, jvmti), true)
|
||||
jvmtiImpl.cpp jvmtiManageCapabilities.cpp jvmtiRawMonitor.cpp jvmtiUtil.cpp jvmtiTrace.cpp \
|
||||
jvmtiCodeBlobEvents.cpp jvmtiEnv.cpp jvmtiRedefineClasses.cpp jvmtiEnvBase.cpp jvmtiEnvThreadState.cpp \
|
||||
jvmtiTagMap.cpp jvmtiEventController.cpp evmCompat.cpp jvmtiEnter.xsl jvmtiExport.cpp \
|
||||
jvmtiClassFileReconstituter.cpp jvmtiTagMapTable.cpp jvmtiEnhancedRedefineClasses.cpp
|
||||
jvmtiClassFileReconstituter.cpp jvmtiTagMapTable.cpp
|
||||
endif
|
||||
|
||||
ifneq ($(call check-jvm-feature, jvmci), true)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 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,10 +204,6 @@ public class MakeZipReproducible {
|
||||
entry.setTimeLocal(timestamp);
|
||||
}
|
||||
|
||||
// Ensure "extra" field is not set from original ZipEntry info that may be not deterministic
|
||||
// eg.may contain specific UID/GID
|
||||
entry.setExtra(null);
|
||||
|
||||
zos.putNextEntry(entry);
|
||||
if (entry.getSize() > 0 && entryInputStream != null) {
|
||||
entryInputStream.transferTo(zos);
|
||||
|
||||
@@ -4034,7 +4034,7 @@ jint Arguments::parse(const JavaVMInitArgs* initial_cmd_args) {
|
||||
// Set object alignment values.
|
||||
set_object_alignment();
|
||||
|
||||
#if INCLUDE_JFR
|
||||
#ifndef ZERO
|
||||
if (FlightRecorder) {
|
||||
if (AllowEnhancedClassRedefinition) {
|
||||
warning("EnhancedClassRedefinition was disabled, it is not allowed in FlightRecorder.");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011, 2020, 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
|
||||
@@ -59,7 +59,6 @@ import java.awt.peer.KeyboardFocusManagerPeer;
|
||||
import java.awt.peer.WindowPeer;
|
||||
import java.util.List;
|
||||
|
||||
import java.util.Objects;
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import sun.awt.AWTAccessor;
|
||||
@@ -284,16 +283,6 @@ public class LWWindowPeer
|
||||
super.disposeImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBackground(final Color c) {
|
||||
Color oldBg = getBackground();
|
||||
if (Objects.equals(oldBg, c)) {
|
||||
return;
|
||||
}
|
||||
super.setBackground(c);
|
||||
updateOpaque();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setVisibleImpl(final boolean visible) {
|
||||
if (!visible && warningWindow != null) {
|
||||
@@ -302,6 +291,10 @@ public class LWWindowPeer
|
||||
updateFocusableWindowState();
|
||||
super.setVisibleImpl(visible);
|
||||
// TODO: update graphicsConfig, see 4868278
|
||||
if (visible) {
|
||||
// Set correct background for a window before making it visible
|
||||
platformWindow.setOpaque(!isTranslucent());
|
||||
}
|
||||
platformWindow.setVisible(visible);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
|
||||
package sun.lwawt.macosx;
|
||||
|
||||
import sun.awt.AWTThreading;
|
||||
import java.awt.Menu;
|
||||
import java.awt.MenuBar;
|
||||
import java.awt.MenuItem;
|
||||
@@ -65,14 +64,14 @@ public class CMenu extends CMenuItem implements MenuPeer {
|
||||
LWCToolkit.targetToPeer(getTarget().getParent());
|
||||
|
||||
if (parent instanceof CMenu) {
|
||||
return AWTThreading.executeWaitToolkit(() -> parent.executeGet(this::nativeCreateSubMenu));
|
||||
return parent.executeGet(this::nativeCreateSubMenu);
|
||||
}
|
||||
if (parent instanceof CMenuBar) {
|
||||
MenuBar parentContainer = (MenuBar)getTarget().getParent();
|
||||
boolean isHelpMenu = parentContainer.getHelpMenu() == getTarget();
|
||||
int insertionLocation = ((CMenuBar)parent).getNextInsertionIndex();
|
||||
return AWTThreading.executeWaitToolkit(() -> parent.executeGet(ptr -> nativeCreateMenu(ptr, isHelpMenu,
|
||||
insertionLocation)));
|
||||
return parent.executeGet(ptr -> nativeCreateMenu(ptr, isHelpMenu,
|
||||
insertionLocation));
|
||||
}
|
||||
throw new InternalError("Parent must be CMenu or CMenuBar");
|
||||
}
|
||||
@@ -85,7 +84,7 @@ public class CMenu extends CMenuItem implements MenuPeer {
|
||||
|
||||
@Override
|
||||
public final void delItem(final int index) {
|
||||
AWTThreading.executeWaitToolkit(() -> execute(ptr -> nativeDeleteItem(ptr, index)));
|
||||
execute(ptr -> nativeDeleteItem(ptr, index));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -33,7 +33,6 @@ import java.awt.event.KeyEvent;
|
||||
import java.awt.peer.MenuItemPeer;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import sun.awt.AWTThreading;
|
||||
import sun.awt.SunToolkit;
|
||||
import sun.lwawt.LWToolkit;
|
||||
|
||||
@@ -62,7 +61,7 @@ public class CMenuItem extends CMenuComponent implements MenuItemPeer {
|
||||
@Override
|
||||
long createModel() {
|
||||
CMenuComponent parent = (CMenuComponent)LWToolkit.targetToPeer(getTarget().getParent());
|
||||
return AWTThreading.executeWaitToolkit(() -> parent.executeGet(ptr->nativeCreate(ptr, isSeparator())));
|
||||
return parent.executeGet(ptr->nativeCreate(ptr, isSeparator()));
|
||||
}
|
||||
@SuppressWarnings("deprecation")
|
||||
public void setLabel(String label, char keyChar, int keyCode, int modifiers) {
|
||||
|
||||
@@ -112,7 +112,6 @@ import sun.lwawt.PlatformComponent;
|
||||
import sun.lwawt.PlatformDropTarget;
|
||||
import sun.lwawt.PlatformWindow;
|
||||
import sun.lwawt.SecurityWarningWindow;
|
||||
import sun.security.action.GetBooleanAction;
|
||||
import sun.util.logging.PlatformLogger;
|
||||
|
||||
@SuppressWarnings("serial") // JDK implementation class
|
||||
@@ -142,13 +141,6 @@ public final class LWCToolkit extends LWToolkit {
|
||||
|
||||
private static CInputMethodDescriptor sInputMethodDescriptor;
|
||||
|
||||
// Listens to EDT state in invokeAndWait() and disposes the invocation event
|
||||
// when EDT becomes free but the invocation event is not yet dispatched (considered lost).
|
||||
// This prevents a deadlock and makes the invocation return some default result.
|
||||
@SuppressWarnings("removal")
|
||||
private static final boolean DISPOSE_INVOCATION_ON_EDT_FREE =
|
||||
AccessController.doPrivileged(new GetBooleanAction("sun.lwawt.macosx.LWCToolkit.invokeAndWait.disposeOnEDTFree"));
|
||||
|
||||
static {
|
||||
System.err.flush();
|
||||
|
||||
@@ -195,7 +187,7 @@ public final class LWCToolkit extends LWToolkit {
|
||||
*/
|
||||
private static final boolean inAWT;
|
||||
|
||||
private static final PlatformLogger log = PlatformLogger.getLogger(LWCToolkit.class.getName());
|
||||
private static final PlatformLogger log = PlatformLogger.getLogger("sun.lwawt.macosx.LWCToolkit");
|
||||
|
||||
public LWCToolkit() {
|
||||
final String extraButtons = "sun.awt.enableExtraMouseButtons";
|
||||
@@ -504,8 +496,7 @@ public final class LWCToolkit extends LWToolkit {
|
||||
if (!(gd instanceof CGraphicsDevice)) {
|
||||
return AWTThreading.executeWaitToolkit(() -> super.getScreenInsets(gc));
|
||||
}
|
||||
CGraphicsDevice cgd = (CGraphicsDevice) gd;
|
||||
return cgd.getScreenInsets();
|
||||
return AWTThreading.executeWaitToolkit(() -> ((CGraphicsDevice)gd).getScreenInsets());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -720,6 +711,30 @@ public final class LWCToolkit extends LWToolkit {
|
||||
}
|
||||
}
|
||||
|
||||
private static final AtomicInteger blockingRunLoopCounter = new AtomicInteger(0);
|
||||
private static final AtomicBoolean priorityInvocationPending = new AtomicBoolean(false);
|
||||
|
||||
@Override
|
||||
public void unsafeNonblockingExecute(Runnable runnable) {
|
||||
if (!EventQueue.isDispatchThread()) {
|
||||
throw new Error("the method must be called on the Event Dispatching thread");
|
||||
}
|
||||
if (runnable == null) return;
|
||||
|
||||
synchronized (priorityInvocationPending) {
|
||||
priorityInvocationPending.set(true);
|
||||
}
|
||||
AWTAccessor.getEventQueueAccessor().createSecondaryLoop(
|
||||
getSystemEventQueue(),
|
||||
() -> blockingRunLoopCounter.get() > 0).enter();
|
||||
|
||||
try {
|
||||
runnable.run();
|
||||
} finally {
|
||||
priorityInvocationPending.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Kicks an event over to the appropriate eventqueue and waits for it to
|
||||
* finish To avoid deadlocking, we manually run the NSRunLoop while waiting
|
||||
@@ -751,18 +766,11 @@ public final class LWCToolkit extends LWToolkit {
|
||||
log.fine("invokeAndWait started: " + runnable);
|
||||
}
|
||||
|
||||
if (isBlockingEventDispatchThread()) {
|
||||
String msg = "invokeAndWait is discarded as the EventDispatch thread is currently blocked";
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine(msg, new Throwable());
|
||||
log.fine(AWTThreading.getInstance(component).printEventDispatchThreadStackTrace().toString());
|
||||
} else if (log.isLoggable(PlatformLogger.Level.INFO)) {
|
||||
StackTraceElement[] stack = new Throwable().getStackTrace();
|
||||
log.info(msg + ". Originated at " + stack[stack.length - 1]);
|
||||
Thread dispatchThread = AWTThreading.getInstance(component).getEventDispatchThread();
|
||||
log.info(dispatchThread.getName() + " at: " + dispatchThread.getStackTrace()[0].toString());
|
||||
}
|
||||
return;
|
||||
boolean nonBlockingRunLoop;
|
||||
|
||||
synchronized (priorityInvocationPending) {
|
||||
nonBlockingRunLoop = priorityInvocationPending.get();
|
||||
if (!nonBlockingRunLoop) blockingRunLoopCounter.incrementAndGet();
|
||||
}
|
||||
|
||||
AWTThreading.TrackedInvocationEvent invocationEvent =
|
||||
@@ -783,22 +791,23 @@ public final class LWCToolkit extends LWToolkit {
|
||||
((LWCToolkit)Toolkit.getDefaultToolkit()).getSystemEventQueueForInvokeAndWait().postEvent(invocationEvent);
|
||||
}
|
||||
|
||||
if (DISPOSE_INVOCATION_ON_EDT_FREE) {
|
||||
CompletableFuture<Void> eventDispatchThreadFreeFuture =
|
||||
AWTThreading.getInstance(component).onEventDispatchThreadFree(() -> {
|
||||
if (!invocationEvent.isCompleted(true)) {
|
||||
// EventQueue is now empty but the posted InvocationEvent is still not dispatched,
|
||||
// consider it lost then.
|
||||
invocationEvent.dispose("InvocationEvent was lost");
|
||||
}
|
||||
});
|
||||
invocationEvent.onDone(() -> eventDispatchThreadFreeFuture.cancel(false));
|
||||
}
|
||||
CompletableFuture<Void> eventDispatchThreadFreeFuture =
|
||||
AWTThreading.getInstance(component).onEventDispatchThreadFree(() -> {
|
||||
if (!invocationEvent.isDone()) {
|
||||
// EventQueue is now empty but the posted InvocationEvent is still not dispatched,
|
||||
// consider it lost then.
|
||||
invocationEvent.dispose("InvocationEvent was lost");
|
||||
}
|
||||
});
|
||||
|
||||
if (!doAWTRunLoop(mediator, false, timeoutSeconds)) {
|
||||
invocationEvent.onDone(() -> eventDispatchThreadFreeFuture.cancel(false));
|
||||
|
||||
if (!doAWTRunLoop(mediator, nonBlockingRunLoop, timeoutSeconds)) {
|
||||
invocationEvent.dispose("InvocationEvent has timed out");
|
||||
}
|
||||
|
||||
if (!nonBlockingRunLoop) blockingRunLoopCounter.decrementAndGet();
|
||||
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("invokeAndWait finished: " + runnable);
|
||||
}
|
||||
@@ -806,8 +815,6 @@ public final class LWCToolkit extends LWToolkit {
|
||||
checkException(invocationEvent);
|
||||
}
|
||||
|
||||
private static native boolean isBlockingEventDispatchThread();
|
||||
|
||||
public static void invokeLater(Runnable event, Component component)
|
||||
throws InvocationTargetException {
|
||||
Objects.requireNonNull(component, "Null component provided to invokeLater");
|
||||
@@ -850,11 +857,6 @@ public final class LWCToolkit extends LWToolkit {
|
||||
*/
|
||||
static native void performOnMainThreadAfterDelay(Runnable r, long delay);
|
||||
|
||||
/**
|
||||
* Schedules a {@code Runnable} execution on the Appkit thread and waits for completion.
|
||||
*/
|
||||
public static native void performOnMainThreadAndWait(Runnable r);
|
||||
|
||||
// DnD support
|
||||
|
||||
@Override
|
||||
|
||||
@@ -69,9 +69,6 @@ static int* gsButtonEventNumber;
|
||||
static NSTimeInterval gNextKeyEventTime;
|
||||
static NSTimeInterval safeDelay;
|
||||
|
||||
#define KEY_CODE_COUNT 128
|
||||
static CGEventFlags keyOwnFlags[KEY_CODE_COUNT];
|
||||
|
||||
static inline CGKeyCode GetCGKeyCode(jint javaKeyCode);
|
||||
|
||||
static void PostMouseEvent(const CGPoint point, CGMouseButton button,
|
||||
@@ -110,20 +107,6 @@ static inline void autoDelay(BOOL isMove) {
|
||||
gNextKeyEventTime = [[NSDate date] timeIntervalSinceReferenceDate] + safeDelay;
|
||||
}
|
||||
|
||||
static void initKeyFlags() {
|
||||
CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStatePrivate);
|
||||
for (CGKeyCode keyCode = 0; keyCode < KEY_CODE_COUNT; keyCode++) {
|
||||
CGEventRef event = CGEventCreateKeyboardEvent(source, keyCode, true);
|
||||
if (event != NULL) {
|
||||
keyOwnFlags[keyCode] = CGEventGetFlags(event);
|
||||
CFRelease(event);
|
||||
}
|
||||
}
|
||||
if (source != NULL) {
|
||||
CFRelease(source);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_lwawt_macosx_CRobot
|
||||
* Method: initRobot
|
||||
@@ -172,8 +155,6 @@ Java_sun_lwawt_macosx_CRobot_initRobot
|
||||
for (i = 0; i < gNumberOfButtons; ++i) {
|
||||
gsButtonEventNumber[i] = ROBOT_EVENT_NUMBER_START;
|
||||
}
|
||||
|
||||
initKeyFlags();
|
||||
}];
|
||||
}
|
||||
}
|
||||
@@ -302,20 +283,6 @@ Java_sun_lwawt_macosx_CRobot_mouseWheel
|
||||
}];
|
||||
}
|
||||
|
||||
// CGEventCreateKeyboardEvent incorrectly handles flags pertinent to non-modifier keys
|
||||
// (e.g. F1-F12 keys always Fn flag set, while arrow keys always have Fn and NumPad flags set).
|
||||
// Those flags are not cleared for following key presses automatically, so we need to do it ourselves.
|
||||
// See JBR-4306 for details.
|
||||
static void clearStickyFlags(CGEventRef event, CGKeyCode keyCode, CGEventFlags flagToCheck) {
|
||||
if (keyCode < KEY_CODE_COUNT && (keyOwnFlags[keyCode] & flagToCheck) == 0) {
|
||||
CGEventFlags flags = CGEventGetFlags(event);
|
||||
CGEventFlags updatedFlags = flags & ~flagToCheck;
|
||||
if (updatedFlags != flags) {
|
||||
CGEventSetFlags(event, updatedFlags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_lwawt_macosx_CRobot
|
||||
* Method: keyEvent
|
||||
@@ -331,10 +298,6 @@ Java_sun_lwawt_macosx_CRobot_keyEvent
|
||||
CGKeyCode keyCode = GetCGKeyCode(javaKeyCode);
|
||||
CGEventRef event = CGEventCreateKeyboardEvent(source, keyCode, keyPressed);
|
||||
if (event != NULL) {
|
||||
// this assumes Robot isn't used to generate Fn key presses
|
||||
clearStickyFlags(event, keyCode, kCGEventFlagMaskSecondaryFn);
|
||||
// there is no NumPad key, so this won't hurt in any case
|
||||
clearStickyFlags(event, keyCode, kCGEventFlagMaskNumericPad);
|
||||
CGEventPost(kCGHIDEventTap, event);
|
||||
CFRelease(event);
|
||||
}
|
||||
|
||||
@@ -620,17 +620,6 @@ JNI_COCOA_EXIT(env);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_lwawt_macosx_LWCToolkit
|
||||
* Method: isBlockingEventDispatchThread
|
||||
* Signature: ()Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_LWCToolkit_isBlockingEventDispatchThread
|
||||
(JNIEnv *env, jclass clz)
|
||||
{
|
||||
return ThreadUtilities.blockingEventDispatchThread;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_lwawt_macosx_LWCToolkit
|
||||
* Method: stopAWTRunLoop
|
||||
@@ -670,23 +659,6 @@ JNI_COCOA_ENTER(env);
|
||||
JNI_COCOA_EXIT(env);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_lwawt_macosx_LWCToolkit
|
||||
* Method: performOnMainThreadAndWait
|
||||
* Signature: (Ljava/lang/Runnable)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_performOnMainThreadAndWait
|
||||
(JNIEnv *env, jclass clz, jobject runnable)
|
||||
{
|
||||
JNI_COCOA_ENTER(env);
|
||||
jobject gRunnable = (*env)->NewGlobalRef(env, runnable);
|
||||
CHECK_NULL(gRunnable);
|
||||
[ThreadUtilities performOnMainThreadWaiting:YES block:^() {
|
||||
JavaRunnable* performer = [[JavaRunnable alloc] initWithRunnable:gRunnable];
|
||||
[performer perform];
|
||||
}];
|
||||
JNI_COCOA_EXIT(env);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_lwawt_macosx_LWCToolkit
|
||||
|
||||
@@ -127,13 +127,6 @@ do { \
|
||||
__attribute__((visibility("default")))
|
||||
@interface ThreadUtilities : NSObject { } /* Extend NSObject so can call performSelectorOnMainThread */
|
||||
|
||||
/*
|
||||
* When a blocking performSelectorOnMainThread is executed from the EventDispatch thread,
|
||||
* and the executed code triggers an opposite blocking a11y call (via LWCToolkit.invokeAndWait)
|
||||
* this is a deadlock case, and then this property is used to discard LWCToolkit.invokeAndWait.
|
||||
*/
|
||||
@property (class, nonatomic, readonly) BOOL blockingEventDispatchThread;
|
||||
|
||||
+ (JNIEnv*)getJNIEnv;
|
||||
+ (JNIEnv*)getJNIEnvUncached;
|
||||
+ (void)detachCurrentThread;
|
||||
|
||||
@@ -50,24 +50,6 @@ static inline void attachCurrentThread(void** env) {
|
||||
|
||||
@implementation ThreadUtilities
|
||||
|
||||
static BOOL _blockingEventDispatchThread = NO;
|
||||
static long eventDispatchThreadPtr = (long)nil;
|
||||
|
||||
static BOOL isEventDispatchThread() {
|
||||
return (long)[NSThread currentThread] == eventDispatchThreadPtr;
|
||||
}
|
||||
|
||||
// The [blockingEventDispatchThread] property is readonly, so we implement a private setter
|
||||
static void setBlockingEventDispatchThread(BOOL value) {
|
||||
assert([NSThread isMainThread]);
|
||||
_blockingEventDispatchThread = value;
|
||||
}
|
||||
|
||||
+ (BOOL) blockingEventDispatchThread {
|
||||
assert([NSThread isMainThread]);
|
||||
return _blockingEventDispatchThread;
|
||||
}
|
||||
|
||||
+ (void)initialize {
|
||||
/* All the standard modes plus ours */
|
||||
javaModes = [[NSArray alloc] initWithObjects:NSDefaultRunLoopMode,
|
||||
@@ -100,10 +82,10 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
}
|
||||
|
||||
/* This is needed because we can't directly pass a block to
|
||||
* performSelectorOnMainThreadWaiting:..waitUntilDone:YES.. since it expects a selector
|
||||
* performSelectorOnMainThreadWaiting .. since it expects a selector
|
||||
*/
|
||||
+ (void)invokeBlock:(void (^)())block {
|
||||
block();
|
||||
block();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -134,19 +116,7 @@ AWT_ASSERT_APPKIT_THREAD;
|
||||
if ([NSThread isMainThread] && wait == YES) {
|
||||
[target performSelector:aSelector withObject:arg];
|
||||
} else {
|
||||
if (wait && isEventDispatchThread()) {
|
||||
void (^block)(void) = ^{
|
||||
setBlockingEventDispatchThread(YES);
|
||||
@try {
|
||||
[target performSelector:aSelector withObject:arg];
|
||||
} @finally {
|
||||
setBlockingEventDispatchThread(NO);
|
||||
}
|
||||
};
|
||||
[self performSelectorOnMainThread:@selector(invokeBlock:) withObject:block waitUntilDone:YES modes:javaModes];
|
||||
} else {
|
||||
[target performSelectorOnMainThread:aSelector withObject:arg waitUntilDone:wait modes:javaModes];
|
||||
}
|
||||
[target performSelectorOnMainThread:aSelector withObject:arg waitUntilDone:wait modes:javaModes];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,16 +143,3 @@ JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_CThreading_isMainThread
|
||||
return [NSThread isMainThread];
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_awt_AWTThreading
|
||||
* Method: notifyEventDispatchThreadStartedNative
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_sun_awt_AWTThreading_notifyEventDispatchThreadStartedNative
|
||||
(JNIEnv *env, jclass c)
|
||||
{
|
||||
@synchronized([ThreadUtilities class]) {
|
||||
eventDispatchThreadPtr = (long)[NSThread currentThread];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ import java.awt.event.WindowEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import sun.awt.AWTThreading;
|
||||
import sun.util.logging.PlatformLogger;
|
||||
|
||||
import sun.awt.dnd.SunDragSourceContextPeer;
|
||||
@@ -87,7 +86,6 @@ class EventDispatchThread extends Thread {
|
||||
}
|
||||
|
||||
public void run() {
|
||||
AWTThreading.getInstance(Thread.currentThread()).notifyEventDispatchThreadStarted();
|
||||
try {
|
||||
pumpEvents(new Conditional() {
|
||||
public boolean evaluate() {
|
||||
@@ -99,8 +97,6 @@ class EventDispatchThread extends Thread {
|
||||
}
|
||||
}
|
||||
|
||||
private static native void registerEventDispatchThread();
|
||||
|
||||
void pumpEvents(Conditional cond) {
|
||||
pumpEvents(ANY_EVENT, cond);
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ import sun.util.logging.PlatformLogger;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.InvocationEvent;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
@@ -16,7 +14,6 @@ import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Used to perform a cross threads (EventDispatch, Toolkit) execution so that the execution does not cause a deadlock.
|
||||
@@ -78,25 +75,14 @@ public class AWTThreading {
|
||||
}
|
||||
|
||||
/**
|
||||
* A boolean value passed to the consumer indicates whether the consumer should perform
|
||||
* a synchronous invocation on the Toolkit thread and wait, or return immediately.
|
||||
*
|
||||
* @see #executeWaitToolkit(Callable).
|
||||
* Same as {@link #executeWaitToolkit(Callable)}, but without returning a value. If requested (as indicated by
|
||||
* the passed parameter), the invoked native method is supposed to wait for the result of invocation on AppKit
|
||||
* thread, and vice versa.
|
||||
*/
|
||||
public static void executeWaitToolkit(Consumer<Boolean> consumer) {
|
||||
public static void executeWaitToolkit(Task runnable) {
|
||||
boolean wait = EventQueue.isDispatchThread();
|
||||
executeWaitToolkit(() -> {
|
||||
consumer.accept(wait);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #executeWaitToolkit(Callable).
|
||||
*/
|
||||
public static void executeWaitToolkit(Runnable runnable) {
|
||||
executeWaitToolkit(() -> {
|
||||
runnable.run();
|
||||
runnable.run(wait);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
@@ -116,6 +102,12 @@ public class AWTThreading {
|
||||
}
|
||||
}
|
||||
|
||||
if (!isEDT && logger.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
// this can cause deadlock if calling thread is holding a lock which EDT might require (e.g. AWT tree lock)
|
||||
logger.fine("AWTThreading.executeWaitToolkit invoked from non-EDT thread", new Throwable());
|
||||
}
|
||||
|
||||
// fallback to default
|
||||
try {
|
||||
return callable.call();
|
||||
} catch (Exception e) {
|
||||
@@ -254,8 +246,8 @@ public class AWTThreading {
|
||||
private final Throwable throwable = new Throwable();
|
||||
private final CompletableFuture<Void> futureResult = new CompletableFuture<>();
|
||||
|
||||
// dispatch or dispose has been started or already completed
|
||||
private final AtomicBoolean isCompletionStarted = new AtomicBoolean(false);
|
||||
// dispatched or disposed
|
||||
private final AtomicBoolean isFinished = new AtomicBoolean(false);
|
||||
|
||||
static TrackedInvocationEvent create(Object source,
|
||||
Runnable onDispatch,
|
||||
@@ -273,7 +265,7 @@ public class AWTThreading {
|
||||
TrackedInvocationEvent thisEvent = eventRef.get();
|
||||
if (!thisEvent.isDispatched()) {
|
||||
// If we're here - this {onDone} is being disposed.
|
||||
thisEvent.completeIfNotYet(() ->
|
||||
thisEvent.finishIfNotYet(() ->
|
||||
// If we're here - this {onDone} is called by the outer AWTAccessor.getInvocationEventAccessor().dispose()
|
||||
// which we do not control, so complete here.
|
||||
thisEvent.futureResult.completeExceptionally(new Throwable("InvocationEvent was disposed"))
|
||||
@@ -306,37 +298,28 @@ public class AWTThreading {
|
||||
|
||||
@Override
|
||||
public void dispatch() {
|
||||
completeIfNotYet(super::dispatch);
|
||||
finishIfNotYet(super::dispatch);
|
||||
futureResult.complete(null);
|
||||
}
|
||||
|
||||
public void dispose(String reason) {
|
||||
completeIfNotYet(() -> AWTAccessor.getInvocationEventAccessor().dispose(this));
|
||||
finishIfNotYet(() -> AWTAccessor.getInvocationEventAccessor().dispose(this));
|
||||
futureResult.completeExceptionally(new Throwable(reason));
|
||||
}
|
||||
|
||||
private void completeIfNotYet(Runnable competeRunnable) {
|
||||
if (!isCompletionStarted.getAndSet(true)) {
|
||||
competeRunnable.run();
|
||||
private void finishIfNotYet(Runnable finish) {
|
||||
if (!isFinished.getAndSet(true)) {
|
||||
finish.run();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the event is dispatched or disposed.
|
||||
*/
|
||||
public boolean isCompleted() {
|
||||
public boolean isDone() {
|
||||
return futureResult.isDone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the event is dispatched or disposed.
|
||||
* If {@code isCompletionInProgress} is true then also checks whether the event
|
||||
* dispatching or disposal is in progress and if so returns true.
|
||||
*/
|
||||
public boolean isCompleted(boolean isCompletionInProgress) {
|
||||
return isCompleted() || (isCompletionInProgress && isCompletionStarted.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the runnable when it's done (immediately if it's done).
|
||||
*/
|
||||
@@ -374,19 +357,6 @@ public class AWTThreading {
|
||||
return eventDispatchThread;
|
||||
}
|
||||
|
||||
public StringWriter printEventDispatchThreadStackTrace() {
|
||||
return printEventDispatchThreadStackTrace(new StringWriter());
|
||||
}
|
||||
|
||||
public StringWriter printEventDispatchThreadStackTrace(StringWriter writer) {
|
||||
assert writer != null;
|
||||
var printer = new PrintWriter(writer);
|
||||
Thread dispatchThread = getEventDispatchThread();
|
||||
printer.println(dispatchThread.getName());
|
||||
Arrays.asList(dispatchThread.getStackTrace()).forEach(frame -> printer.append("\tat ").println(frame));
|
||||
return writer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@code AWTThreading} instance factory.
|
||||
* WARNING: for testing purpose.
|
||||
@@ -395,15 +365,6 @@ public class AWTThreading {
|
||||
theAWTThreadingFactory.set(factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be called on the EventDispatch thread.
|
||||
*/
|
||||
public void notifyEventDispatchThreadStarted() {
|
||||
if (FontUtilities.isMacOSX) notifyEventDispatchThreadStartedNative();
|
||||
}
|
||||
|
||||
private static native void notifyEventDispatchThreadStartedNative();
|
||||
|
||||
public void notifyEventDispatchThreadFree() {
|
||||
List<CompletableFuture<Void>> notifiers = Collections.emptyList();
|
||||
synchronized (eventDispatchThreadStateNotifiers) {
|
||||
@@ -452,4 +413,8 @@ public class AWTThreading {
|
||||
future.complete(null);
|
||||
return future;
|
||||
}
|
||||
|
||||
public interface Task {
|
||||
void run(boolean wait);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,8 +65,6 @@ class XWindowPeer extends XPanelPeer implements WindowPeer,
|
||||
|
||||
static final boolean ENABLE_REPARENTING_CHECK
|
||||
= "true".equals(GetPropertyAction.privilegedGetProperty("reparenting.check"));
|
||||
private static final boolean ENABLE_DESKTOP_CHECK
|
||||
= "true".equals(GetPropertyAction.privilegedGetProperty("transients.desktop.check", "true"));
|
||||
|
||||
// should be synchronized on awtLock
|
||||
private static Set<XWindowPeer> windows = new HashSet<XWindowPeer>();
|
||||
@@ -97,9 +95,6 @@ class XWindowPeer extends XPanelPeer implements WindowPeer,
|
||||
|
||||
private static final AtomicBoolean isStartupNotificationRemoved = new AtomicBoolean();
|
||||
|
||||
private Long desktopId; // guarded by AWT lock
|
||||
private boolean desktopIdInvalid; // guarded by AWT lock
|
||||
|
||||
/*
|
||||
* Focus related flags
|
||||
*/
|
||||
@@ -164,7 +159,6 @@ class XWindowPeer extends XPanelPeer implements WindowPeer,
|
||||
params.put(EVENT_MASK, eventMask);
|
||||
|
||||
XA_NET_WM_STATE = XAtom.get("_NET_WM_STATE");
|
||||
XA_NET_WM_DESKTOP = XAtom.get("_NET_WM_DESKTOP");
|
||||
|
||||
|
||||
params.put(OVERRIDE_REDIRECT, Boolean.valueOf(isOverrideRedirect()));
|
||||
@@ -1736,7 +1730,7 @@ class XWindowPeer extends XPanelPeer implements WindowPeer,
|
||||
if (!allStates && (window.getWMState() != transientForWindow.getWMState())) {
|
||||
return;
|
||||
}
|
||||
if (screenOrDesktopDiffers(window, transientForWindow)) {
|
||||
if (window.getScreenNumber() != transientForWindow.getScreenNumber()) {
|
||||
return;
|
||||
}
|
||||
long bpw = window.getWindow();
|
||||
@@ -1773,7 +1767,7 @@ class XWindowPeer extends XPanelPeer implements WindowPeer,
|
||||
void updateTransientFor() {
|
||||
int state = getWMState();
|
||||
XWindowPeer p = prevTransientFor;
|
||||
while ((p != null) && ((p.getWMState() != state) || screenOrDesktopDiffers(p, this))) {
|
||||
while ((p != null) && ((p.getWMState() != state) || (p.getScreenNumber() != getScreenNumber()))) {
|
||||
p = p.prevTransientFor;
|
||||
}
|
||||
if (p != null) {
|
||||
@@ -1782,7 +1776,7 @@ class XWindowPeer extends XPanelPeer implements WindowPeer,
|
||||
restoreTransientFor(this);
|
||||
}
|
||||
XWindowPeer n = nextTransientFor;
|
||||
while ((n != null) && ((n.getWMState() != state) || screenOrDesktopDiffers(n, this))) {
|
||||
while ((n != null) && ((n.getWMState() != state) || (n.getScreenNumber() != getScreenNumber()))) {
|
||||
n = n.nextTransientFor;
|
||||
}
|
||||
if (n != null) {
|
||||
@@ -1790,38 +1784,6 @@ class XWindowPeer extends XPanelPeer implements WindowPeer,
|
||||
}
|
||||
}
|
||||
|
||||
private Long getDesktopId() {
|
||||
XToolkit.awtLock();
|
||||
try {
|
||||
if (desktopIdInvalid) {
|
||||
desktopIdInvalid = false;
|
||||
desktopId = null;
|
||||
WindowPropertyGetter getter =
|
||||
new WindowPropertyGetter(window, XA_NET_WM_DESKTOP, 0, 1, false, XAtom.XA_CARDINAL);
|
||||
try {
|
||||
if (getter.execute() == XConstants.Success &&
|
||||
getter.getActualType() == XAtom.XA_CARDINAL &&
|
||||
getter.getActualFormat() == 32) {
|
||||
long ptr = getter.getData();
|
||||
if (ptr != 0) {
|
||||
desktopId = Native.getCard32(ptr);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
getter.dispose();
|
||||
}
|
||||
}
|
||||
return desktopId;
|
||||
} finally {
|
||||
XToolkit.awtUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean screenOrDesktopDiffers(XWindowPeer p1, XWindowPeer p2) {
|
||||
return p1.getScreenNumber() != p2.getScreenNumber() ||
|
||||
ENABLE_DESKTOP_CHECK && !Objects.equals(p1.getDesktopId(), p2.getDesktopId());
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes the TRANSIENT_FOR hint from the given top-level window.
|
||||
* If window or transientForWindow are embedded frames, the containing
|
||||
@@ -2201,7 +2163,6 @@ class XWindowPeer extends XPanelPeer implements WindowPeer,
|
||||
cachedFocusableWindow = isFocusableWindow();
|
||||
}
|
||||
|
||||
XAtom XA_NET_WM_DESKTOP;
|
||||
XAtom XA_NET_WM_STATE;
|
||||
XAtomList net_wm_state;
|
||||
public XAtomList getNETWMState() {
|
||||
@@ -2218,18 +2179,6 @@ class XWindowPeer extends XPanelPeer implements WindowPeer,
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePropertyNotify(XEvent xev) {
|
||||
super.handlePropertyNotify(xev);
|
||||
XPropertyEvent ev = xev.get_xproperty();
|
||||
if (ev.get_atom() == XA_NET_WM_DESKTOP.getAtom()) {
|
||||
desktopIdInvalid = true;
|
||||
if (ENABLE_DESKTOP_CHECK) {
|
||||
updateTransientFor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public PropMwmHints getMWMHints() {
|
||||
if (mwm_hints == null) {
|
||||
mwm_hints = new PropMwmHints();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2020, 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
|
||||
@@ -583,8 +583,6 @@ cleanup:
|
||||
XFree (pVI8sg);
|
||||
if (n1sg != 0)
|
||||
XFree (pVI1sg);
|
||||
if (nTrue != 0)
|
||||
XFree (pVITrue);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1432,9 +1430,6 @@ Java_sun_awt_X11GraphicsDevice_getDoubleBufferVisuals(JNIEnv *env,
|
||||
break;
|
||||
}
|
||||
}
|
||||
AWT_LOCK();
|
||||
XdbeFreeVisualInfo(visScreenInfo);
|
||||
AWT_UNLOCK();
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static helper.ToolkitTestHelper.*;
|
||||
import static helper.ToolkitTestHelper.TestCase.*;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary tests that CMenu/CMenuItem methods wrapped with {AWTThreading.executeWaitToolkit} are normally callable
|
||||
* @requires (os.family == "mac")
|
||||
* @modules java.desktop/sun.lwawt.macosx java.desktop/sun.awt
|
||||
* @run main/othervm -Dapple.laf.useScreenMenuBar=true AWTThreadingCMenuTest
|
||||
* @author Anton Tarasov
|
||||
*/
|
||||
public class AWTThreadingCMenuTest {
|
||||
public static void main(String[] args) {
|
||||
initTest(AWTThreadingCMenuTest.class);
|
||||
|
||||
testCase().
|
||||
withCaption("populate Menu").
|
||||
withRunnable(AWTThreadingCMenuTest::test1, true).
|
||||
withExpectedInLog("discarded", false).
|
||||
withCompletionTimeout(1).
|
||||
run();
|
||||
|
||||
testCase().
|
||||
withCaption("de-populate Menu").
|
||||
withRunnable(AWTThreadingCMenuTest::test2, true).
|
||||
withExpectedInLog("discarded", false).
|
||||
withCompletionTimeout(1).
|
||||
run();
|
||||
|
||||
System.out.println("Test PASSED");
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private static void test1() {
|
||||
var bar = new JMenuBar();
|
||||
FRAME.setJMenuBar(bar);
|
||||
|
||||
var menu = new JMenu("Menu");
|
||||
|
||||
Consumer<String> addItem = text -> {
|
||||
var item = new JMenuItem(text);
|
||||
item.setLabel("label " + text);
|
||||
menu.add(item);
|
||||
menu.addSeparator();
|
||||
};
|
||||
|
||||
addItem.accept("one");
|
||||
addItem.accept("two");
|
||||
addItem.accept("three");
|
||||
|
||||
bar.add(menu);
|
||||
|
||||
TEST_CASE_RESULT.complete(true);
|
||||
}
|
||||
|
||||
private static void test2() {
|
||||
var bar = FRAME.getJMenuBar();
|
||||
var menu = bar.getMenu(0);
|
||||
|
||||
for (int i = 0; i < FRAME.getJMenuBar().getMenuCount(); i++) {
|
||||
menu.remove(i);
|
||||
}
|
||||
|
||||
TEST_CASE_RESULT.complete(true);
|
||||
}
|
||||
}
|
||||
@@ -1,134 +1,139 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.*;
|
||||
import javax.swing.*;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import sun.lwawt.macosx.CThreading;
|
||||
import sun.lwawt.macosx.LWCToolkit;
|
||||
import sun.awt.AWTThreading;
|
||||
|
||||
import static helper.ToolkitTestHelper.*;
|
||||
import static helper.ToolkitTestHelper.TestCase.*;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary tests that AWTThreading can manage cross EDT/AppKit blocking invocation requests
|
||||
* @summary tests that AWTThreading can manage a stream of cross EDT/AppKit invocation requests
|
||||
* @requires (os.family == "mac")
|
||||
* @modules java.desktop/sun.lwawt.macosx java.desktop/sun.awt
|
||||
* @run main AWTThreadingTest
|
||||
* @compile --add-exports=java.desktop/sun.lwawt.macosx=ALL-UNNAMED --add-exports=java.desktop/sun.awt=ALL-UNNAMED AWTThreadingTest.java
|
||||
* @run main/othervm --add-exports=java.desktop/sun.lwawt.macosx=ALL-UNNAMED --add-exports=java.desktop/sun.awt=ALL-UNNAMED AWTThreadingTest
|
||||
* @author Anton Tarasov
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public class AWTThreadingTest {
|
||||
static final int TIMEOUT_SECONDS = 1;
|
||||
static final ReentrantLock LOCK = new ReentrantLock();
|
||||
static final Condition COND = LOCK.newCondition();
|
||||
static final CountDownLatch LATCH = new CountDownLatch(1);
|
||||
|
||||
static final AtomicInteger ITER_COUNTER = new AtomicInteger();
|
||||
static final AtomicBoolean DUMP_STACK = new AtomicBoolean(false);
|
||||
static JFrame frame;
|
||||
static Thread thread;
|
||||
|
||||
static volatile Thread THREAD;
|
||||
final static AtomicBoolean passed = new AtomicBoolean(true);
|
||||
final static AtomicInteger counter = new AtomicInteger(0);
|
||||
|
||||
public static void main(String[] args) {
|
||||
DUMP_STACK.set(args.length > 0 && "dumpStack".equals(args[0]));
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
EventQueue.invokeLater(AWTThreadingTest::runGui);
|
||||
|
||||
initTest(AWTThreadingTest.class);
|
||||
LATCH.await(5, TimeUnit.SECONDS);
|
||||
|
||||
testCase().
|
||||
withCaption("certain threads superposition").
|
||||
withRunnable(AWTThreadingTest::test, false).
|
||||
run();
|
||||
|
||||
testCase().
|
||||
withCaption("random threads superposition").
|
||||
withRunnable(AWTThreadingTest::test, false).
|
||||
run();
|
||||
frame.dispose();
|
||||
thread.interrupt();
|
||||
|
||||
if (!passed.get()) {
|
||||
throw new RuntimeException("Test FAILED!");
|
||||
}
|
||||
System.out.println("Test PASSED");
|
||||
}
|
||||
|
||||
static void test() {
|
||||
ITER_COUNTER.set(0);
|
||||
|
||||
var timer = new TestTimer(TIMEOUT_SECONDS * 3, TimeUnit.SECONDS);
|
||||
EventQueue.invokeLater(() -> startThread(() ->
|
||||
TEST_CASE_RESULT.isDone() ||
|
||||
timer.hasFinished()));
|
||||
|
||||
waitTestCaseCompletion(TIMEOUT_SECONDS * 4);
|
||||
|
||||
tryRun(THREAD::join);
|
||||
|
||||
System.out.println(ITER_COUNTER + " iterations passed");
|
||||
static void runGui() {
|
||||
frame = new JFrame("frame");
|
||||
frame.getContentPane().setBackground(Color.green);
|
||||
frame.setLocationRelativeTo(null);
|
||||
frame.setSize(200, 200);
|
||||
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
|
||||
frame.addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowOpened(WindowEvent e) {
|
||||
startThread();
|
||||
}
|
||||
});
|
||||
frame.setVisible(true);
|
||||
}
|
||||
|
||||
static void startThread(Supplier<Boolean> shouldExitLoop) {
|
||||
THREAD = new Thread(() -> {
|
||||
|
||||
while (!shouldExitLoop.get()) {
|
||||
ITER_COUNTER.incrementAndGet();
|
||||
|
||||
var point_1 = new CountDownLatch(1);
|
||||
var point_2 = new CountDownLatch(1);
|
||||
var point_3 = new CountDownLatch(1);
|
||||
var invocations = new CountDownLatch(2);
|
||||
static void startThread() {
|
||||
thread = new Thread(() -> {
|
||||
while (true) {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
// 1. Blocking invocation from AppKit to EDT
|
||||
// 1. Execute invokeAndWait() from AppKit to EDT
|
||||
//
|
||||
CThreading.executeOnAppKit(() -> {
|
||||
// We're on AppKit, wait for the 2nd invocation to be on the AWTThreading-pool thread.
|
||||
if (TEST_CASE_NUM == 1) await(point_1, TIMEOUT_SECONDS);
|
||||
|
||||
tryRun(() -> LWCToolkit.invokeAndWait(() -> {
|
||||
// We're being dispatched on EDT.
|
||||
if (TEST_CASE_NUM == 1) point_2.countDown();
|
||||
|
||||
// Wait for the 2nd invocation to be executed on AppKit.
|
||||
if (TEST_CASE_NUM == 1) await(point_3, TIMEOUT_SECONDS);
|
||||
}, FRAME));
|
||||
|
||||
invocations.countDown();
|
||||
try {
|
||||
LWCToolkit.invokeAndWait(counter::incrementAndGet, Window.getWindows()[0]);
|
||||
} catch (Exception e) {
|
||||
fail(e);
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// 2. Blocking invocation from EDT to AppKit
|
||||
// 2. Execute invokeAndBlock() from EDT to AppKit
|
||||
//
|
||||
EventQueue.invokeLater(() -> AWTThreading.executeWaitToolkit(() -> {
|
||||
// We're on the AWTThreading-pool thread.
|
||||
if (TEST_CASE_NUM == 1) point_1.countDown();
|
||||
EventQueue.invokeLater(() -> {
|
||||
passed.set(false);
|
||||
|
||||
// Wait for the 1st invocation to start NSRunLoop and be dispatched
|
||||
if (TEST_CASE_NUM == 1) await(point_2, TIMEOUT_SECONDS);
|
||||
|
||||
// Perform in JavaRunLoopMode to be accepted by NSRunLoop started by LWCToolkit.invokeAndWait.
|
||||
LWCToolkit.performOnMainThreadAndWait(() -> {
|
||||
if (DUMP_STACK.get()) {
|
||||
dumpAllThreads();
|
||||
Boolean success = AWTThreading.executeWaitToolkit(() -> {
|
||||
try {
|
||||
return CThreading.executeOnAppKit(() -> Boolean.TRUE);
|
||||
} catch (Throwable e) {
|
||||
fail(e);
|
||||
}
|
||||
// We're being executed on AppKit.
|
||||
if (TEST_CASE_NUM == 1) point_3.countDown();
|
||||
return null;
|
||||
});
|
||||
System.out.println("Success: " + counter.get() + ": " + success);
|
||||
|
||||
invocations.countDown();
|
||||
}));
|
||||
passed.set(Boolean.TRUE.equals(success));
|
||||
|
||||
await(invocations, TIMEOUT_SECONDS * 2);
|
||||
} // while
|
||||
if (passed.get()) {
|
||||
lock(COND::signal);
|
||||
}
|
||||
else {
|
||||
fail(null);
|
||||
}
|
||||
});
|
||||
|
||||
TEST_CASE_RESULT.complete(true);
|
||||
lock(COND::await);
|
||||
}
|
||||
});
|
||||
THREAD.setDaemon(true);
|
||||
THREAD.start();
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
static void dumpAllThreads() {
|
||||
Thread.getAllStackTraces().keySet().forEach(t -> {
|
||||
System.out.printf("%s\t%s\t%d\t%s\n", t.getName(), t.getState(), t.getPriority(), t.isDaemon() ? "Daemon" : "Normal");
|
||||
Arrays.asList(t.getStackTrace()).forEach(frame -> System.out.println("\tat " + frame));
|
||||
});
|
||||
System.out.println("\n\n");
|
||||
static void lock(MyRunnable runnable) {
|
||||
LOCK.lock();
|
||||
try {
|
||||
try {
|
||||
runnable.run();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} finally {
|
||||
LOCK.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
interface MyRunnable {
|
||||
void run() throws Exception;
|
||||
}
|
||||
|
||||
static void fail(Throwable e) {
|
||||
if (e != null) e.printStackTrace();
|
||||
passed.set(false);
|
||||
LATCH.countDown();
|
||||
}
|
||||
}
|
||||
@@ -1,35 +1,38 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
|
||||
import java.awt.*;
|
||||
import javax.swing.*;
|
||||
import java.awt.event.InvocationEvent;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.*;
|
||||
|
||||
import sun.awt.AWTAccessor;
|
||||
import sun.awt.AWTThreading;
|
||||
import sun.lwawt.macosx.CThreading;
|
||||
import sun.lwawt.macosx.LWCToolkit;
|
||||
|
||||
import static helper.ToolkitTestHelper.*;
|
||||
import static helper.ToolkitTestHelper.TestCase.*;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Tests different scenarios for LWCToolkit.invokeAndWait().
|
||||
* @requires (os.family == "mac")
|
||||
* @modules java.desktop/sun.lwawt.macosx java.desktop/sun.awt
|
||||
* @run main/othervm -Dsun.lwawt.macosx.LWCToolkit.invokeAndWait.disposeOnEDTFree=true LWCToolkitInvokeAndWaitTest
|
||||
* @run main/othervm -Dlog.level.FINER=true -Dsun.lwawt.macosx.LWCToolkit.invokeAndWait.disposeOnEDTFree=true LWCToolkitInvokeAndWaitTest
|
||||
* @run main LWCToolkitInvokeAndWaitTest
|
||||
* @run main/othervm -DAWTThreading.level.FINER=true LWCToolkitInvokeAndWaitTest
|
||||
* @author Anton Tarasov
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public class LWCToolkitInvokeAndWaitTest {
|
||||
// This property is used in {CAccessibility}
|
||||
static final int INVOKE_TIMEOUT_SECONDS = Integer.getInteger("sun.lwawt.macosx.CAccessibility.invokeTimeoutSeconds", 1);
|
||||
|
||||
static final Runnable CONSUME_DISPATCHING = () -> TEST_CASE_RESULT.completeExceptionally(new Throwable("Unexpected dispatching!"));
|
||||
private static final int INVOKE_TIMEOUT_SECONDS = Integer.getInteger("sun.lwawt.macosx.CAccessibility.invokeTimeoutSeconds", 1);
|
||||
|
||||
static TestLogHandler LOG_HANDLER = new TestLogHandler();
|
||||
static volatile CompletableFuture<Boolean> FUTURE;
|
||||
static volatile CountDownLatch EDT_FAST_FREE_LATCH;
|
||||
static volatile JFrame FRAME;
|
||||
static volatile Thread MAIN_THREAD;
|
||||
static int TEST_COUNTER;
|
||||
|
||||
static final Runnable CONSUME_DISPATCHING = () -> FUTURE.completeExceptionally(new Throwable("Unexpected dispatching!"));
|
||||
|
||||
static {
|
||||
AWTThreading.setAWTThreadingFactory(edt -> new AWTThreading(edt) {
|
||||
@@ -38,7 +41,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
|
||||
tryRun(EDT_FAST_FREE_LATCH::await);
|
||||
trycatch(() -> EDT_FAST_FREE_LATCH.await());
|
||||
|
||||
EDT_FAST_FREE_LATCH = null;
|
||||
}
|
||||
@@ -59,69 +62,101 @@ public class LWCToolkitInvokeAndWaitTest {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
initTest(LWCToolkitInvokeAndWaitTest.class);
|
||||
MAIN_THREAD = Thread.currentThread();
|
||||
|
||||
trycatch(() -> {
|
||||
Logger log = LogManager.getLogManager().getLogger(AWTThreading.class.getName());
|
||||
log.setUseParentHandlers(false);
|
||||
log.addHandler(LOG_HANDLER);
|
||||
if (Boolean.getBoolean("AWTThreading.level.FINER")) {
|
||||
log.setLevel(Level.FINER);
|
||||
}
|
||||
});
|
||||
|
||||
Consumer<InvocationEvent> noop = e -> {};
|
||||
|
||||
testCase().
|
||||
withCaption("InvocationEvent is normally dispatched").
|
||||
withRunnable(() -> test1(noop, () -> System.out.println("I'm dispatched")), true).
|
||||
withCompletionTimeout(INVOKE_TIMEOUT_SECONDS * 2).
|
||||
run();
|
||||
try {
|
||||
trycatch(() -> EventQueue.invokeAndWait(LWCToolkitInvokeAndWaitTest::runGui));
|
||||
|
||||
testCase().
|
||||
withCaption("InvocationEvent is lost").
|
||||
withRunnable(() -> test1(noop, CONSUME_DISPATCHING), true).
|
||||
withCompletionTimeout(INVOKE_TIMEOUT_SECONDS * 2).
|
||||
withExpectedInLog("lost", true).
|
||||
run();
|
||||
test("InvocationEvent is normally dispatched",
|
||||
"",
|
||||
noop,
|
||||
() -> System.out.println("I'm dispatched"));
|
||||
|
||||
EDT_FAST_FREE_LATCH = new CountDownLatch(2);
|
||||
testCase().
|
||||
withCaption("InvocationEvent is lost (EDT becomes fast free)").
|
||||
withRunnable(() -> test1(invocationEvent -> EDT_FAST_FREE_LATCH.countDown(), CONSUME_DISPATCHING), true).
|
||||
withCompletionTimeout(INVOKE_TIMEOUT_SECONDS * 2).
|
||||
withExpectedInLog("lost", true).
|
||||
run();
|
||||
test("InvocationEvent is lost",
|
||||
"lost",
|
||||
noop,
|
||||
CONSUME_DISPATCHING);
|
||||
|
||||
testCase().
|
||||
withCaption("InvocationEvent is disposed").
|
||||
withRunnable(() -> test1(invocationEvent -> AWTAccessor.getInvocationEventAccessor().dispose(invocationEvent), CONSUME_DISPATCHING), true).
|
||||
withCompletionTimeout(INVOKE_TIMEOUT_SECONDS * 2).
|
||||
withExpectedInLog("disposed", true).
|
||||
run();
|
||||
EDT_FAST_FREE_LATCH = new CountDownLatch(2);
|
||||
test("InvocationEvent is lost (EDT becomes fast free)",
|
||||
"lost",
|
||||
// notify the invocationEvent has been dispatched
|
||||
invocationEvent -> EDT_FAST_FREE_LATCH.countDown(),
|
||||
CONSUME_DISPATCHING);
|
||||
|
||||
testCase().
|
||||
withCaption("InvocationEvent is timed out (delayed before dispatching)").
|
||||
withRunnable(() -> test1(invocationEvent -> sleep(INVOKE_TIMEOUT_SECONDS * 4), CONSUME_DISPATCHING), true).
|
||||
withCompletionTimeout(INVOKE_TIMEOUT_SECONDS * 2).
|
||||
withExpectedInLog("timed out", true).
|
||||
run();
|
||||
test("InvocationEvent is disposed",
|
||||
"disposed",
|
||||
invocationEvent -> AWTAccessor.getInvocationEventAccessor().dispose(invocationEvent),
|
||||
CONSUME_DISPATCHING);
|
||||
|
||||
testCase().
|
||||
withCaption("InvocationEvent is timed out (delayed during dispatching)").
|
||||
withRunnable(() -> test1(noop, () -> sleep(INVOKE_TIMEOUT_SECONDS * 4)), true).
|
||||
withCompletionTimeout(INVOKE_TIMEOUT_SECONDS * 2).
|
||||
withExpectedInLog("timed out", true).
|
||||
run();
|
||||
test("InvocationEvent is timed out (delayed before dispatching)",
|
||||
"timed out",
|
||||
invocationEvent -> sleep(INVOKE_TIMEOUT_SECONDS * 4),
|
||||
CONSUME_DISPATCHING);
|
||||
|
||||
testCase().
|
||||
withCaption("invokeAndWait is discarded").
|
||||
withRunnable(LWCToolkitInvokeAndWaitTest::test2, true).
|
||||
withCompletionTimeout(INVOKE_TIMEOUT_SECONDS * 2).
|
||||
withExpectedInLog("discarded", true).
|
||||
run();
|
||||
|
||||
testCase().
|
||||
withCaption("invokeAndWait is passed").
|
||||
withRunnable(LWCToolkitInvokeAndWaitTest::test3, true).
|
||||
withCompletionTimeout(INVOKE_TIMEOUT_SECONDS * 2).
|
||||
run();
|
||||
test("InvocationEvent is timed out (delayed during dispatching)",
|
||||
"timed out",
|
||||
noop,
|
||||
() -> sleep(INVOKE_TIMEOUT_SECONDS * 4));
|
||||
|
||||
} finally {
|
||||
FRAME.dispose();
|
||||
}
|
||||
System.out.println("Test PASSED");
|
||||
}
|
||||
|
||||
static void test1(Consumer<InvocationEvent> onBeforeDispatching, Runnable onDispatching) {
|
||||
static void runGui() {
|
||||
FRAME = new JFrame(LWCToolkitInvokeAndWaitTest.class.getSimpleName());
|
||||
FRAME.getContentPane().setBackground(Color.green);
|
||||
FRAME.setLocationRelativeTo(null);
|
||||
FRAME.setSize(200, 200);
|
||||
FRAME.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
|
||||
FRAME.setVisible(true);
|
||||
}
|
||||
|
||||
static void test(String testCaption,
|
||||
String expectedInLog,
|
||||
Consumer<InvocationEvent> onBeforeDispatching,
|
||||
Runnable onDispatching)
|
||||
{
|
||||
System.out.println("\n(" + (++TEST_COUNTER) + ") TEST: " + testCaption);
|
||||
|
||||
FUTURE = new CompletableFuture<>();
|
||||
FUTURE.whenComplete((r, ex) -> Optional.of(ex).ifPresent(Throwable::printStackTrace));
|
||||
|
||||
EventQueue.invokeLater(() -> subTest(onBeforeDispatching, onDispatching));
|
||||
|
||||
trycatch(() -> {
|
||||
if (!FUTURE.get(INVOKE_TIMEOUT_SECONDS * 2L, TimeUnit.SECONDS)) {
|
||||
throw new RuntimeException("Test FAILED! (negative result)");
|
||||
}
|
||||
});
|
||||
|
||||
// let AppKit and EDT print all the logging
|
||||
var latch = new CountDownLatch(1);
|
||||
CThreading.executeOnAppKit(latch::countDown);
|
||||
trycatch(latch::await);
|
||||
trycatch(() -> EventQueue.invokeAndWait(() -> {}));
|
||||
|
||||
if (!LOG_HANDLER.testContains(expectedInLog)) {
|
||||
throw new RuntimeException("Test FAILED! (not found in the log: \"" + expectedInLog + "\")");
|
||||
}
|
||||
|
||||
System.out.println("(" + TEST_COUNTER + ") SUCCEEDED\n");
|
||||
}
|
||||
|
||||
static void subTest(Consumer<InvocationEvent> onBeforeDispatching, Runnable onDispatching) {
|
||||
Toolkit.getDefaultToolkit().getSystemEventQueue().push(new EventQueue() {
|
||||
@Override
|
||||
protected void dispatchEvent(AWTEvent event) {
|
||||
@@ -140,54 +175,55 @@ public class LWCToolkitInvokeAndWaitTest {
|
||||
super.dispatchEvent(event);
|
||||
}
|
||||
});
|
||||
CThreading.executeOnAppKit(() -> tryRun(() -> {
|
||||
CThreading.executeOnAppKit(() -> trycatch(() -> {
|
||||
//
|
||||
// Post an invocation from AppKit.
|
||||
//
|
||||
LWCToolkit.invokeAndWait(onDispatching, FRAME, false, INVOKE_TIMEOUT_SECONDS);
|
||||
TEST_CASE_RESULT.complete(true);
|
||||
FUTURE.complete(true);
|
||||
}));
|
||||
}
|
||||
|
||||
static void test2() {
|
||||
EventQueue.invokeLater(() ->
|
||||
//
|
||||
// Blocking EDT.
|
||||
//
|
||||
LWCToolkit.performOnMainThreadAndWait(() -> {
|
||||
//
|
||||
// The invocation from AppKit should be discarded.
|
||||
//
|
||||
tryRun(() -> LWCToolkit.invokeAndWait(EMPTY_RUNNABLE, FRAME, false, INVOKE_TIMEOUT_SECONDS * 4));
|
||||
TEST_CASE_RESULT.complete(true);
|
||||
}));
|
||||
static void sleep(int seconds) {
|
||||
trycatch(() -> Thread.sleep(seconds * 1000L));
|
||||
}
|
||||
|
||||
static void test3() {
|
||||
var point = new CountDownLatch(1);
|
||||
|
||||
EventQueue.invokeLater(() -> {
|
||||
// We're on EDT, wait for the second invocation to perform on AppKit.
|
||||
await(point, INVOKE_TIMEOUT_SECONDS * 2);
|
||||
|
||||
// This should be dispatched in the RunLoop started by LWCToolkit.invokeAndWait from the second invocation.
|
||||
LWCToolkit.performOnMainThreadAndWait(() -> TEST_CASE_RESULT.complete(true));
|
||||
});
|
||||
|
||||
LWCToolkit.performOnMainThreadAndWait(() -> {
|
||||
// Notify we're on AppKit.
|
||||
point.countDown();
|
||||
|
||||
// The LWCToolkit.invokeAndWait call starts a native RunLoop.
|
||||
tryRun(() -> LWCToolkit.invokeAndWait(EMPTY_RUNNABLE, FRAME));
|
||||
});
|
||||
}
|
||||
|
||||
static void check(String expectedInLog) {
|
||||
tryRun(() -> {
|
||||
if (!TEST_CASE_RESULT.get(INVOKE_TIMEOUT_SECONDS * 2L, TimeUnit.SECONDS)) {
|
||||
throw new RuntimeException("Test FAILED! (negative result)");
|
||||
|
||||
static void trycatch(ThrowableRunnable runnable) {
|
||||
try {
|
||||
runnable.run();
|
||||
} catch (Exception e) {
|
||||
if (Thread.currentThread() == MAIN_THREAD) {
|
||||
throw new RuntimeException("Test FAILED!", e);
|
||||
} else {
|
||||
FUTURE.completeExceptionally(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
interface ThrowableRunnable {
|
||||
void run() throws Exception;
|
||||
}
|
||||
|
||||
static class TestLogHandler extends StreamHandler {
|
||||
public StringBuilder buffer = new StringBuilder();
|
||||
|
||||
public TestLogHandler() {
|
||||
// Use System.out to merge with the test printing.
|
||||
super(System.out, new SimpleFormatter());
|
||||
setLevel(Level.ALL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publish(LogRecord record) {
|
||||
buffer.append(record.getMessage());
|
||||
super.publish(record);
|
||||
flush();
|
||||
}
|
||||
|
||||
public boolean testContains(String str) {
|
||||
boolean contains = buffer.toString().contains(str);
|
||||
buffer.setLength(0);
|
||||
return contains;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,239 +0,0 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package helper;
|
||||
|
||||
import sun.awt.AWTThreading;
|
||||
import sun.lwawt.macosx.CThreading;
|
||||
import sun.lwawt.macosx.LWCToolkit;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.Timer;
|
||||
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.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.*;
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public class ToolkitTestHelper {
|
||||
public static final Runnable EMPTY_RUNNABLE = () -> {};
|
||||
|
||||
public static final TestLogHandler LOG_HANDLER = new TestLogHandler();
|
||||
|
||||
public static volatile CompletableFuture<Boolean> TEST_CASE_RESULT;
|
||||
public static volatile int TEST_CASE_NUM;
|
||||
public static volatile JFrame FRAME;
|
||||
|
||||
private static volatile JLabel LABEL;
|
||||
private static volatile Thread MAIN_THREAD;
|
||||
|
||||
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_NUM + ")");
|
||||
}
|
||||
};
|
||||
|
||||
public static void initTest(Class<?> testClass) {
|
||||
MAIN_THREAD = Thread.currentThread();
|
||||
|
||||
assert MAIN_THREAD.getName().toLowerCase().contains("main");
|
||||
|
||||
loop(); // init the event threads
|
||||
|
||||
tryRun(() -> {
|
||||
Consumer<Class<?>> setLog = cls -> {
|
||||
Logger log = LogManager.getLogManager().getLogger(cls.getName());
|
||||
log.setUseParentHandlers(false);
|
||||
log.addHandler(LOG_HANDLER);
|
||||
if (Boolean.getBoolean("log.level.FINER")) {
|
||||
log.setLevel(Level.FINER);
|
||||
}
|
||||
};
|
||||
setLog.accept(AWTThreading.class);
|
||||
setLog.accept(LWCToolkit.class);
|
||||
});
|
||||
|
||||
Thread.UncaughtExceptionHandler handler = MAIN_THREAD.getUncaughtExceptionHandler();
|
||||
MAIN_THREAD.setUncaughtExceptionHandler((t, e) -> {
|
||||
if (FRAME != null) FRAME.dispose();
|
||||
handler.uncaughtException(t, e);
|
||||
});
|
||||
|
||||
tryRun(() -> EventQueue.invokeAndWait(() -> {
|
||||
FRAME = new JFrame(testClass.getSimpleName());
|
||||
LABEL = new JLabel("0");
|
||||
LABEL.setFont(LABEL.getFont().deriveFont((float)30));
|
||||
LABEL.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
FRAME.add(LABEL, BorderLayout.CENTER);
|
||||
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 waitTestCaseCompletion(int seconds) {
|
||||
tryRun(() -> {
|
||||
if (!TEST_CASE_RESULT.get(seconds, TimeUnit.SECONDS)) {
|
||||
throw new RuntimeException("Test FAILED! (negative result)");
|
||||
}
|
||||
});
|
||||
|
||||
loop(); // wait for the logging to be printed
|
||||
}
|
||||
|
||||
public static class TestCase {
|
||||
volatile String caption = "";
|
||||
volatile Runnable testRunnable = EMPTY_RUNNABLE;
|
||||
volatile int completionTimeoutSeconds = -1;
|
||||
volatile String expectedInLog = "";
|
||||
volatile boolean expectedIncludedInLog = true;
|
||||
|
||||
public static TestCase testCase() {
|
||||
return new TestCase();
|
||||
}
|
||||
|
||||
public TestCase withCaption(String caption) {
|
||||
this.caption = caption;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TestCase withRunnable(Runnable testRunnable, boolean invokeOnEdt) {
|
||||
this.testRunnable = invokeOnEdt ? () -> EventQueue.invokeLater(testRunnable) : testRunnable;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TestCase withCompletionTimeout(int seconds) {
|
||||
this.completionTimeoutSeconds = seconds;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TestCase withExpectedInLog(String expectedInLog, boolean included) {
|
||||
this.expectedInLog = expectedInLog;
|
||||
this.expectedIncludedInLog = included;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
TEST_CASE_RESULT = new CompletableFuture<>();
|
||||
TEST_CASE_RESULT.whenComplete((r, e) -> Optional.of(e).ifPresent(Throwable::printStackTrace));
|
||||
|
||||
//noinspection NonAtomicOperationOnVolatileField
|
||||
String prefix = "(" + (++TEST_CASE_NUM) + ")";
|
||||
|
||||
System.out.println("\n" + prefix + " TEST: " + caption);
|
||||
UPDATE_LABEL.run();
|
||||
|
||||
testRunnable.run();
|
||||
|
||||
if (completionTimeoutSeconds >= 0) {
|
||||
waitTestCaseCompletion(completionTimeoutSeconds);
|
||||
|
||||
if (expectedIncludedInLog && !LOG_HANDLER.testContains(expectedInLog)) {
|
||||
throw new RuntimeException("Test FAILED! (not found in the log: \"" + expectedInLog + "\")");
|
||||
|
||||
} else if (!expectedIncludedInLog && LOG_HANDLER.testContains(expectedInLog)) {
|
||||
throw new RuntimeException("Test FAILED! (found in the log: \"" + expectedInLog + "\")");
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println(prefix + " SUCCEEDED\n");
|
||||
}
|
||||
}
|
||||
|
||||
public static void tryRun(ThrowableRunnable runnable) {
|
||||
tryCall(() -> {
|
||||
runnable.run();
|
||||
return null;
|
||||
}, null);
|
||||
}
|
||||
|
||||
public static <T> T tryCall(Callable<T> callable, T defValue) {
|
||||
try {
|
||||
return callable.call();
|
||||
} catch (Exception e) {
|
||||
if (Thread.currentThread() == MAIN_THREAD) {
|
||||
throw new RuntimeException("Test FAILED!", e);
|
||||
} else {
|
||||
TEST_CASE_RESULT.completeExceptionally(e);
|
||||
}
|
||||
}
|
||||
return defValue;
|
||||
}
|
||||
|
||||
public static void await(CountDownLatch latch, int seconds) {
|
||||
if (!tryCall(() -> latch.await(seconds, TimeUnit.SECONDS), false)) {
|
||||
TEST_CASE_RESULT.completeExceptionally(new Throwable("Awaiting has timed out"));
|
||||
}
|
||||
}
|
||||
|
||||
public static void sleep(int seconds) {
|
||||
tryRun(() -> Thread.sleep(seconds * 1000L));
|
||||
}
|
||||
|
||||
private static void loop() {
|
||||
tryRun(() -> EventQueue.invokeAndWait(EMPTY_RUNNABLE));
|
||||
var latch = new CountDownLatch(1);
|
||||
CThreading.executeOnAppKit(latch::countDown);
|
||||
tryRun(latch::await);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
public static class TestLogHandler extends StreamHandler {
|
||||
public StringBuilder buffer = new StringBuilder();
|
||||
|
||||
public TestLogHandler() {
|
||||
// Use System.out to merge with the test printing.
|
||||
super(System.out, new SimpleFormatter());
|
||||
setLevel(Level.ALL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publish(LogRecord record) {
|
||||
buffer.append(record.getMessage());
|
||||
super.publish(record);
|
||||
flush();
|
||||
}
|
||||
|
||||
public boolean testContains(String str) {
|
||||
boolean contains = buffer.toString().contains(str);
|
||||
buffer.setLength(0);
|
||||
return contains;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -524,7 +524,6 @@ java/awt/image/VolatileImage/BitmaskVolatileImage.java 8133102 linux-all
|
||||
java/awt/SplashScreen/MultiResolutionSplash/MultiResolutionSplashTest.java 8134231 windows-all,linux-all,macosx-all
|
||||
java/awt/SplashScreen/MultiResolutionSplash/unix/UnixMultiResolutionSplashTest.java 8203004 linux-all
|
||||
java/awt/Robot/AcceptExtraMouseButtons/AcceptExtraMouseButtons.java 7107528 linux-all,macosx-all
|
||||
java/awt/Mouse/GetMousePositionTest/GetMousePositionWithPopup.java 8282232 windows-all
|
||||
java/awt/Mouse/MouseDragEvent/MouseDraggedTest.java 8080676 linux-all
|
||||
java/awt/Mouse/MouseModifiersUnitTest/MouseModifiersInKeyEvent.java 8157147 linux-all,windows-all,macosx-all
|
||||
java/awt/Mouse/TitleBarDoubleClick/TitleBarDoubleClick.java 8148041 linux-all
|
||||
@@ -593,7 +592,6 @@ javax/management/monitor/DerivedGaugeMonitorTest.java 8042211 generic-al
|
||||
# jdk_io
|
||||
|
||||
java/io/pathNames/GeneralWin32.java 8180264 windows-all
|
||||
java/io/File/createTempFile/SpecialTempFile.java 8274122 windows11
|
||||
|
||||
############################################################################
|
||||
|
||||
|
||||
Reference in New Issue
Block a user