diff --git a/make/autoconf/lib-alsa.m4 b/make/autoconf/lib-alsa.m4 index 19a91f94809..8d0fb324cd0 100644 --- a/make/autoconf/lib-alsa.m4 +++ b/make/autoconf/lib-alsa.m4 @@ -70,6 +70,25 @@ AC_DEFUN_ONCE([LIB_SETUP_ALSA], PKG_CHECK_MODULES(ALSA, alsa, [ALSA_FOUND=yes], [ALSA_FOUND=no]) fi fi + if test "x$ALSA_FOUND" = xno; then + # If we have sysroot set, and no explicit library location is set, + # look at known locations in sysroot. + if test "x$SYSROOT" != "x" && test "x${with_alsa_lib}" == x; then + if test -f "$SYSROOT/usr/lib64/libasound.so" && test "x$OPENJDK_TARGET_CPU_BITS" = x64; then + ALSA_LIBS="-L$SYSROOT/usr/lib64 -lasound" + ALSA_FOUND=yes + elif test -f "$SYSROOT/usr/lib/libasound.so"; then + ALSA_LIBS="-L$SYSROOT/usr/lib -lasound" + ALSA_FOUND=yes + elif test -f "$SYSROOT/usr/lib/$OPENJDK_TARGET_CPU-$OPENJDK_TARGET_OS-$OPENJDK_TARGET_ABI/libasound.so"; then + ALSA_LIBS="-L$SYSROOT/usr/lib/$OPENJDK_TARGET_CPU-$OPENJDK_TARGET_OS-$OPENJDK_TARGET_ABI -lasound" + ALSA_FOUND=yes + elif test -f "$SYSROOT/usr/lib/$OPENJDK_TARGET_CPU_AUTOCONF-$OPENJDK_TARGET_OS-$OPENJDK_TARGET_ABI/libasound.so"; then + ALSA_LIBS="-L$SYSROOT/usr/lib/$OPENJDK_TARGET_CPU_AUTOCONF-$OPENJDK_TARGET_OS-$OPENJDK_TARGET_ABI -lasound" + ALSA_FOUND=yes + fi + fi + fi if test "x$ALSA_FOUND" = xno; then AC_CHECK_HEADERS([alsa/asoundlib.h], [ diff --git a/make/autoconf/lib-x11.m4 b/make/autoconf/lib-x11.m4 index b1902a432a1..6849b4a26c7 100644 --- a/make/autoconf/lib-x11.m4 +++ b/make/autoconf/lib-x11.m4 @@ -71,9 +71,9 @@ AC_DEFUN_ONCE([LIB_SETUP_X11], elif test -f "$SYSROOT/usr/lib/libX11.so"; then x_libraries="$SYSROOT/usr/lib" elif test -f "$SYSROOT/usr/lib/$OPENJDK_TARGET_CPU-$OPENJDK_TARGET_OS-$OPENJDK_TARGET_ABI/libX11.so"; then - x_libraries="$SYSROOT/usr/lib/$OPENJDK_TARGET_CPU-$OPENJDK_TARGET_OS-$OPENJDK_TARGET_ABI/libX11.so" + x_libraries="$SYSROOT/usr/lib/$OPENJDK_TARGET_CPU-$OPENJDK_TARGET_OS-$OPENJDK_TARGET_ABI" elif test -f "$SYSROOT/usr/lib/$OPENJDK_TARGET_CPU_AUTOCONF-$OPENJDK_TARGET_OS-$OPENJDK_TARGET_ABI/libX11.so"; then - x_libraries="$SYSROOT/usr/lib/$OPENJDK_TARGET_CPU_AUTOCONF-$OPENJDK_TARGET_OS-$OPENJDK_TARGET_ABI/libX11.so" + x_libraries="$SYSROOT/usr/lib/$OPENJDK_TARGET_CPU_AUTOCONF-$OPENJDK_TARGET_OS-$OPENJDK_TARGET_ABI" fi fi fi diff --git a/make/test/JtregNativeHotspot.gmk b/make/test/JtregNativeHotspot.gmk index ecc7e34c917..fee5fe86031 100644 --- a/make/test/JtregNativeHotspot.gmk +++ b/make/test/JtregNativeHotspot.gmk @@ -871,6 +871,7 @@ ifeq ($(call isTargetOs, windows), true) BUILD_HOTSPOT_JTREG_EXECUTABLES_CFLAGS_exeFPRegs := -MT BUILD_HOTSPOT_JTREG_EXCLUDE += exesigtest.c libterminatedThread.c libTestJNI.c libCompleteExit.c libMonitorWithDeadObjectTest.c libTestPsig.c exeGetCreatedJavaVMs.c BUILD_HOTSPOT_JTREG_LIBRARIES_JDK_LIBS_libnativeStack := java.base:libjvm + BUILD_HOTSPOT_JTREG_LIBRARIES_JDK_LIBS_libVThreadEventTest := java.base:libjvm else BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libbootclssearch_agent += -lpthread BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libsystemclssearch_agent += -lpthread @@ -1509,6 +1510,7 @@ else BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libCompleteExit += -lpthread BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libMonitorWithDeadObjectTest += -lpthread BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libnativeStack += -lpthread + BUILD_HOTSPOT_JTREG_LIBRARIES_JDK_LIBS_libVThreadEventTest := java.base:libjvm BUILD_HOTSPOT_JTREG_EXECUTABLES_LIBS_exeGetCreatedJavaVMs := -lpthread BUILD_HOTSPOT_JTREG_EXECUTABLES_JDK_LIBS_exeGetCreatedJavaVMs := java.base:libjvm diff --git a/src/demo/share/jfc/SwingSet2/SwingSet2.java b/src/demo/share/jfc/SwingSet2/SwingSet2.java index 13b3d63a693..75eeb4e3657 100644 --- a/src/demo/share/jfc/SwingSet2/SwingSet2.java +++ b/src/demo/share/jfc/SwingSet2/SwingSet2.java @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2024, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -848,7 +848,14 @@ private void updateThisSwingSet() { if (frame == null) { SwingUtilities.updateComponentTreeUI(this); } else { + if (currentLookAndFeel.name.contains("GTK")) { + this.setPreferredSize(new Dimension(PREFERRED_WIDTH + 260, PREFERRED_HEIGHT + 80)); + } else { + this.setPreferredSize(new Dimension(PREFERRED_WIDTH, PREFERRED_HEIGHT)); + } + SwingUtilities.updateComponentTreeUI(frame); + frame.pack(); } SwingUtilities.updateComponentTreeUI(popupMenu); diff --git a/src/hotspot/cpu/aarch64/aarch64.ad b/src/hotspot/cpu/aarch64/aarch64.ad index d4b3d634438..d8db75961d8 100644 --- a/src/hotspot/cpu/aarch64/aarch64.ad +++ b/src/hotspot/cpu/aarch64/aarch64.ad @@ -2745,10 +2745,7 @@ typedef void (MacroAssembler::* mem_vector_insn)(FloatRegister Rt, } if (index == -1) { - /* If we get an out-of-range offset it is a bug in the compiler, - so we assert here. */ - assert(Address::offset_ok_for_immed(disp, exact_log2(size_in_memory)), "c2 compiler bug"); - /* Fix up any out-of-range offsets. */ + // Fix up any out-of-range offsets. assert_different_registers(rscratch1, base); Address addr = Address(base, disp); addr = __ legitimize_address(addr, size_in_memory, rscratch1); @@ -3348,7 +3345,11 @@ encode %{ int scale = $mem$$scale; int disp = $mem$$disp; if (index == -1) { - __ prfm(Address(base, disp), PSTL1KEEP); + // Fix up any out-of-range offsets. + assert_different_registers(rscratch1, base); + Address addr = Address(base, disp); + addr = __ legitimize_address(addr, 8, rscratch1); + __ prfm(addr, PSTL1KEEP); } else { Register index_reg = as_Register(index); if (disp == 0) { @@ -4229,9 +4230,9 @@ operand immIOffset16() interface(CONST_INTER); %} -operand immLoffset() +operand immLOffset() %{ - predicate(Address::offset_ok_for_immed(n->get_long(), 0)); + predicate(n->get_long() >= -256 && n->get_long() <= 65520); match(ConL); op_cost(0); @@ -5341,6 +5342,34 @@ operand indOffL16(iRegP reg, immLoffset16 off) %} %} +operand indirectX2P(iRegL reg) +%{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(CastX2P reg); + op_cost(0); + format %{ "[$reg]\t# long -> ptr" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0xffffffff); + scale(0x0); + disp(0x0); + %} +%} + +operand indOffX2P(iRegL reg, immLOffset off) +%{ + constraint(ALLOC_IN_RC(ptr_reg)); + match(AddP (CastX2P reg) off); + op_cost(0); + format %{ "[$reg, $off]\t# long -> ptr" %} + interface(MEMORY_INTER) %{ + base($reg); + index(0xffffffff); + scale(0x0); + disp($off); + %} +%} + operand indirectN(iRegN reg) %{ predicate(CompressedOops::shift() == 0); @@ -5431,7 +5460,7 @@ operand indOffIN(iRegN reg, immIOffset off) %} %} -operand indOffLN(iRegN reg, immLoffset off) +operand indOffLN(iRegN reg, immLOffset off) %{ predicate(CompressedOops::shift() == 0); constraint(ALLOC_IN_RC(ptr_reg)); @@ -5664,6 +5693,17 @@ operand iRegL2I(iRegL reg) %{ interface(REG_INTER) %} +operand iRegL2P(iRegL reg) %{ + + op_cost(0); + + match(CastX2P reg); + + format %{ "l2p($reg)" %} + + interface(REG_INTER) +%} + opclass vmem2(indirect, indIndex, indOffI2, indOffL2); opclass vmem4(indirect, indIndex, indOffI4, indOffL4); opclass vmem8(indirect, indIndex, indOffI8, indOffL8); @@ -5680,21 +5720,21 @@ opclass vmem16(indirect, indIndex, indOffI16, indOffL16); // instruction defs. we can turn a memory op into an Address opclass memory1(indirect, indIndexScaled, indIndexScaledI2L, indIndexI2L, indIndex, indOffI1, indOffL1, - indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN); + indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN, indirectX2P, indOffX2P); opclass memory2(indirect, indIndexScaled, indIndexScaledI2L, indIndexI2L, indIndex, indOffI2, indOffL2, - indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN); + indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN, indirectX2P, indOffX2P); opclass memory4(indirect, indIndexScaled, indIndexScaledI2L, indIndexI2L, indIndex, indOffI4, indOffL4, - indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN, indOffIN, indOffLN); + indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN, indOffIN, indOffLN, indirectX2P, indOffX2P); opclass memory8(indirect, indIndexScaled, indIndexScaledI2L, indIndexI2L, indIndex, indOffI8, indOffL8, - indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN, indOffIN, indOffLN); + indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN, indOffIN, indOffLN, indirectX2P, indOffX2P); // All of the memory operands. For the pipeline description. opclass memory(indirect, indIndexScaled, indIndexScaledI2L, indIndexI2L, indIndex, indOffI1, indOffL1, indOffI2, indOffL2, indOffI4, indOffL4, indOffI8, indOffL8, - indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN, indOffIN, indOffLN); + indirectN, indIndexScaledN, indIndexScaledI2LN, indIndexI2LN, indIndexN, indOffIN, indOffLN, indirectX2P, indOffX2P); // iRegIorL2I is used for src inputs in rules for 32 bit int (I) @@ -5711,6 +5751,7 @@ opclass memory(indirect, indIndexScaled, indIndexScaledI2L, indIndexI2L, indInde // movw is actually redundant but its not too costly. opclass iRegIorL2I(iRegI, iRegL2I); +opclass iRegPorL2P(iRegP, iRegL2P); //----------PIPELINE----------------------------------------------------------- // Rules which define the behavior of the target architectures pipeline. @@ -9811,7 +9852,7 @@ instruct addI_reg_imm_i2l(iRegINoSp dst, iRegL src1, immIAddSub src2) %{ %} // Pointer Addition -instruct addP_reg_reg(iRegPNoSp dst, iRegP src1, iRegL src2) %{ +instruct addP_reg_reg(iRegPNoSp dst, iRegPorL2P src1, iRegL src2) %{ match(Set dst (AddP src1 src2)); ins_cost(INSN_COST); @@ -9826,7 +9867,7 @@ instruct addP_reg_reg(iRegPNoSp dst, iRegP src1, iRegL src2) %{ ins_pipe(ialu_reg_reg); %} -instruct addP_reg_reg_ext(iRegPNoSp dst, iRegP src1, iRegIorL2I src2) %{ +instruct addP_reg_reg_ext(iRegPNoSp dst, iRegPorL2P src1, iRegIorL2I src2) %{ match(Set dst (AddP src1 (ConvI2L src2))); ins_cost(1.9 * INSN_COST); @@ -9841,7 +9882,7 @@ instruct addP_reg_reg_ext(iRegPNoSp dst, iRegP src1, iRegIorL2I src2) %{ ins_pipe(ialu_reg_reg); %} -instruct addP_reg_reg_lsl(iRegPNoSp dst, iRegP src1, iRegL src2, immIScale scale) %{ +instruct addP_reg_reg_lsl(iRegPNoSp dst, iRegPorL2P src1, iRegL src2, immIScale scale) %{ match(Set dst (AddP src1 (LShiftL src2 scale))); ins_cost(1.9 * INSN_COST); @@ -9856,7 +9897,7 @@ instruct addP_reg_reg_lsl(iRegPNoSp dst, iRegP src1, iRegL src2, immIScale scale ins_pipe(ialu_reg_reg_shift); %} -instruct addP_reg_reg_ext_shift(iRegPNoSp dst, iRegP src1, iRegIorL2I src2, immIScale scale) %{ +instruct addP_reg_reg_ext_shift(iRegPNoSp dst, iRegPorL2P src1, iRegIorL2I src2, immIScale scale) %{ match(Set dst (AddP src1 (LShiftL (ConvI2L src2) scale))); ins_cost(1.9 * INSN_COST); @@ -9889,7 +9930,7 @@ instruct lshift_ext(iRegLNoSp dst, iRegIorL2I src, immI scale, rFlagsReg cr) %{ // Pointer Immediate Addition // n.b. this needs to be more expensive than using an indirect memory // operand -instruct addP_reg_imm(iRegPNoSp dst, iRegP src1, immLAddSub src2) %{ +instruct addP_reg_imm(iRegPNoSp dst, iRegPorL2P src1, immLAddSub src2) %{ match(Set dst (AddP src1 src2)); ins_cost(INSN_COST); diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/c1/shenandoahBarrierSetC1_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shenandoah/c1/shenandoahBarrierSetC1_aarch64.cpp index c02f93313b3..261502d5823 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/c1/shenandoahBarrierSetC1_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/c1/shenandoahBarrierSetC1_aarch64.cpp @@ -41,8 +41,6 @@ void LIR_OpShenandoahCompareAndSwap::emit_code(LIR_Assembler* masm) { Register tmp2 = _tmp2->as_register(); Register result = result_opr()->as_register(); - ShenandoahBarrierSet::assembler()->iu_barrier(masm->masm(), newval, rscratch2); - if (UseCompressedOops) { __ encode_heap_oop(tmp1, cmpval); cmpval = tmp1; @@ -102,10 +100,6 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_xchg_at_resolved(LIRAccess& access, LIRIt value.load_item(); LIR_Opr value_opr = value.result(); - if (access.is_oop()) { - value_opr = iu_barrier(access.gen(), value_opr, access.access_emit_info(), access.decorators()); - } - assert(type == T_INT || is_reference_type(type) LP64_ONLY( || type == T_LONG ), "unexpected type"); LIR_Opr tmp = gen->new_register(T_INT); __ xchg(access.resolved_addr(), value_opr, result, tmp); diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp index 5db29729239..06f43820156 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp @@ -47,7 +47,7 @@ void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler* masm, Dec Register src, Register dst, Register count, RegSet saved_regs) { if (is_oop) { bool dest_uninitialized = (decorators & IS_DEST_UNINITIALIZED) != 0; - if ((ShenandoahSATBBarrier && !dest_uninitialized) || ShenandoahIUBarrier || ShenandoahLoadRefBarrier) { + if ((ShenandoahSATBBarrier && !dest_uninitialized) || ShenandoahLoadRefBarrier) { Label done; @@ -300,14 +300,6 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm, __ leave(); } -void ShenandoahBarrierSetAssembler::iu_barrier(MacroAssembler* masm, Register dst, Register tmp) { - if (ShenandoahIUBarrier) { - __ push_call_clobbered_registers(); - satb_write_barrier_pre(masm, noreg, dst, rthread, tmp, rscratch1, true, false); - __ pop_call_clobbered_registers(); - } -} - // // Arguments: // @@ -398,8 +390,7 @@ void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet if (val == noreg) { BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp3, 0), noreg, noreg, noreg, noreg); } else { - iu_barrier(masm, val, tmp1); - // G1 barrier needs uncompressed oop for region cross check. + // Barrier needs uncompressed oop for region cross check. Register new_val = val; if (UseCompressedOops) { new_val = rscratch2; diff --git a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp index 375893702e1..ee11b2e73f7 100644 --- a/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp +++ b/src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp @@ -60,9 +60,6 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { void load_reference_barrier(MacroAssembler* masm, Register dst, Address load_addr, DecoratorSet decorators); public: - - void iu_barrier(MacroAssembler* masm, Register dst, Register tmp); - virtual NMethodPatchingType nmethod_patching_type() { return NMethodPatchingType::conc_data_patch; } #ifdef COMPILER1 diff --git a/src/hotspot/cpu/aarch64/gc/x/x_aarch64.ad b/src/hotspot/cpu/aarch64/gc/x/x_aarch64.ad index c7c7165affb..6e401724baa 100644 --- a/src/hotspot/cpu/aarch64/gc/x/x_aarch64.ad +++ b/src/hotspot/cpu/aarch64/gc/x/x_aarch64.ad @@ -1,5 +1,5 @@ // -// Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. +// Copyright (c) 2019, 2024, 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 @@ -62,7 +62,13 @@ instruct xLoadP(iRegPNoSp dst, memory8 mem, rFlagsReg cr) format %{ "ldr $dst, $mem" %} ins_encode %{ - const Address ref_addr = mem2address($mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp); + Address ref_addr = mem2address($mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp); + if (ref_addr.getMode() == Address::base_plus_offset) { + // Fix up any out-of-range offsets. + assert_different_registers(rscratch1, as_Register($mem$$base)); + assert_different_registers(rscratch1, $dst$$Register); + ref_addr = __ legitimize_address(ref_addr, 8, rscratch1); + } __ ldr($dst$$Register, ref_addr); x_load_barrier(masm, this, ref_addr, $dst$$Register, rscratch2 /* tmp */, barrier_data()); %} diff --git a/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad b/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad index 92181e2b6b9..56d45384779 100644 --- a/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad +++ b/src/hotspot/cpu/aarch64/gc/z/z_aarch64.ad @@ -1,5 +1,5 @@ // -// Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. +// Copyright (c) 2019, 2024, 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 @@ -111,7 +111,13 @@ instruct zLoadP(iRegPNoSp dst, memory8 mem, rFlagsReg cr) format %{ "ldr $dst, $mem" %} ins_encode %{ - const Address ref_addr = mem2address($mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp); + Address ref_addr = mem2address($mem->opcode(), as_Register($mem$$base), $mem$$index, $mem$$scale, $mem$$disp); + if (ref_addr.getMode() == Address::base_plus_offset) { + // Fix up any out-of-range offsets. + assert_different_registers(rscratch2, as_Register($mem$$base)); + assert_different_registers(rscratch2, $dst$$Register); + ref_addr = __ legitimize_address(ref_addr, 8, rscratch2); + } __ ldr($dst$$Register, ref_addr); z_load_barrier(masm, this, ref_addr, $dst$$Register, rscratch1); %} diff --git a/src/hotspot/cpu/aarch64/smallRegisterMap_aarch64.inline.hpp b/src/hotspot/cpu/aarch64/smallRegisterMap_aarch64.inline.hpp index db38b9cf5df..dcba233c9dc 100644 --- a/src/hotspot/cpu/aarch64/smallRegisterMap_aarch64.inline.hpp +++ b/src/hotspot/cpu/aarch64/smallRegisterMap_aarch64.inline.hpp @@ -30,8 +30,15 @@ // Java frames don't have callee saved registers (except for rfp), so we can use a smaller RegisterMap class SmallRegisterMap { + constexpr SmallRegisterMap() = default; + ~SmallRegisterMap() = default; + NONCOPYABLE(SmallRegisterMap); + public: - static constexpr SmallRegisterMap* instance = nullptr; + static const SmallRegisterMap* instance() { + static constexpr SmallRegisterMap the_instance{}; + return &the_instance; + } private: static void assert_is_rfp(VMReg r) NOT_DEBUG_RETURN DEBUG_ONLY({ assert (r == rfp->as_VMReg() || r == rfp->as_VMReg()->next(), "Reg: %s", r->name()); }) @@ -48,17 +55,6 @@ class SmallRegisterMap { return map; } - SmallRegisterMap() {} - - SmallRegisterMap(const RegisterMap* map) { - #ifdef ASSERT - for(int i = 0; i < RegisterMap::reg_count; i++) { - VMReg r = VMRegImpl::as_VMReg(i); - if (map->location(r, (intptr_t*)nullptr) != nullptr) assert_is_rfp(r); - } - #endif - } - inline address location(VMReg reg, intptr_t* sp) const { assert_is_rfp(reg); return (address)(sp - frame::sender_sp_offset); diff --git a/src/hotspot/cpu/arm/smallRegisterMap_arm.inline.hpp b/src/hotspot/cpu/arm/smallRegisterMap_arm.inline.hpp index 4186eafd35f..08adbbd89d8 100644 --- a/src/hotspot/cpu/arm/smallRegisterMap_arm.inline.hpp +++ b/src/hotspot/cpu/arm/smallRegisterMap_arm.inline.hpp @@ -30,8 +30,15 @@ // Java frames don't have callee saved registers (except for rfp), so we can use a smaller RegisterMap class SmallRegisterMap { + constexpr SmallRegisterMap() = default; + ~SmallRegisterMap() = default; + NONCOPYABLE(SmallRegisterMap); + public: - static constexpr SmallRegisterMap* instance = nullptr; + static const SmallRegisterMap* instance() { + static constexpr SmallRegisterMap the_instance{}; + return &the_instance; + } private: static void assert_is_rfp(VMReg r) NOT_DEBUG_RETURN DEBUG_ONLY({ Unimplemented(); }) @@ -46,12 +53,6 @@ class SmallRegisterMap { return map; } - SmallRegisterMap() {} - - SmallRegisterMap(const RegisterMap* map) { - Unimplemented(); - } - inline address location(VMReg reg, intptr_t* sp) const { Unimplemented(); return nullptr; diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/c1/shenandoahBarrierSetC1_ppc.cpp b/src/hotspot/cpu/ppc/gc/shenandoah/c1/shenandoahBarrierSetC1_ppc.cpp index 6d9a1db1ed4..5f87281bdf9 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/c1/shenandoahBarrierSetC1_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/c1/shenandoahBarrierSetC1_ppc.cpp @@ -43,11 +43,6 @@ void LIR_OpShenandoahCompareAndSwap::emit_code(LIR_Assembler *masm) { Register tmp2 = _tmp2->as_register(); Register result = result_opr()->as_register(); - if (ShenandoahIUBarrier) { - ShenandoahBarrierSet::assembler()->iu_barrier(masm->masm(), new_val, tmp1, tmp2, - MacroAssembler::PRESERVATION_FRAME_LR_GP_FP_REGS); - } - if (UseCompressedOops) { __ encode_heap_oop(cmp_val, cmp_val); __ encode_heap_oop(new_val, new_val); @@ -122,10 +117,6 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_xchg_at_resolved(LIRAccess &access, LIRIt value.load_item(); LIR_Opr value_opr = value.result(); - if (access.is_oop()) { - value_opr = iu_barrier(access.gen(), value_opr, access.access_emit_info(), access.decorators()); - } - assert(type == T_INT || is_reference_type(type) LP64_ONLY( || type == T_LONG ), "unexpected type"); LIR_Opr tmp_xchg = gen->new_register(T_INT); __ xchg(access.resolved_addr(), value_opr, result, tmp_xchg); diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp index 535fe88a680..62aa9a2bf59 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.cpp @@ -61,20 +61,6 @@ void ShenandoahBarrierSetAssembler::satb_write_barrier(MacroAssembler *masm, } } -void ShenandoahBarrierSetAssembler::iu_barrier(MacroAssembler *masm, - Register val, - Register tmp1, Register tmp2, - MacroAssembler::PreservationLevel preservation_level, - DecoratorSet decorators) { - // IU barriers are also employed to avoid resurrection of weak references, - // even if Shenandoah does not operate in incremental update mode. - if (ShenandoahIUBarrier || ShenandoahSATBBarrier) { - __ block_comment("iu_barrier (shenandoahgc) {"); - satb_write_barrier_impl(masm, decorators, noreg, noreg, val, tmp1, tmp2, preservation_level); - __ block_comment("} iu_barrier (shenandoahgc)"); - } -} - void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler *masm, DecoratorSet decorators, Register base, RegisterOrConstant ind_or_offs, Register dst, @@ -110,7 +96,7 @@ void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler *masm, Dec // Fast path: No barrier required if for every barrier type, it is either disabled or would not store // any useful information. - if ((!ShenandoahSATBBarrier || dest_uninitialized) && !ShenandoahIUBarrier && !ShenandoahLoadRefBarrier) { + if ((!ShenandoahSATBBarrier || dest_uninitialized) && !ShenandoahLoadRefBarrier) { return; } @@ -582,7 +568,11 @@ void ShenandoahBarrierSetAssembler::load_at( /* ==== Apply keep-alive barrier, if required (e.g., to inhibit weak reference resurrection) ==== */ if (ShenandoahBarrierSet::need_keep_alive_barrier(decorators, type)) { - iu_barrier(masm, dst, tmp1, tmp2, preservation_level); + if (ShenandoahSATBBarrier) { + __ block_comment("keep_alive_barrier (shenandoahgc) {"); + satb_write_barrier_impl(masm, 0, noreg, noreg, dst, tmp1, tmp2, preservation_level); + __ block_comment("} keep_alive_barrier (shenandoahgc)"); + } } } @@ -597,10 +587,6 @@ void ShenandoahBarrierSetAssembler::store_at(MacroAssembler *masm, DecoratorSet if (ShenandoahSATBBarrier) { satb_write_barrier(masm, base, ind_or_offs, tmp1, tmp2, tmp3, preservation_level); } - - if (ShenandoahIUBarrier && val != noreg) { - iu_barrier(masm, val, tmp1, tmp2, preservation_level, decorators); - } } BarrierSetAssembler::store_at(masm, decorators, type, diff --git a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp index 4514f2540ac..2e56187c169 100644 --- a/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp +++ b/src/hotspot/cpu/ppc/gc/shenandoah/shenandoahBarrierSetAssembler_ppc.hpp @@ -82,11 +82,6 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { Register tmp1, Register tmp2, Register tmp3, MacroAssembler::PreservationLevel preservation_level); - void iu_barrier(MacroAssembler* masm, - Register val, - Register tmp1, Register tmp2, - MacroAssembler::PreservationLevel preservation_level, DecoratorSet decorators = 0); - void load_reference_barrier(MacroAssembler* masm, DecoratorSet decorators, Register base, RegisterOrConstant ind_or_offs, Register dst, diff --git a/src/hotspot/cpu/ppc/smallRegisterMap_ppc.inline.hpp b/src/hotspot/cpu/ppc/smallRegisterMap_ppc.inline.hpp index 8c96f51fd88..a6246cd7e74 100644 --- a/src/hotspot/cpu/ppc/smallRegisterMap_ppc.inline.hpp +++ b/src/hotspot/cpu/ppc/smallRegisterMap_ppc.inline.hpp @@ -30,9 +30,16 @@ // Java frames don't have callee saved registers, so we can use a smaller RegisterMap class SmallRegisterMap { + constexpr SmallRegisterMap() = default; + ~SmallRegisterMap() = default; + NONCOPYABLE(SmallRegisterMap); + public: - static constexpr SmallRegisterMap* instance = nullptr; -public: + static const SmallRegisterMap* instance() { + static constexpr SmallRegisterMap the_instance{}; + return &the_instance; + } + // as_RegisterMap is used when we didn't want to templatize and abstract over RegisterMap type to support SmallRegisterMap // Consider enhancing SmallRegisterMap to support those cases const RegisterMap* as_RegisterMap() const { return nullptr; } @@ -44,19 +51,6 @@ class SmallRegisterMap { return map; } - SmallRegisterMap() {} - - SmallRegisterMap(const RegisterMap* map) { -#ifdef ASSERT - for(int i = 0; i < RegisterMap::reg_count; i++) { - VMReg r = VMRegImpl::as_VMReg(i); - if (map->location(r, (intptr_t*)nullptr) != nullptr) { - assert(false, "Reg: %s", r->name()); // Should not reach here - } - } -#endif - } - inline address location(VMReg reg, intptr_t* sp) const { assert(false, "Reg: %s", reg->name()); return nullptr; diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/c1/shenandoahBarrierSetC1_riscv.cpp b/src/hotspot/cpu/riscv/gc/shenandoah/c1/shenandoahBarrierSetC1_riscv.cpp index cd568cc723f..f503cb762e7 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/c1/shenandoahBarrierSetC1_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/c1/shenandoahBarrierSetC1_riscv.cpp @@ -41,8 +41,6 @@ void LIR_OpShenandoahCompareAndSwap::emit_code(LIR_Assembler* masm) { Register tmp2 = _tmp2->as_register(); Register result = result_opr()->as_register(); - ShenandoahBarrierSet::assembler()->iu_barrier(masm->masm(), newval, t1); - if (UseCompressedOops) { __ encode_heap_oop(tmp1, cmpval); cmpval = tmp1; @@ -94,10 +92,6 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_xchg_at_resolved(LIRAccess& access, LIRIt value.load_item(); LIR_Opr value_opr = value.result(); - if (access.is_oop()) { - value_opr = iu_barrier(access.gen(), value_opr, access.access_emit_info(), access.decorators()); - } - assert(type == T_INT || is_reference_type(type) LP64_ONLY( || type == T_LONG ), "unexpected type"); LIR_Opr tmp = gen->new_register(T_INT); __ xchg(access.resolved_addr(), value_opr, result, tmp); diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp index 9be8259e7e8..9a79a923277 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.cpp @@ -48,7 +48,7 @@ void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler* masm, Dec Register src, Register dst, Register count, RegSet saved_regs) { if (is_oop) { bool dest_uninitialized = (decorators & IS_DEST_UNINITIALIZED) != 0; - if ((ShenandoahSATBBarrier && !dest_uninitialized) || ShenandoahIUBarrier || ShenandoahLoadRefBarrier) { + if ((ShenandoahSATBBarrier && !dest_uninitialized) || ShenandoahLoadRefBarrier) { Label done; @@ -308,16 +308,6 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm, __ leave(); } -void ShenandoahBarrierSetAssembler::iu_barrier(MacroAssembler* masm, Register dst, Register tmp) { - if (ShenandoahIUBarrier) { - __ push_call_clobbered_registers(); - - satb_write_barrier_pre(masm, noreg, dst, xthread, tmp, t0, true, false); - - __ pop_call_clobbered_registers(); - } -} - // // Arguments: // @@ -420,8 +410,7 @@ void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet if (val == noreg) { BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp3, 0), noreg, noreg, noreg, noreg); } else { - iu_barrier(masm, val, tmp1); - // G1 barrier needs uncompressed oop for region cross check. + // Barrier needs uncompressed oop for region cross check. Register new_val = val; if (UseCompressedOops) { new_val = t1; diff --git a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp index 0ae352f325d..bfdea7f607e 100644 --- a/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp +++ b/src/hotspot/cpu/riscv/gc/shenandoah/shenandoahBarrierSetAssembler_riscv.hpp @@ -63,8 +63,6 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { public: - void iu_barrier(MacroAssembler* masm, Register dst, Register tmp); - virtual NMethodPatchingType nmethod_patching_type() { return NMethodPatchingType::conc_data_patch; } #ifdef COMPILER1 diff --git a/src/hotspot/cpu/riscv/smallRegisterMap_riscv.inline.hpp b/src/hotspot/cpu/riscv/smallRegisterMap_riscv.inline.hpp index 93adaadcef6..9fc4f1d7b0a 100644 --- a/src/hotspot/cpu/riscv/smallRegisterMap_riscv.inline.hpp +++ b/src/hotspot/cpu/riscv/smallRegisterMap_riscv.inline.hpp @@ -30,8 +30,15 @@ // Java frames don't have callee saved registers (except for fp), so we can use a smaller RegisterMap class SmallRegisterMap { + constexpr SmallRegisterMap() = default; + ~SmallRegisterMap() = default; + NONCOPYABLE(SmallRegisterMap); + public: - static constexpr SmallRegisterMap* instance = nullptr; + static const SmallRegisterMap* instance() { + static constexpr SmallRegisterMap the_instance{}; + return &the_instance; + } private: static void assert_is_fp(VMReg r) NOT_DEBUG_RETURN DEBUG_ONLY({ assert (r == fp->as_VMReg() || r == fp->as_VMReg()->next(), "Reg: %s", r->name()); }) @@ -48,17 +55,6 @@ class SmallRegisterMap { return map; } - SmallRegisterMap() {} - - SmallRegisterMap(const RegisterMap* map) { - #ifdef ASSERT - for(int i = 0; i < RegisterMap::reg_count; i++) { - VMReg r = VMRegImpl::as_VMReg(i); - if (map->location(r, (intptr_t*)nullptr) != nullptr) assert_is_fp(r); - } - #endif - } - inline address location(VMReg reg, intptr_t* sp) const { assert_is_fp(reg); return (address)(sp - 2); diff --git a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp index 2a9faee7e2f..f78d7261e40 100644 --- a/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp +++ b/src/hotspot/cpu/riscv/stubGenerator_riscv.cpp @@ -5936,7 +5936,6 @@ static const int64_t right_3_bits = right_n_bits(3); } void generate_compiler_stubs() { -#if COMPILER2_OR_JVMCI #ifdef COMPILER2 if (UseMulAddIntrinsic) { StubRoutines::_mulAdd = generate_mulAdd(); @@ -5970,7 +5969,6 @@ static const int64_t right_3_bits = right_n_bits(3); StubRoutines::_bigIntegerLeftShiftWorker = generate_bigIntegerLeftShift(); StubRoutines::_bigIntegerRightShiftWorker = generate_bigIntegerRightShift(); } -#endif // COMPILER2 if (UseSHA256Intrinsics) { Sha2Generator sha2(_masm, this); @@ -5984,10 +5982,6 @@ static const int64_t right_3_bits = right_n_bits(3); StubRoutines::_sha512_implCompressMB = sha2.generate_sha512_implCompress(true); } - generate_compare_long_strings(); - - generate_string_indexof_stubs(); - if (UseMD5Intrinsics) { StubRoutines::_md5_implCompress = generate_md5_implCompress(false, "md5_implCompress"); StubRoutines::_md5_implCompressMB = generate_md5_implCompress(true, "md5_implCompressMB"); @@ -6006,7 +6000,11 @@ static const int64_t right_3_bits = right_n_bits(3); StubRoutines::_updateBytesAdler32 = generate_updateBytesAdler32(); } -#endif // COMPILER2_OR_JVMCI + generate_compare_long_strings(); + + generate_string_indexof_stubs(); + +#endif // COMPILER2 } public: diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.cpp b/src/hotspot/cpu/riscv/vm_version_riscv.cpp index c52c809f935..ac2d6cde1a2 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.cpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.cpp @@ -58,6 +58,13 @@ void VM_Version::useRVA23U64Profile() { } void VM_Version::initialize() { + common_initialize(); +#ifdef COMPILER2 + c2_initialize(); +#endif // COMPILER2 +} + +void VM_Version::common_initialize() { _supports_atomic_getset4 = true; _supports_atomic_getadd4 = true; _supports_atomic_getset8 = true; @@ -152,10 +159,6 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseVectorizedMismatchIntrinsic, false); } - if (FLAG_IS_DEFAULT(UseMD5Intrinsics)) { - FLAG_SET_DEFAULT(UseMD5Intrinsics, true); - } - if (FLAG_IS_DEFAULT(UsePoly1305Intrinsics)) { FLAG_SET_DEFAULT(UsePoly1305Intrinsics, true); } @@ -230,15 +233,105 @@ void VM_Version::initialize() { _initial_vector_length = cpu_vector_length(); } } +} #ifdef COMPILER2 - c2_initialize(); -#endif // COMPILER2 +void VM_Version::c2_initialize() { + if (UseCMoveUnconditionally) { + FLAG_SET_DEFAULT(UseCMoveUnconditionally, false); + } + + if (ConditionalMoveLimit > 0) { + FLAG_SET_DEFAULT(ConditionalMoveLimit, 0); + } - // NOTE: Make sure codes dependent on UseRVV are put after c2_initialize(), + if (!UseRVV) { + FLAG_SET_DEFAULT(MaxVectorSize, 0); + FLAG_SET_DEFAULT(UseRVVForBigIntegerShiftIntrinsics, false); + } else { + if (!FLAG_IS_DEFAULT(MaxVectorSize) && MaxVectorSize != _initial_vector_length) { + warning("Current system does not support RVV vector length for MaxVectorSize %d. Set MaxVectorSize to %d", + (int)MaxVectorSize, _initial_vector_length); + } + MaxVectorSize = _initial_vector_length; + if (MaxVectorSize < 16) { + warning("RVV does not support vector length less than 16 bytes. Disabling RVV."); + UseRVV = false; + FLAG_SET_DEFAULT(MaxVectorSize, 0); + } + } + + // NOTE: Make sure codes dependent on UseRVV are put after MaxVectorSize initialize, // as there are extra checks inside it which could disable UseRVV // in some situations. + if (FLAG_IS_DEFAULT(UseVectorizedHashCodeIntrinsic)) { + FLAG_SET_DEFAULT(UseVectorizedHashCodeIntrinsic, true); + } + + if (!UseZicbop) { + if (!FLAG_IS_DEFAULT(AllocatePrefetchStyle)) { + warning("Zicbop is not available on this CPU"); + } + FLAG_SET_DEFAULT(AllocatePrefetchStyle, 0); + } else { + // Limit AllocatePrefetchDistance so that it does not exceed the + // static constraint of 512 defined in runtime/globals.hpp. + if (FLAG_IS_DEFAULT(AllocatePrefetchDistance)) { + FLAG_SET_DEFAULT(AllocatePrefetchDistance, MIN2(512, 3 * (int)CacheLineSize)); + } + if (FLAG_IS_DEFAULT(AllocatePrefetchStepSize)) { + FLAG_SET_DEFAULT(AllocatePrefetchStepSize, (int)CacheLineSize); + } + if (FLAG_IS_DEFAULT(PrefetchScanIntervalInBytes)) { + FLAG_SET_DEFAULT(PrefetchScanIntervalInBytes, 3 * (int)CacheLineSize); + } + if (FLAG_IS_DEFAULT(PrefetchCopyIntervalInBytes)) { + FLAG_SET_DEFAULT(PrefetchCopyIntervalInBytes, 3 * (int)CacheLineSize); + } + + if (PrefetchCopyIntervalInBytes != -1 && + ((PrefetchCopyIntervalInBytes & 7) || (PrefetchCopyIntervalInBytes >= 32768))) { + warning("PrefetchCopyIntervalInBytes must be -1, or a multiple of 8 and < 32768"); + PrefetchCopyIntervalInBytes &= ~7; + if (PrefetchCopyIntervalInBytes >= 32768) { + PrefetchCopyIntervalInBytes = 32760; + } + } + if (AllocatePrefetchDistance !=-1 && (AllocatePrefetchDistance & 7)) { + warning("AllocatePrefetchDistance must be multiple of 8"); + AllocatePrefetchDistance &= ~7; + } + if (AllocatePrefetchStepSize & 7) { + warning("AllocatePrefetchStepSize must be multiple of 8"); + AllocatePrefetchStepSize &= ~7; + } + } + + if (FLAG_IS_DEFAULT(UseMulAddIntrinsic)) { + FLAG_SET_DEFAULT(UseMulAddIntrinsic, true); + } + + if (FLAG_IS_DEFAULT(UseMultiplyToLenIntrinsic)) { + FLAG_SET_DEFAULT(UseMultiplyToLenIntrinsic, true); + } + + if (FLAG_IS_DEFAULT(UseSquareToLenIntrinsic)) { + FLAG_SET_DEFAULT(UseSquareToLenIntrinsic, true); + } + + if (FLAG_IS_DEFAULT(UseMontgomeryMultiplyIntrinsic)) { + FLAG_SET_DEFAULT(UseMontgomeryMultiplyIntrinsic, true); + } + + if (FLAG_IS_DEFAULT(UseMontgomerySquareIntrinsic)) { + FLAG_SET_DEFAULT(UseMontgomerySquareIntrinsic, true); + } + + if (FLAG_IS_DEFAULT(UseMD5Intrinsics)) { + FLAG_SET_DEFAULT(UseMD5Intrinsics, true); + } + // Adler32 if (UseRVV) { if (FLAG_IS_DEFAULT(UseAdler32Intrinsics)) { @@ -252,7 +345,9 @@ void VM_Version::initialize() { } // ChaCha20 - if (UseRVV) { + if (UseRVV && MaxVectorSize >= 32) { + // performance tests on hardwares (MaxVectorSize == 16, 32) show that + // it brings regression when MaxVectorSize == 16. if (FLAG_IS_DEFAULT(UseChaCha20Intrinsics)) { FLAG_SET_DEFAULT(UseChaCha20Intrinsics, true); } @@ -330,96 +425,6 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseSHA, false); } } - -#ifdef COMPILER2 -void VM_Version::c2_initialize() { - if (UseCMoveUnconditionally) { - FLAG_SET_DEFAULT(UseCMoveUnconditionally, false); - } - - if (ConditionalMoveLimit > 0) { - FLAG_SET_DEFAULT(ConditionalMoveLimit, 0); - } - - if (!UseRVV) { - FLAG_SET_DEFAULT(MaxVectorSize, 0); - FLAG_SET_DEFAULT(UseRVVForBigIntegerShiftIntrinsics, false); - } else { - if (!FLAG_IS_DEFAULT(MaxVectorSize) && MaxVectorSize != _initial_vector_length) { - warning("Current system does not support RVV vector length for MaxVectorSize %d. Set MaxVectorSize to %d", - (int)MaxVectorSize, _initial_vector_length); - } - MaxVectorSize = _initial_vector_length; - if (MaxVectorSize < 16) { - warning("RVV does not support vector length less than 16 bytes. Disabling RVV."); - UseRVV = false; - FLAG_SET_DEFAULT(MaxVectorSize, 0); - } - } - - if (FLAG_IS_DEFAULT(UseVectorizedHashCodeIntrinsic)) { - FLAG_SET_DEFAULT(UseVectorizedHashCodeIntrinsic, true); - } - - if (!UseZicbop) { - if (!FLAG_IS_DEFAULT(AllocatePrefetchStyle)) { - warning("Zicbop is not available on this CPU"); - } - FLAG_SET_DEFAULT(AllocatePrefetchStyle, 0); - } else { - // Limit AllocatePrefetchDistance so that it does not exceed the - // static constraint of 512 defined in runtime/globals.hpp. - if (FLAG_IS_DEFAULT(AllocatePrefetchDistance)) { - FLAG_SET_DEFAULT(AllocatePrefetchDistance, MIN2(512, 3 * (int)CacheLineSize)); - } - if (FLAG_IS_DEFAULT(AllocatePrefetchStepSize)) { - FLAG_SET_DEFAULT(AllocatePrefetchStepSize, (int)CacheLineSize); - } - if (FLAG_IS_DEFAULT(PrefetchScanIntervalInBytes)) { - FLAG_SET_DEFAULT(PrefetchScanIntervalInBytes, 3 * (int)CacheLineSize); - } - if (FLAG_IS_DEFAULT(PrefetchCopyIntervalInBytes)) { - FLAG_SET_DEFAULT(PrefetchCopyIntervalInBytes, 3 * (int)CacheLineSize); - } - - if (PrefetchCopyIntervalInBytes != -1 && - ((PrefetchCopyIntervalInBytes & 7) || (PrefetchCopyIntervalInBytes >= 32768))) { - warning("PrefetchCopyIntervalInBytes must be -1, or a multiple of 8 and < 32768"); - PrefetchCopyIntervalInBytes &= ~7; - if (PrefetchCopyIntervalInBytes >= 32768) { - PrefetchCopyIntervalInBytes = 32760; - } - } - if (AllocatePrefetchDistance !=-1 && (AllocatePrefetchDistance & 7)) { - warning("AllocatePrefetchDistance must be multiple of 8"); - AllocatePrefetchDistance &= ~7; - } - if (AllocatePrefetchStepSize & 7) { - warning("AllocatePrefetchStepSize must be multiple of 8"); - AllocatePrefetchStepSize &= ~7; - } - } - - if (FLAG_IS_DEFAULT(UseMulAddIntrinsic)) { - FLAG_SET_DEFAULT(UseMulAddIntrinsic, true); - } - - if (FLAG_IS_DEFAULT(UseMultiplyToLenIntrinsic)) { - FLAG_SET_DEFAULT(UseMultiplyToLenIntrinsic, true); - } - - if (FLAG_IS_DEFAULT(UseSquareToLenIntrinsic)) { - FLAG_SET_DEFAULT(UseSquareToLenIntrinsic, true); - } - - if (FLAG_IS_DEFAULT(UseMontgomeryMultiplyIntrinsic)) { - FLAG_SET_DEFAULT(UseMontgomeryMultiplyIntrinsic, true); - } - - if (FLAG_IS_DEFAULT(UseMontgomerySquareIntrinsic)) { - FLAG_SET_DEFAULT(UseMontgomerySquareIntrinsic, true); - } -} #endif // COMPILER2 void VM_Version::initialize_cpu_information(void) { diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.hpp b/src/hotspot/cpu/riscv/vm_version_riscv.hpp index 9556e2dc9ad..bd4bfe86d9b 100644 --- a/src/hotspot/cpu/riscv/vm_version_riscv.hpp +++ b/src/hotspot/cpu/riscv/vm_version_riscv.hpp @@ -264,6 +264,8 @@ class VM_Version : public Abstract_VM_Version { static uint32_t cpu_vector_length(); static uint32_t _initial_vector_length; + static void common_initialize(); + #ifdef COMPILER2 static void c2_initialize(); #endif // COMPILER2 diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.cpp b/src/hotspot/cpu/s390/macroAssembler_s390.cpp index 66d6f25f0fd..72d10ee80aa 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.cpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2024 SAP SE. All rights reserved. + * Copyright 2024 IBM Corporation. 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 @@ -3152,6 +3153,301 @@ void MacroAssembler::check_klass_subtype(Register sub_klass, BLOCK_COMMENT("} check_klass_subtype"); } +// scans r_count pointer sized words at [r_addr] for occurrence of r_value, +// generic (r_count must be >0) +// iff found: CC eq, r_result == 0 +void MacroAssembler::repne_scan(Register r_addr, Register r_value, Register r_count, Register r_result) { + NearLabel L_loop, L_exit; + + BLOCK_COMMENT("repne_scan {"); +#ifdef ASSERT + z_chi(r_count, 0); + asm_assert(bcondHigh, "count must be positive", 11); +#endif + + clear_reg(r_result, true /* whole_reg */, false /* set_cc */); // sets r_result=0, let's hope that search will be successful + + bind(L_loop); + z_cg(r_value, Address(r_addr)); + z_bre(L_exit); // branch on success + z_la(r_addr, wordSize, r_addr); + z_brct(r_count, L_loop); + + // z_brct above doesn't change CC. + // If we reach here, then the value in r_value is not present. Set r_result to 1. + z_lghi(r_result, 1); + + bind(L_exit); + BLOCK_COMMENT("} repne_scan"); +} + +// Ensure that the inline code and the stub are using the same registers. +#define LOOKUP_SECONDARY_SUPERS_TABLE_REGISTERS \ +do { \ + assert(r_super_klass == Z_ARG1 && \ + r_array_base == Z_ARG5 && \ + r_array_length == Z_ARG4 && \ + (r_array_index == Z_ARG3 || r_array_index == noreg) && \ + (r_sub_klass == Z_ARG2 || r_sub_klass == noreg) && \ + (r_bitmap == Z_R10 || r_bitmap == noreg) && \ + (r_result == Z_R11 || r_result == noreg), "registers must match s390.ad"); \ +} while(0) + +// Note: this method also kills Z_R1_scratch register on machines older than z15 +void MacroAssembler::lookup_secondary_supers_table(Register r_sub_klass, + Register r_super_klass, + Register r_temp1, + Register r_temp2, + Register r_temp3, + Register r_temp4, + Register r_result, + u1 super_klass_slot) { + NearLabel L_done, L_failure; + + BLOCK_COMMENT("lookup_secondary_supers_table {"); + + const Register + r_array_base = r_temp1, + r_array_length = r_temp2, + r_array_index = r_temp3, + r_bitmap = r_temp4; + + LOOKUP_SECONDARY_SUPERS_TABLE_REGISTERS; + + z_lg(r_bitmap, Address(r_sub_klass, Klass::bitmap_offset())); + + // First check the bitmap to see if super_klass might be present. If + // the bit is zero, we are certain that super_klass is not one of + // the secondary supers. + u1 bit = super_klass_slot; + int shift_count = Klass::SECONDARY_SUPERS_TABLE_MASK - bit; + + z_sllg(r_array_index, r_bitmap, shift_count); // take the bit to 63rd location + + // Initialize r_result with 0 (indicating success). If searching fails, r_result will be loaded + // with 1 (failure) at the end of this method. + clear_reg(r_result, true /* whole_reg */, false /* set_cc */); // r_result = 0 + + // We test the MSB of r_array_index, i.e., its sign bit + testbit(r_array_index, 63); + z_bfalse(L_failure); // if not set, then jump!!! + + // We will consult the secondary-super array. + z_lg(r_array_base, Address(r_sub_klass, Klass::secondary_supers_offset())); + + // The value i in r_array_index is >= 1, so even though r_array_base + // points to the length, we don't need to adjust it to point to the + // data. + assert(Array::base_offset_in_bytes() == wordSize, "Adjust this code"); + + // Get the first array index that can contain super_klass. + if (bit != 0) { + pop_count_long(r_array_index, r_array_index, Z_R1_scratch); // kills Z_R1_scratch on machines older than z15 + + // NB! r_array_index is off by 1. It is compensated by keeping r_array_base off by 1 word. + z_sllg(r_array_index, r_array_index, LogBytesPerWord); // scale + } else { + // Actually use index 0, but r_array_base and r_array_index are off by 1 word + // such that the sum is precise. + z_lghi(r_array_index, BytesPerWord); // for slow path (scaled) + } + + z_cg(r_super_klass, Address(r_array_base, r_array_index)); + branch_optimized(bcondEqual, L_done); // found a match; success + + // Is there another entry to check? Consult the bitmap. + testbit(r_bitmap, (bit + 1) & Klass::SECONDARY_SUPERS_TABLE_MASK); + z_bfalse(L_failure); + + // Linear probe. Rotate the bitmap so that the next bit to test is + // in Bit 2 for the look-ahead check in the slow path. + if (bit != 0) { + z_rllg(r_bitmap, r_bitmap, 64-bit); // rotate right + } + + // Calls into the stub generated by lookup_secondary_supers_table_slow_path. + // Arguments: r_super_klass, r_array_base, r_array_index, r_bitmap. + // Kills: r_array_length. + // Returns: r_result + + call_stub(StubRoutines::lookup_secondary_supers_table_slow_path_stub()); + + z_bru(L_done); // pass whatever result we got from a slow path + + bind(L_failure); + // TODO: use load immediate on condition and z_bru above will not be required + z_lghi(r_result, 1); + + bind(L_done); + BLOCK_COMMENT("} lookup_secondary_supers_table"); + + if (VerifySecondarySupers) { + verify_secondary_supers_table(r_sub_klass, r_super_klass, r_result, + r_temp1, r_temp2, r_temp3); + } +} + +// Called by code generated by check_klass_subtype_slow_path +// above. This is called when there is a collision in the hashed +// lookup in the secondary supers array. +void MacroAssembler::lookup_secondary_supers_table_slow_path(Register r_super_klass, + Register r_array_base, + Register r_array_index, + Register r_bitmap, + Register r_result, + Register r_temp1) { + assert_different_registers(r_super_klass, r_array_base, r_array_index, r_bitmap, r_result, r_temp1); + + const Register + r_array_length = r_temp1, + r_sub_klass = noreg; + + LOOKUP_SECONDARY_SUPERS_TABLE_REGISTERS; + + BLOCK_COMMENT("lookup_secondary_supers_table_slow_path {"); + NearLabel L_done, L_failure; + + // Load the array length. + z_llgf(r_array_length, Address(r_array_base, Array::length_offset_in_bytes())); + + // And adjust the array base to point to the data. + // NB! + // Effectively increments the current slot index by 1. + assert(Array::base_offset_in_bytes() == wordSize, ""); + add2reg(r_array_base, Array::base_offset_in_bytes()); + + // Linear probe + NearLabel L_huge; + + // The bitmap is full to bursting. + z_cghi(r_bitmap, Klass::SECONDARY_SUPERS_BITMAP_FULL); + z_bre(L_huge); + + // NB! Our caller has checked bits 0 and 1 in the bitmap. The + // current slot (at secondary_supers[r_array_index]) has not yet + // been inspected, and r_array_index may be out of bounds if we + // wrapped around the end of the array. + + { // This is conventional linear probing, but instead of terminating + // when a null entry is found in the table, we maintain a bitmap + // in which a 0 indicates missing entries. + // The check above guarantees there are 0s in the bitmap, so the loop + // eventually terminates. + +#ifdef ASSERT + // r_result is set to 0 by lookup_secondary_supers_table. + // clear_reg(r_result, true /* whole_reg */, false /* set_cc */); + z_cghi(r_result, 0); + asm_assert(bcondEqual, "r_result required to be 0, used by z_locgr", 44); + + // We should only reach here after having found a bit in the bitmap. + z_ltgr(r_array_length, r_array_length); + asm_assert(bcondHigh, "array_length > 0, should hold", 22); +#endif // ASSERT + + // Compute limit in r_array_length + add2reg(r_array_length, -1); + z_sllg(r_array_length, r_array_length, LogBytesPerWord); + + NearLabel L_loop; + bind(L_loop); + + // Check for wraparound. + z_cgr(r_array_index, r_array_length); + z_locgr(r_array_index, r_result, bcondHigh); // r_result is containing 0 + + z_cg(r_super_klass, Address(r_array_base, r_array_index)); + z_bre(L_done); // success + + // look-ahead check: if Bit 2 is 0, we're done + testbit(r_bitmap, 2); + z_bfalse(L_failure); + + z_rllg(r_bitmap, r_bitmap, 64-1); // rotate right + add2reg(r_array_index, BytesPerWord); + + z_bru(L_loop); + } + + { // Degenerate case: more than 64 secondary supers. + // FIXME: We could do something smarter here, maybe a vectorized + // comparison or a binary search, but is that worth any added + // complexity? + + bind(L_huge); + repne_scan(r_array_base, r_super_klass, r_array_length, r_result); + + z_bru(L_done); // forward the result we got from repne_scan + } + + bind(L_failure); + z_lghi(r_result, 1); + + bind(L_done); + BLOCK_COMMENT("} lookup_secondary_supers_table_slow_path"); +} + +// Make sure that the hashed lookup and a linear scan agree. +void MacroAssembler::verify_secondary_supers_table(Register r_sub_klass, + Register r_super_klass, + Register r_result /* expected */, + Register r_temp1, + Register r_temp2, + Register r_temp3) { + assert_different_registers(r_sub_klass, r_super_klass, r_result, r_temp1, r_temp2, r_temp3); + + const Register + r_array_base = r_temp1, + r_array_length = r_temp2, + r_array_index = r_temp3, + r_bitmap = noreg; // unused + + const Register r_one = Z_R0_scratch; + z_lghi(r_one, 1); // for locgr down there, to a load result for failure + + LOOKUP_SECONDARY_SUPERS_TABLE_REGISTERS; + + BLOCK_COMMENT("verify_secondary_supers_table {"); + + Label L_passed, L_failure; + + // We will consult the secondary-super array. + z_lg(r_array_base, Address(r_sub_klass, in_bytes(Klass::secondary_supers_offset()))); + + // Load the array length. + z_llgf(r_array_length, Address(r_array_base, Array::length_offset_in_bytes())); + + // And adjust the array base to point to the data. + z_aghi(r_array_base, Array::base_offset_in_bytes()); + + const Register r_linear_result = r_array_index; // reuse + z_chi(r_array_length, 0); + z_locgr(r_linear_result, r_one, bcondNotHigh); // load failure if array_length <= 0 + z_brc(bcondNotHigh, L_failure); + repne_scan(r_array_base, r_super_klass, r_array_length, r_linear_result); + bind(L_failure); + + z_cr(r_result, r_linear_result); + z_bre(L_passed); + + assert_different_registers(Z_ARG1, r_sub_klass, r_linear_result, r_result); + lgr_if_needed(Z_ARG1, r_super_klass); + assert_different_registers(Z_ARG2, r_linear_result, r_result); + lgr_if_needed(Z_ARG2, r_sub_klass); + assert_different_registers(Z_ARG3, r_result); + z_lgr(Z_ARG3, r_linear_result); + z_lgr(Z_ARG4, r_result); + const char* msg = "mismatch"; + load_const_optimized(Z_ARG5, (address)msg); + + call_VM_leaf(CAST_FROM_FN_PTR(address, Klass::on_secondary_supers_verification_failure)); + should_not_reach_here(); + + bind(L_passed); + + BLOCK_COMMENT("} verify_secondary_supers_table"); +} + void MacroAssembler::clinit_barrier(Register klass, Register thread, Label* L_fast_path, Label* L_slow_path) { assert(L_fast_path != nullptr || L_slow_path != nullptr, "at least one is required"); diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.hpp b/src/hotspot/cpu/s390/macroAssembler_s390.hpp index 684741d79db..90210eb28c3 100644 --- a/src/hotspot/cpu/s390/macroAssembler_s390.hpp +++ b/src/hotspot/cpu/s390/macroAssembler_s390.hpp @@ -708,6 +708,31 @@ class MacroAssembler: public Assembler { Label* L_success, Label* L_failure); + void repne_scan(Register r_addr, Register r_value, Register r_count, Register r_scratch); + + void lookup_secondary_supers_table(Register r_sub_klass, + Register r_super_klass, + Register r_temp1, + Register r_temp2, + Register r_temp3, + Register r_temp4, + Register r_result, + u1 super_klass_slot); + + void lookup_secondary_supers_table_slow_path(Register r_super_klass, + Register r_array_base, + Register r_array_index, + Register r_bitmap, + Register r_result, + Register r_temp1); + + void verify_secondary_supers_table(Register r_sub_klass, + Register r_super_klass, + Register r_result /* expected */, + Register r_temp1, + Register r_temp2, + Register r_temp3); + // Simplified, combined version, good for typical uses. // Falls through on failure. void check_klass_subtype(Register sub_klass, diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad index 0fe3840b9b5..e3dcfdb8759 100644 --- a/src/hotspot/cpu/s390/s390.ad +++ b/src/hotspot/cpu/s390/s390.ad @@ -356,6 +356,8 @@ reg_class z_rarg3_ptr_reg(Z_R4_H,Z_R4); reg_class z_rarg4_ptr_reg(Z_R5_H,Z_R5); reg_class z_rarg5_ptr_reg(Z_R6_H,Z_R6); reg_class z_thread_ptr_reg(Z_R8_H,Z_R8); +reg_class z_r10_ptr_reg(Z_R10_H, Z_R10); +reg_class z_r11_ptr_reg(Z_R11_H, Z_R11); reg_class z_ptr_reg( /*Z_R0_H,Z_R0*/ // R0 @@ -2985,6 +2987,8 @@ operand iRegP() %{ match(rarg5RegP); match(revenRegP); match(roddRegP); + match(r10TempRegP); + match(r11TempRegP); format %{ %} interface(REG_INTER); %} @@ -2997,6 +3001,20 @@ operand threadRegP() %{ interface(REG_INTER); %} +operand r10TempRegP() %{ + constraint(ALLOC_IN_RC(z_r10_ptr_reg)); + match(iRegP); + format %{ %} + interface(REG_INTER); +%} + +operand r11TempRegP() %{ + constraint(ALLOC_IN_RC(z_r11_ptr_reg)); + match(iRegP); + format %{ %} + interface(REG_INTER); +%} + operand noArg_iRegP() %{ constraint(ALLOC_IN_RC(z_no_arg_ptr_reg)); match(iRegP); @@ -9560,6 +9578,32 @@ instruct partialSubtypeCheck(rarg1RegP index, rarg2RegP sub, rarg3RegP super, fl ins_pipe(pipe_class_dummy); %} +instruct partialSubtypeCheckConstSuper(rarg2RegP sub, rarg1RegP super, immP super_con, + r11TempRegP result, rarg5RegP temp1, rarg4RegP temp2, + rarg3RegP temp3, r10TempRegP temp4, flagsReg pcc) %{ + match(Set result (PartialSubtypeCheck sub (Binary super super_con))); + predicate(UseSecondarySupersTable); + effect(KILL pcc, TEMP temp1, TEMP temp2, TEMP temp3, TEMP temp4); + ins_cost(7 * DEFAULT_COST); // needs to be less than competing nodes + format %{ "partialSubtypeCheck $result, $sub, $super, $super_con" %} + + ins_encode %{ + u1 super_klass_slot = ((Klass*)$super_con$$constant)->hash_slot(); + if (InlineSecondarySupersTest) { + __ lookup_secondary_supers_table($sub$$Register, $super$$Register, + $temp1$$Register, $temp2$$Register, $temp3$$Register, + $temp4$$Register, $result$$Register, super_klass_slot); + } else { + AddressLiteral stub_address(StubRoutines::lookup_secondary_supers_table_stub(super_klass_slot)); + __ load_const_optimized(Z_ARG4, stub_address); + __ z_basr(Z_R14, Z_ARG4); + } + + %} + + ins_pipe(pipe_class_dummy); +%} + instruct partialSubtypeCheck_vs_zero(flagsReg pcc, rarg2RegP sub, rarg3RegP super, immP0 zero, rarg1RegP index, rarg4RegP scratch1, rarg5RegP scratch2) %{ match(Set pcc (CmpI (PartialSubtypeCheck sub super) zero)); diff --git a/src/hotspot/cpu/s390/smallRegisterMap_s390.inline.hpp b/src/hotspot/cpu/s390/smallRegisterMap_s390.inline.hpp index 8c74eb7dd6d..625d17cf992 100644 --- a/src/hotspot/cpu/s390/smallRegisterMap_s390.inline.hpp +++ b/src/hotspot/cpu/s390/smallRegisterMap_s390.inline.hpp @@ -30,8 +30,15 @@ // Java frames don't have callee saved registers (except for rfp), so we can use a smaller RegisterMap class SmallRegisterMap { + constexpr SmallRegisterMap() = default; + ~SmallRegisterMap() = default; + NONCOPYABLE(SmallRegisterMap); + public: - static constexpr SmallRegisterMap* instance = nullptr; + static const SmallRegisterMap* instance() { + static constexpr SmallRegisterMap the_instance{}; + return &the_instance; + } private: static void assert_is_rfp(VMReg r) NOT_DEBUG_RETURN DEBUG_ONLY({ Unimplemented(); }) @@ -46,12 +53,6 @@ class SmallRegisterMap { return map; } - SmallRegisterMap() {} - - SmallRegisterMap(const RegisterMap* map) { - Unimplemented(); - } - inline address location(VMReg reg, intptr_t* sp) const { Unimplemented(); return nullptr; diff --git a/src/hotspot/cpu/s390/stubGenerator_s390.cpp b/src/hotspot/cpu/s390/stubGenerator_s390.cpp index dfd20730b84..556c0d4703d 100644 --- a/src/hotspot/cpu/s390/stubGenerator_s390.cpp +++ b/src/hotspot/cpu/s390/stubGenerator_s390.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2016, 2023 SAP SE. All rights reserved. + * Copyright (c) 2016, 2024 SAP SE. 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 @@ -705,6 +705,50 @@ class StubGenerator: public StubCodeGenerator { return start; } + address generate_lookup_secondary_supers_table_stub(u1 super_klass_index) { + StubCodeMark mark(this, "StubRoutines", "lookup_secondary_supers_table"); + + const Register + r_super_klass = Z_ARG1, + r_sub_klass = Z_ARG2, + r_array_index = Z_ARG3, + r_array_length = Z_ARG4, + r_array_base = Z_ARG5, + r_bitmap = Z_R10, + r_result = Z_R11; + address start = __ pc(); + + __ lookup_secondary_supers_table(r_sub_klass, r_super_klass, + r_array_base, r_array_length, r_array_index, + r_bitmap, r_result, super_klass_index); + + __ z_br(Z_R14); + + return start; + } + + // Slow path implementation for UseSecondarySupersTable. + address generate_lookup_secondary_supers_table_slow_path_stub() { + StubCodeMark mark(this, "StubRoutines", "lookup_secondary_supers_table_slow_path"); + + address start = __ pc(); + + const Register + r_super_klass = Z_ARG1, + r_array_base = Z_ARG5, + r_temp1 = Z_ARG4, + r_array_index = Z_ARG3, + r_bitmap = Z_R10, + r_result = Z_R11; + + __ lookup_secondary_supers_table_slow_path(r_super_klass, r_array_base, + r_array_index, r_bitmap, r_result, r_temp1); + + __ z_br(Z_R14); + + return start; + } + #if !defined(PRODUCT) // Wrapper which calls oopDesc::is_oop_or_null() // Only called by MacroAssembler::verify_oop @@ -3247,6 +3291,14 @@ class StubGenerator: public StubCodeGenerator { StubRoutines::_montgomerySquare = CAST_FROM_FN_PTR(address, SharedRuntime::montgomery_square); } + if (UseSecondarySupersTable) { + StubRoutines::_lookup_secondary_supers_table_slow_path_stub = generate_lookup_secondary_supers_table_slow_path_stub(); + if (!InlineSecondarySupersTest) { + for (int slot = 0; slot < Klass::SECONDARY_SUPERS_TABLE_SIZE; slot++) { + StubRoutines::_lookup_secondary_supers_table_stubs[slot] = generate_lookup_secondary_supers_table_stub(slot); + } + } + } #endif #endif // COMPILER2_OR_JVMCI } diff --git a/src/hotspot/cpu/s390/vm_version_s390.cpp b/src/hotspot/cpu/s390/vm_version_s390.cpp index af0903884fb..4b17ff4594c 100644 --- a/src/hotspot/cpu/s390/vm_version_s390.cpp +++ b/src/hotspot/cpu/s390/vm_version_s390.cpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2016, 2023 SAP SE. All rights reserved. + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024 SAP SE. 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 @@ -272,6 +272,13 @@ void VM_Version::initialize() { FLAG_SET_DEFAULT(UseSHA, false); } + if (UseSecondarySupersTable && VM_Version::get_model_index() < 5 /* z196/z11 */) { + if (!FLAG_IS_DEFAULT(UseSecondarySupersTable)) { + warning("UseSecondarySupersTable requires z196 or later."); + } + FLAG_SET_DEFAULT(UseSecondarySupersTable, false); + } + #ifdef COMPILER2 if (FLAG_IS_DEFAULT(UseMultiplyToLenIntrinsic)) { FLAG_SET_DEFAULT(UseMultiplyToLenIntrinsic, true); diff --git a/src/hotspot/cpu/s390/vm_version_s390.hpp b/src/hotspot/cpu/s390/vm_version_s390.hpp index 4f963c4e485..31fdedc59f6 100644 --- a/src/hotspot/cpu/s390/vm_version_s390.hpp +++ b/src/hotspot/cpu/s390/vm_version_s390.hpp @@ -413,6 +413,8 @@ class VM_Version: public Abstract_VM_Version { // s390 supports fast class initialization checks static bool supports_fast_class_init_checks() { return true; } + constexpr static bool supports_secondary_supers_table() { return true; } + constexpr static bool supports_recursive_lightweight_locking() { return true; } // CPU feature query functions diff --git a/src/hotspot/cpu/x86/gc/shenandoah/c1/shenandoahBarrierSetC1_x86.cpp b/src/hotspot/cpu/x86/gc/shenandoah/c1/shenandoahBarrierSetC1_x86.cpp index 9995a87f5cf..032b33f40e0 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/c1/shenandoahBarrierSetC1_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/c1/shenandoahBarrierSetC1_x86.cpp @@ -46,9 +46,6 @@ void LIR_OpShenandoahCompareAndSwap::emit_code(LIR_Assembler* masm) { assert(cmpval != addr, "cmp and addr must be in different registers"); assert(newval != addr, "new value and addr must be in different registers"); - // Apply IU barrier to newval. - ShenandoahBarrierSet::assembler()->iu_barrier(masm->masm(), newval, tmp1); - #ifdef _LP64 if (UseCompressedOops) { __ encode_heap_oop(cmpval); @@ -101,10 +98,6 @@ LIR_Opr ShenandoahBarrierSetC1::atomic_xchg_at_resolved(LIRAccess& access, LIRIt value.load_item(); LIR_Opr value_opr = value.result(); - if (access.is_oop()) { - value_opr = iu_barrier(access.gen(), value_opr, access.access_emit_info(), access.decorators()); - } - // Because we want a 2-arg form of xchg and xadd __ move(value_opr, result); diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp index 25a2b931468..47078dff907 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp @@ -121,7 +121,7 @@ void ShenandoahBarrierSetAssembler::arraycopy_prologue(MacroAssembler* masm, Dec if (is_reference_type(type)) { - if ((ShenandoahSATBBarrier && !dest_uninitialized) || ShenandoahIUBarrier || ShenandoahLoadRefBarrier) { + if ((ShenandoahSATBBarrier && !dest_uninitialized) || ShenandoahLoadRefBarrier) { #ifdef _LP64 Register thread = r15_thread; #else @@ -472,40 +472,6 @@ void ShenandoahBarrierSetAssembler::load_reference_barrier(MacroAssembler* masm, #endif } -void ShenandoahBarrierSetAssembler::iu_barrier(MacroAssembler* masm, Register dst, Register tmp) { - if (ShenandoahIUBarrier) { - iu_barrier_impl(masm, dst, tmp); - } -} - -void ShenandoahBarrierSetAssembler::iu_barrier_impl(MacroAssembler* masm, Register dst, Register tmp) { - assert(ShenandoahIUBarrier, "should be enabled"); - - if (dst == noreg) return; - - if (ShenandoahIUBarrier) { - save_machine_state(masm, /* handle_gpr = */ true, /* handle_fp = */ true); - -#ifdef _LP64 - Register thread = r15_thread; -#else - Register thread = rcx; - if (thread == dst || thread == tmp) { - thread = rdi; - } - if (thread == dst || thread == tmp) { - thread = rbx; - } - __ get_thread(thread); -#endif - assert_different_registers(dst, tmp, thread); - - satb_write_barrier_pre(masm, noreg, dst, thread, tmp, true, false); - - restore_machine_state(masm, /* handle_gpr = */ true, /* handle_fp = */ true); - } -} - // // Arguments: // @@ -626,12 +592,7 @@ void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet val != noreg /* tosca_live */, false /* expand_call */); } - if (val == noreg) { - BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp1, 0), val, noreg, noreg, noreg); - } else { - iu_barrier(masm, val, tmp3); - BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp1, 0), val, noreg, noreg, noreg); - } + BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp1, 0), val, noreg, noreg, noreg); NOT_LP64(imasm->restore_bcp()); } else { BarrierSetAssembler::store_at(masm, decorators, type, dst, val, tmp1, tmp2, tmp3); diff --git a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp index 47dfe144928..c8140f4a76a 100644 --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp @@ -56,10 +56,7 @@ class ShenandoahBarrierSetAssembler: public BarrierSetAssembler { bool tosca_live, bool expand_call); - void iu_barrier_impl(MacroAssembler* masm, Register dst, Register tmp); - public: - void iu_barrier(MacroAssembler* masm, Register dst, Register tmp); #ifdef COMPILER1 void gen_pre_barrier_stub(LIR_Assembler* ce, ShenandoahPreBarrierStub* stub); void gen_load_reference_barrier_stub(LIR_Assembler* ce, ShenandoahLoadReferenceBarrierStub* stub); diff --git a/src/hotspot/cpu/x86/smallRegisterMap_x86.inline.hpp b/src/hotspot/cpu/x86/smallRegisterMap_x86.inline.hpp index 5f21939a3aa..63212a686af 100644 --- a/src/hotspot/cpu/x86/smallRegisterMap_x86.inline.hpp +++ b/src/hotspot/cpu/x86/smallRegisterMap_x86.inline.hpp @@ -30,8 +30,16 @@ // Java frames don't have callee saved registers (except for rbp), so we can use a smaller RegisterMap class SmallRegisterMap { + constexpr SmallRegisterMap() = default; + ~SmallRegisterMap() = default; + NONCOPYABLE(SmallRegisterMap); + public: - static constexpr SmallRegisterMap* instance = nullptr; + static const SmallRegisterMap* instance() { + static constexpr SmallRegisterMap the_instance{}; + return &the_instance; + } + private: static void assert_is_rbp(VMReg r) NOT_DEBUG_RETURN DEBUG_ONLY({ assert(r == rbp->as_VMReg() || r == rbp->as_VMReg()->next(), "Reg: %s", r->name()); }) @@ -48,17 +56,6 @@ class SmallRegisterMap { return map; } - SmallRegisterMap() {} - - SmallRegisterMap(const RegisterMap* map) { - #ifdef ASSERT - for(int i = 0; i < RegisterMap::reg_count; i++) { - VMReg r = VMRegImpl::as_VMReg(i); - if (map->location(r, (intptr_t*)nullptr) != nullptr) assert_is_rbp(r); - } - #endif - } - inline address location(VMReg reg, intptr_t* sp) const { assert_is_rbp(reg); return (address)(sp - frame::sender_sp_offset); diff --git a/src/hotspot/cpu/zero/smallRegisterMap_zero.inline.hpp b/src/hotspot/cpu/zero/smallRegisterMap_zero.inline.hpp index b85ead32fd2..51fb114f595 100644 --- a/src/hotspot/cpu/zero/smallRegisterMap_zero.inline.hpp +++ b/src/hotspot/cpu/zero/smallRegisterMap_zero.inline.hpp @@ -30,8 +30,15 @@ // Java frames don't have callee saved registers (except for rfp), so we can use a smaller RegisterMap class SmallRegisterMap { + constexpr SmallRegisterMap() = default; + ~SmallRegisterMap() = default; + NONCOPYABLE(SmallRegisterMap); + public: - static constexpr SmallRegisterMap* instance = nullptr; + static const SmallRegisterMap* instance() { + static constexpr SmallRegisterMap the_instance{}; + return &the_instance; + } private: static void assert_is_rfp(VMReg r) NOT_DEBUG_RETURN DEBUG_ONLY({ Unimplemented(); }) @@ -46,12 +53,6 @@ class SmallRegisterMap { return map; } - SmallRegisterMap() {} - - SmallRegisterMap(const RegisterMap* map) { - Unimplemented(); - } - inline address location(VMReg reg, intptr_t* sp) const { Unimplemented(); return nullptr; diff --git a/src/hotspot/share/c1/c1_LIRGenerator.cpp b/src/hotspot/share/c1/c1_LIRGenerator.cpp index cd084135658..7b519804bfe 100644 --- a/src/hotspot/share/c1/c1_LIRGenerator.cpp +++ b/src/hotspot/share/c1/c1_LIRGenerator.cpp @@ -1300,7 +1300,7 @@ void LIRGenerator::do_isPrimitive(Intrinsic* x) { } __ move(new LIR_Address(rcvr.result(), java_lang_Class::klass_offset(), T_ADDRESS), temp, info); - __ cmp(lir_cond_notEqual, temp, LIR_OprFact::metadataConst(0)); + __ cmp(lir_cond_notEqual, temp, LIR_OprFact::metadataConst(nullptr)); __ cmove(lir_cond_notEqual, LIR_OprFact::intConst(0), LIR_OprFact::intConst(1), result, T_BOOLEAN); } @@ -1333,7 +1333,7 @@ void LIRGenerator::do_getModifiers(Intrinsic* x) { // Check if this is a Java mirror of primitive type, and select the appropriate klass. LIR_Opr klass = new_register(T_METADATA); - __ cmp(lir_cond_equal, recv_klass, LIR_OprFact::metadataConst(0)); + __ cmp(lir_cond_equal, recv_klass, LIR_OprFact::metadataConst(nullptr)); __ cmove(lir_cond_equal, prim_klass, recv_klass, klass, T_ADDRESS); // Get the answer. diff --git a/src/hotspot/share/ci/ciConstantPoolCache.cpp b/src/hotspot/share/ci/ciConstantPoolCache.cpp index 8dc9cfc75e1..e831516b0f7 100644 --- a/src/hotspot/share/ci/ciConstantPoolCache.cpp +++ b/src/hotspot/share/ci/ciConstantPoolCache.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, 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 @@ -37,7 +37,7 @@ ciConstantPoolCache::ciConstantPoolCache(Arena* arena, int expected_size) { _elements = - new (arena) GrowableArray(arena, expected_size, 0, 0); + new (arena) GrowableArray(arena, expected_size, 0, nullptr); _keys = new (arena) GrowableArray(arena, expected_size, 0, 0); } diff --git a/src/hotspot/share/ci/ciReplay.cpp b/src/hotspot/share/ci/ciReplay.cpp index 3ed71806b07..fd29e0cf857 100644 --- a/src/hotspot/share/ci/ciReplay.cpp +++ b/src/hotspot/share/ci/ciReplay.cpp @@ -132,6 +132,7 @@ class CompileReplay : public StackObj { char* _bufptr; char* _buffer; int _buffer_length; + ReallocMark _nesting; // Safety checks for arena reallocation // "compile" data ciKlass* _iklass; @@ -601,6 +602,7 @@ class CompileReplay : public StackObj { int buffer_pos = 0; while(c != EOF) { if (buffer_pos + 1 >= _buffer_length) { + _nesting.check(); // Check if a reallocation in the resource arena is safe int new_length = _buffer_length * 2; // Next call will throw error in case of OOM. _buffer = REALLOC_RESOURCE_ARRAY(char, _buffer, _buffer_length, new_length); diff --git a/src/hotspot/share/ci/ciStreams.cpp b/src/hotspot/share/ci/ciStreams.cpp index 8220910d74c..18d2a46a686 100644 --- a/src/hotspot/share/ci/ciStreams.cpp +++ b/src/hotspot/share/ci/ciStreams.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2024, 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 @@ -135,7 +135,7 @@ Bytecodes::Code ciBytecodeStream::next_wide_or_table(Bytecodes::Code bc) { // ------------------------------------------------------------------ // ciBytecodeStream::reset_to_bci void ciBytecodeStream::reset_to_bci( int bci ) { - _bc_start=_was_wide=0; + _bc_start = _was_wide = nullptr; _pc = _start+bci; } diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp index 535b67887fc..ddde58e5d20 100644 --- a/src/hotspot/share/classfile/classFileParser.cpp +++ b/src/hotspot/share/classfile/classFileParser.cpp @@ -1227,12 +1227,8 @@ void ClassFileParser::parse_field_attributes(const ClassFileStream* const cfs, bool is_synthetic = false; const u1* runtime_visible_annotations = nullptr; int runtime_visible_annotations_length = 0; - const u1* runtime_invisible_annotations = nullptr; - int runtime_invisible_annotations_length = 0; const u1* runtime_visible_type_annotations = nullptr; int runtime_visible_type_annotations_length = 0; - const u1* runtime_invisible_type_annotations = nullptr; - int runtime_invisible_type_annotations_length = 0; bool runtime_invisible_annotations_exists = false; bool runtime_invisible_type_annotations_exists = false; const ConstantPool* const cp = _cp; @@ -1315,11 +1311,6 @@ void ClassFileParser::parse_field_attributes(const ClassFileStream* const cfs, return; } runtime_invisible_annotations_exists = true; - if (PreserveAllAnnotations) { - runtime_invisible_annotations_length = attribute_length; - runtime_invisible_annotations = cfs->current(); - assert(runtime_invisible_annotations != nullptr, "null invisible annotations"); - } cfs->skip_u1(attribute_length, CHECK); } else if (attribute_name == vmSymbols::tag_runtime_visible_type_annotations()) { if (runtime_visible_type_annotations != nullptr) { @@ -1339,11 +1330,6 @@ void ClassFileParser::parse_field_attributes(const ClassFileStream* const cfs, } else { runtime_invisible_type_annotations_exists = true; } - if (PreserveAllAnnotations) { - runtime_invisible_type_annotations_length = attribute_length; - runtime_invisible_type_annotations = cfs->current(); - assert(runtime_invisible_type_annotations != nullptr, "null invisible type annotations"); - } cfs->skip_u1(attribute_length, CHECK); } else { cfs->skip_u1(attribute_length, CHECK); // Skip unknown attributes @@ -1356,16 +1342,12 @@ void ClassFileParser::parse_field_attributes(const ClassFileStream* const cfs, *constantvalue_index_addr = constantvalue_index; *is_synthetic_addr = is_synthetic; *generic_signature_index_addr = generic_signature_index; - AnnotationArray* a = assemble_annotations(runtime_visible_annotations, + AnnotationArray* a = allocate_annotations(runtime_visible_annotations, runtime_visible_annotations_length, - runtime_invisible_annotations, - runtime_invisible_annotations_length, CHECK); parsed_annotations->set_field_annotations(a); - a = assemble_annotations(runtime_visible_type_annotations, + a = allocate_annotations(runtime_visible_type_annotations, runtime_visible_type_annotations_length, - runtime_invisible_type_annotations, - runtime_invisible_type_annotations_length, CHECK); parsed_annotations->set_field_type_annotations(a); return; @@ -2164,57 +2146,40 @@ void ClassFileParser::copy_localvariable_table(const ConstMethod* cm, void ClassFileParser::copy_method_annotations(ConstMethod* cm, const u1* runtime_visible_annotations, int runtime_visible_annotations_length, - const u1* runtime_invisible_annotations, - int runtime_invisible_annotations_length, const u1* runtime_visible_parameter_annotations, int runtime_visible_parameter_annotations_length, - const u1* runtime_invisible_parameter_annotations, - int runtime_invisible_parameter_annotations_length, const u1* runtime_visible_type_annotations, int runtime_visible_type_annotations_length, - const u1* runtime_invisible_type_annotations, - int runtime_invisible_type_annotations_length, const u1* annotation_default, int annotation_default_length, TRAPS) { AnnotationArray* a; - if (runtime_visible_annotations_length + - runtime_invisible_annotations_length > 0) { - a = assemble_annotations(runtime_visible_annotations, + if (runtime_visible_annotations_length > 0) { + a = allocate_annotations(runtime_visible_annotations, runtime_visible_annotations_length, - runtime_invisible_annotations, - runtime_invisible_annotations_length, CHECK); cm->set_method_annotations(a); } - if (runtime_visible_parameter_annotations_length + - runtime_invisible_parameter_annotations_length > 0) { - a = assemble_annotations(runtime_visible_parameter_annotations, + if (runtime_visible_parameter_annotations_length > 0) { + a = allocate_annotations(runtime_visible_parameter_annotations, runtime_visible_parameter_annotations_length, - runtime_invisible_parameter_annotations, - runtime_invisible_parameter_annotations_length, CHECK); cm->set_parameter_annotations(a); } if (annotation_default_length > 0) { - a = assemble_annotations(annotation_default, + a = allocate_annotations(annotation_default, annotation_default_length, - nullptr, - 0, CHECK); cm->set_default_annotations(a); } - if (runtime_visible_type_annotations_length + - runtime_invisible_type_annotations_length > 0) { - a = assemble_annotations(runtime_visible_type_annotations, + if (runtime_visible_type_annotations_length > 0) { + a = allocate_annotations(runtime_visible_type_annotations, runtime_visible_type_annotations_length, - runtime_invisible_type_annotations, - runtime_invisible_type_annotations_length, CHECK); cm->set_type_annotations(a); } @@ -2296,7 +2261,7 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, u2 max_stack = 0; u2 max_locals = 0; u4 code_length = 0; - const u1* code_start = 0; + const u1* code_start = nullptr; u2 exception_table_length = 0; const unsafe_u2* exception_table_start = nullptr; // (potentially unaligned) pointer to array of u2 elements Array* exception_handlers = Universe::the_empty_int_array(); @@ -2327,16 +2292,10 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, MethodAnnotationCollector parsed_annotations; const u1* runtime_visible_annotations = nullptr; int runtime_visible_annotations_length = 0; - const u1* runtime_invisible_annotations = nullptr; - int runtime_invisible_annotations_length = 0; const u1* runtime_visible_parameter_annotations = nullptr; int runtime_visible_parameter_annotations_length = 0; - const u1* runtime_invisible_parameter_annotations = nullptr; - int runtime_invisible_parameter_annotations_length = 0; const u1* runtime_visible_type_annotations = nullptr; int runtime_visible_type_annotations_length = 0; - const u1* runtime_invisible_type_annotations = nullptr; - int runtime_invisible_type_annotations_length = 0; bool runtime_invisible_annotations_exists = false; bool runtime_invisible_type_annotations_exists = false; bool runtime_invisible_parameter_annotations_exists = false; @@ -2607,11 +2566,6 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, return nullptr; } runtime_invisible_annotations_exists = true; - if (PreserveAllAnnotations) { - runtime_invisible_annotations_length = method_attribute_length; - runtime_invisible_annotations = cfs->current(); - assert(runtime_invisible_annotations != nullptr, "null invisible annotations"); - } cfs->skip_u1(method_attribute_length, CHECK_NULL); } else if (method_attribute_name == vmSymbols::tag_runtime_visible_parameter_annotations()) { if (runtime_visible_parameter_annotations != nullptr) { @@ -2632,12 +2586,6 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, return nullptr; } runtime_invisible_parameter_annotations_exists = true; - if (PreserveAllAnnotations) { - runtime_invisible_parameter_annotations_length = method_attribute_length; - runtime_invisible_parameter_annotations = cfs->current(); - assert(runtime_invisible_parameter_annotations != nullptr, - "null invisible parameter annotations"); - } cfs->skip_u1(method_attribute_length, CHECK_NULL); } else if (method_attribute_name == vmSymbols::tag_annotation_default()) { if (annotation_default != nullptr) { @@ -2668,14 +2616,8 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, "Multiple RuntimeInvisibleTypeAnnotations attributes for method in class file %s", THREAD); return nullptr; - } else { - runtime_invisible_type_annotations_exists = true; - } - if (PreserveAllAnnotations) { - runtime_invisible_type_annotations_length = method_attribute_length; - runtime_invisible_type_annotations = cfs->current(); - assert(runtime_invisible_type_annotations != nullptr, "null invisible type annotations"); } + runtime_invisible_type_annotations_exists = true; cfs->skip_u1(method_attribute_length, CHECK_NULL); } else { // Skip unknown attributes @@ -2709,12 +2651,9 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, checked_exceptions_length, method_parameters_length, generic_signature_index, - runtime_visible_annotations_length + - runtime_invisible_annotations_length, - runtime_visible_parameter_annotations_length + - runtime_invisible_parameter_annotations_length, - runtime_visible_type_annotations_length + - runtime_invisible_type_annotations_length, + runtime_visible_annotations_length, + runtime_visible_parameter_annotations_length, + runtime_visible_type_annotations_length, annotation_default_length, 0); @@ -2806,16 +2745,10 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, copy_method_annotations(m->constMethod(), runtime_visible_annotations, runtime_visible_annotations_length, - runtime_invisible_annotations, - runtime_invisible_annotations_length, runtime_visible_parameter_annotations, runtime_visible_parameter_annotations_length, - runtime_invisible_parameter_annotations, - runtime_invisible_parameter_annotations_length, runtime_visible_type_annotations, runtime_visible_type_annotations_length, - runtime_invisible_type_annotations, - runtime_invisible_type_annotations_length, annotation_default, annotation_default_length, CHECK_NULL); @@ -3295,13 +3228,9 @@ u4 ClassFileParser::parse_classfile_record_attribute(const ClassFileStream* cons u2 generic_sig_index = 0; const u1* runtime_visible_annotations = nullptr; int runtime_visible_annotations_length = 0; - const u1* runtime_invisible_annotations = nullptr; - int runtime_invisible_annotations_length = 0; bool runtime_invisible_annotations_exists = false; const u1* runtime_visible_type_annotations = nullptr; int runtime_visible_type_annotations_length = 0; - const u1* runtime_invisible_type_annotations = nullptr; - int runtime_invisible_type_annotations_length = 0; bool runtime_invisible_type_annotations_exists = false; // Expected attributes for record components are Signature, Runtime(In)VisibleAnnotations, @@ -3352,11 +3281,6 @@ u4 ClassFileParser::parse_classfile_record_attribute(const ClassFileStream* cons return 0; } runtime_invisible_annotations_exists = true; - if (PreserveAllAnnotations) { - runtime_invisible_annotations_length = attribute_length; - runtime_invisible_annotations = cfs->current(); - assert(runtime_invisible_annotations != nullptr, "null record component invisible annotation"); - } cfs->skip_u1(attribute_length, CHECK_0); } else if (attribute_name == vmSymbols::tag_runtime_visible_type_annotations()) { @@ -3379,11 +3303,6 @@ u4 ClassFileParser::parse_classfile_record_attribute(const ClassFileStream* cons return 0; } runtime_invisible_type_annotations_exists = true; - if (PreserveAllAnnotations) { - runtime_invisible_type_annotations_length = attribute_length; - runtime_invisible_type_annotations = cfs->current(); - assert(runtime_invisible_type_annotations != nullptr, "null record component invisible type annotation"); - } cfs->skip_u1(attribute_length, CHECK_0); } else { @@ -3393,15 +3312,11 @@ u4 ClassFileParser::parse_classfile_record_attribute(const ClassFileStream* cons calculate_attr_size += attribute_length; } // End of attributes For loop - AnnotationArray* annotations = assemble_annotations(runtime_visible_annotations, + AnnotationArray* annotations = allocate_annotations(runtime_visible_annotations, runtime_visible_annotations_length, - runtime_invisible_annotations, - runtime_invisible_annotations_length, CHECK_0); - AnnotationArray* type_annotations = assemble_annotations(runtime_visible_type_annotations, + AnnotationArray* type_annotations = allocate_annotations(runtime_visible_type_annotations, runtime_visible_type_annotations_length, - runtime_invisible_type_annotations, - runtime_invisible_type_annotations_length, CHECK_0); RecordComponent* record_component = @@ -3538,12 +3453,8 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf bool parsed_bootstrap_methods_attribute = false; const u1* runtime_visible_annotations = nullptr; int runtime_visible_annotations_length = 0; - const u1* runtime_invisible_annotations = nullptr; - int runtime_invisible_annotations_length = 0; const u1* runtime_visible_type_annotations = nullptr; int runtime_visible_type_annotations_length = 0; - const u1* runtime_invisible_type_annotations = nullptr; - int runtime_invisible_type_annotations_length = 0; bool runtime_invisible_type_annotations_exists = false; bool runtime_invisible_annotations_exists = false; bool parsed_source_debug_ext_annotations_exist = false; @@ -3656,11 +3567,6 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf return; } runtime_invisible_annotations_exists = true; - if (PreserveAllAnnotations) { - runtime_invisible_annotations_length = attribute_length; - runtime_invisible_annotations = cfs->current(); - assert(runtime_invisible_annotations != nullptr, "null invisible annotations"); - } cfs->skip_u1(attribute_length, CHECK); } else if (tag == vmSymbols::tag_enclosing_method()) { if (parsed_enclosingmethod_attribute) { @@ -3712,14 +3618,8 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf classfile_parse_error( "Multiple RuntimeInvisibleTypeAnnotations attributes in class file %s", THREAD); return; - } else { - runtime_invisible_type_annotations_exists = true; - } - if (PreserveAllAnnotations) { - runtime_invisible_type_annotations_length = attribute_length; - runtime_invisible_type_annotations = cfs->current(); - assert(runtime_invisible_type_annotations != nullptr, "null invisible type annotations"); } + runtime_invisible_type_annotations_exists = true; cfs->skip_u1(attribute_length, CHECK); } else if (_major_version >= JAVA_11_VERSION) { if (tag == vmSymbols::tag_nest_members()) { @@ -3799,15 +3699,11 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf cfs->skip_u1(attribute_length, CHECK); } } - _class_annotations = assemble_annotations(runtime_visible_annotations, + _class_annotations = allocate_annotations(runtime_visible_annotations, runtime_visible_annotations_length, - runtime_invisible_annotations, - runtime_invisible_annotations_length, CHECK); - _class_type_annotations = assemble_annotations(runtime_visible_type_annotations, + _class_type_annotations = allocate_annotations(runtime_visible_type_annotations, runtime_visible_type_annotations_length, - runtime_invisible_type_annotations, - runtime_invisible_type_annotations_length, CHECK); if (parsed_innerclasses_attribute || parsed_enclosingmethod_attribute) { @@ -3943,28 +3839,16 @@ void ClassFileParser::apply_parsed_class_metadata( clear_class_metadata(); } -AnnotationArray* ClassFileParser::assemble_annotations(const u1* const runtime_visible_annotations, - int runtime_visible_annotations_length, - const u1* const runtime_invisible_annotations, - int runtime_invisible_annotations_length, +AnnotationArray* ClassFileParser::allocate_annotations(const u1* const anno, + int anno_length, TRAPS) { AnnotationArray* annotations = nullptr; - if (runtime_visible_annotations != nullptr || - runtime_invisible_annotations != nullptr) { + if (anno != nullptr) { annotations = MetadataFactory::new_array(_loader_data, - runtime_visible_annotations_length + - runtime_invisible_annotations_length, - CHECK_(annotations)); - if (runtime_visible_annotations != nullptr) { - for (int i = 0; i < runtime_visible_annotations_length; i++) { - annotations->at_put(i, runtime_visible_annotations[i]); - } - } - if (runtime_invisible_annotations != nullptr) { - for (int i = 0; i < runtime_invisible_annotations_length; i++) { - int append = runtime_visible_annotations_length+i; - annotations->at_put(append, runtime_invisible_annotations[i]); - } + anno_length, + CHECK_(annotations)); + for (int i = 0; i < anno_length; i++) { + annotations->at_put(i, anno[i]); } } return annotations; diff --git a/src/hotspot/share/classfile/classFileParser.hpp b/src/hotspot/share/classfile/classFileParser.hpp index f43303722a8..69d1f357ca5 100644 --- a/src/hotspot/share/classfile/classFileParser.hpp +++ b/src/hotspot/share/classfile/classFileParser.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, 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 @@ -347,10 +347,8 @@ class ClassFileParser { TRAPS); // Annotations handling - AnnotationArray* assemble_annotations(const u1* const runtime_visible_annotations, - int runtime_visible_annotations_length, - const u1* const runtime_invisible_annotations, - int runtime_invisible_annotations_length, + AnnotationArray* allocate_annotations(const u1* const anno, + int anno_length, TRAPS); void set_precomputed_flags(InstanceKlass* k); @@ -514,16 +512,10 @@ class ClassFileParser { void copy_method_annotations(ConstMethod* cm, const u1* runtime_visible_annotations, int runtime_visible_annotations_length, - const u1* runtime_invisible_annotations, - int runtime_invisible_annotations_length, const u1* runtime_visible_parameter_annotations, int runtime_visible_parameter_annotations_length, - const u1* runtime_invisible_parameter_annotations, - int runtime_invisible_parameter_annotations_length, const u1* runtime_visible_type_annotations, int runtime_visible_type_annotations_length, - const u1* runtime_invisible_type_annotations, - int runtime_invisible_type_annotations_length, const u1* annotation_default, int annotation_default_length, TRAPS); diff --git a/src/hotspot/share/classfile/defaultMethods.cpp b/src/hotspot/share/classfile/defaultMethods.cpp index dd221efb409..58d24c16ad3 100644 --- a/src/hotspot/share/classfile/defaultMethods.cpp +++ b/src/hotspot/share/classfile/defaultMethods.cpp @@ -870,7 +870,7 @@ static Method* new_method( Symbol* sig, AccessFlags flags, int max_stack, int params, ConstMethod::MethodType mt, TRAPS) { - address code_start = 0; + address code_start = nullptr; int code_length = 0; InlineTableSizes sizes; diff --git a/src/hotspot/share/classfile/javaAssertions.cpp b/src/hotspot/share/classfile/javaAssertions.cpp index 2666683046b..cf51bfb21d6 100644 --- a/src/hotspot/share/classfile/javaAssertions.cpp +++ b/src/hotspot/share/classfile/javaAssertions.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2024, 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 @@ -38,12 +38,12 @@ bool JavaAssertions::_userDefault = false; bool JavaAssertions::_sysDefault = false; -JavaAssertions::OptionList* JavaAssertions::_classes = 0; -JavaAssertions::OptionList* JavaAssertions::_packages = 0; +JavaAssertions::OptionList* JavaAssertions::_classes = nullptr; +JavaAssertions::OptionList* JavaAssertions::_packages = nullptr; JavaAssertions::OptionList::OptionList(const char* name, bool enabled, OptionList* next) { - assert(name != 0, "need a name"); + assert(name != nullptr, "need a name"); _name = name; _enabled = enabled; _next = next; @@ -51,12 +51,12 @@ JavaAssertions::OptionList::OptionList(const char* name, bool enabled, int JavaAssertions::OptionList::count(OptionList* p) { int rc; - for (rc = 0; p != 0; p = p->next(), ++rc) /* empty */; + for (rc = 0; p != nullptr; p = p->next(), ++rc) /* empty */; return rc; } void JavaAssertions::addOption(const char* name, bool enable) { - assert(name != 0, "must have a name"); + assert(name != nullptr, "must have a name"); // Copy the name. The storage needs to exist for the lifetime of the vm; // it is never freed, so will be leaked (along with other option strings - @@ -135,7 +135,7 @@ void JavaAssertions::fillJavaArrays(const OptionList* p, int len, // matches the order on the command line (the list is in reverse order, since // it was created by prepending successive items from the command line). int index; - for (index = len - 1; p != 0; p = p->next(), --index) { + for (index = len - 1; p != nullptr; p = p->next(), --index) { assert(index >= 0, "length does not match list"); TempNewSymbol name = SymbolTable::new_symbol(p->name()); Handle s = java_lang_String::externalize_classname(name, CHECK); @@ -147,12 +147,12 @@ void JavaAssertions::fillJavaArrays(const OptionList* p, int len, inline JavaAssertions::OptionList* JavaAssertions::match_class(const char* classname) { - for (OptionList* p = _classes; p != 0; p = p->next()) { + for (OptionList* p = _classes; p != nullptr; p = p->next()) { if (strcmp(p->name(), classname) == 0) { return p; } } - return 0; + return nullptr; } JavaAssertions::OptionList* @@ -160,7 +160,7 @@ JavaAssertions::match_package(const char* classname) { // Search the package list for any items that apply to classname. Each // sub-package in classname is checked, from most-specific to least, until one // is found. - if (_packages == 0) return 0; + if (_packages == nullptr) return nullptr; // Find the length of the "most-specific" package in classname. If classname // does not include a package, length will be 0 which will match items for the @@ -170,7 +170,7 @@ JavaAssertions::match_package(const char* classname) { do { assert(len == 0 || classname[len] == JVM_SIGNATURE_SLASH, "not a package name"); - for (OptionList* p = _packages; p != 0; p = p->next()) { + for (OptionList* p = _packages; p != nullptr; p = p->next()) { if (strncmp(p->name(), classname, len) == 0 && p->name()[len] == '\0') { return p; } @@ -181,7 +181,7 @@ JavaAssertions::match_package(const char* classname) { while (len > 0 && classname[--len] != JVM_SIGNATURE_SLASH) /* empty */; } while (len > 0); - return 0; + return nullptr; } inline void JavaAssertions::trace(const char* name, @@ -193,7 +193,7 @@ const char* typefound, const char* namefound, bool enabled) { } bool JavaAssertions::enabled(const char* classname, bool systemClass) { - assert(classname != 0, "must have a classname"); + assert(classname != nullptr, "must have a classname"); // This will be slow if the number of assertion options on the command line is // large--it traverses two lists, one of them multiple times. Could use a diff --git a/src/hotspot/share/classfile/symbolTable.cpp b/src/hotspot/share/classfile/symbolTable.cpp index ddbf180a4a1..95094238946 100644 --- a/src/hotspot/share/classfile/symbolTable.cpp +++ b/src/hotspot/share/classfile/symbolTable.cpp @@ -193,7 +193,7 @@ class SymbolTableConfig : public AllStatic { // We cannot use arena because arena chunks are allocated by the OS. As a result, for example, // the archived symbol of "java/lang/Object" may sometimes be lower than "java/lang/String", and // sometimes be higher. This would cause non-deterministic contents in the archive. - DEBUG_ONLY(static void* last = 0); + DEBUG_ONLY(static void* last = nullptr); void* p = (void*)MetaspaceShared::symbol_space_alloc(alloc_size); assert(p > last, "must increase monotonically"); DEBUG_ONLY(last = p); diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp index 4a54fd8d0a9..a491dd62f07 100644 --- a/src/hotspot/share/code/codeCache.cpp +++ b/src/hotspot/share/code/codeCache.cpp @@ -165,8 +165,8 @@ class CodeBlob_sizes { // Iterate over all CodeBlobs (cb) on the given CodeHeap #define FOR_ALL_BLOBS(cb, heap) for (CodeBlob* cb = first_blob(heap); cb != nullptr; cb = next_blob(heap, cb)) -address CodeCache::_low_bound = 0; -address CodeCache::_high_bound = 0; +address CodeCache::_low_bound = nullptr; +address CodeCache::_high_bound = nullptr; volatile int CodeCache::_number_of_nmethods_with_dependencies = 0; ExceptionCache* volatile CodeCache::_exception_cache_purge_list = nullptr; @@ -1790,15 +1790,17 @@ void CodeCache::log_state(outputStream* st) { #ifdef LINUX void CodeCache::write_perf_map(const char* filename, outputStream* st) { MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); - - // Perf expects to find the map file at /tmp/perf-.map - // if the file name is not specified. - char fname[32]; + char fname[JVM_MAXPATHLEN]; if (filename == nullptr) { - jio_snprintf(fname, sizeof(fname), "/tmp/perf-%d.map", os::current_process_id()); + // Invocation outside of jcmd requires pid substitution. + if (!Arguments::copy_expand_pid(DEFAULT_PERFMAP_FILENAME, + strlen(DEFAULT_PERFMAP_FILENAME), + fname, JVM_MAXPATHLEN)) { + st->print_cr("Warning: Not writing perf map as pid substitution failed."); + return; + } filename = fname; } - fileStream fs(filename, "w"); if (!fs.is_open()) { st->print_cr("Warning: Failed to create %s for perf map", filename); diff --git a/src/hotspot/share/code/codeCache.hpp b/src/hotspot/share/code/codeCache.hpp index 565f600453e..b724268b650 100644 --- a/src/hotspot/share/code/codeCache.hpp +++ b/src/hotspot/share/code/codeCache.hpp @@ -80,6 +80,10 @@ class ShenandoahParallelCodeHeapIterator; class NativePostCallNop; class DeoptimizationScope; +#ifdef LINUX +#define DEFAULT_PERFMAP_FILENAME "/tmp/perf-%p.map" +#endif + class CodeCache : AllStatic { friend class VMStructs; friend class JVMCIVMStructs; diff --git a/src/hotspot/share/code/dependencies.cpp b/src/hotspot/share/code/dependencies.cpp index 7d3b744313f..074846e2e79 100644 --- a/src/hotspot/share/code/dependencies.cpp +++ b/src/hotspot/share/code/dependencies.cpp @@ -72,7 +72,7 @@ void Dependencies::initialize(ciEnv* env) { #endif DEBUG_ONLY(_deps[end_marker] = nullptr); for (int i = (int)FIRST_TYPE; i < (int)TYPE_LIMIT; i++) { - _deps[i] = new(arena) GrowableArray(arena, 10, 0, 0); + _deps[i] = new(arena) GrowableArray(arena, 10, 0, nullptr); } _content_bytes = nullptr; _size_in_bytes = (size_t)-1; diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp index 54aeac6be04..0bab731ac56 100644 --- a/src/hotspot/share/code/nmethod.cpp +++ b/src/hotspot/share/code/nmethod.cpp @@ -3270,7 +3270,7 @@ void nmethod::print_recorded_oop(int log_n, int i) { if (value == Universe::non_oop_word()) { tty->print("non-oop word"); } else { - if (value == 0) { + if (value == nullptr) { tty->print("nullptr-oop"); } else { oop_at(i)->print_value_on(tty); diff --git a/src/hotspot/share/code/oopRecorder.cpp b/src/hotspot/share/code/oopRecorder.cpp index b8ecc1eccc0..fd13b105041 100644 --- a/src/hotspot/share/code/oopRecorder.cpp +++ b/src/hotspot/share/code/oopRecorder.cpp @@ -68,11 +68,11 @@ template void ValueRecorder::copy_values_to(nmethod* nm) { template void ValueRecorder::maybe_initialize() { if (_handles == nullptr) { if (_arena != nullptr) { - _handles = new(_arena) GrowableArray(_arena, 10, 0, 0); - _no_finds = new(_arena) GrowableArray( _arena, 10, 0, 0); + _handles = new(_arena) GrowableArray(_arena, 10, 0, T{}); + _no_finds = new(_arena) GrowableArray(_arena, 10, 0, 0); } else { - _handles = new GrowableArray(10, 0, 0); - _no_finds = new GrowableArray( 10, 0, 0); + _handles = new GrowableArray(10, 0, T{}); + _no_finds = new GrowableArray(10, 0, 0); } } } diff --git a/src/hotspot/share/compiler/compilationMemoryStatistic.cpp b/src/hotspot/share/compiler/compilationMemoryStatistic.cpp index 07d8ec41598..4cc2043656e 100644 --- a/src/hotspot/share/compiler/compilationMemoryStatistic.cpp +++ b/src/hotspot/share/compiler/compilationMemoryStatistic.cpp @@ -51,29 +51,36 @@ #include "utilities/quickSort.hpp" #include "utilities/resourceHash.hpp" -ArenaStatCounter::ArenaStatCounter() : - _current(0), _start(0), _peak(0), - _na(0), _ra(0), - _limit(0), _hit_limit(false), _limit_in_process(false), - _na_at_peak(0), _ra_at_peak(0), _live_nodes_at_peak(0) -{} - -size_t ArenaStatCounter::peak_since_start() const { - return _peak > _start ? _peak - _start : 0; +ArenaStatCounter::ArenaStatCounter() { + reset(); +} + +void ArenaStatCounter::reset() { + _current = 0; + _peak = 0; + _current_by_tag.clear(); + _peak_by_tag.clear(); + _limit = 0; + _hit_limit = false; + _limit_in_process = false; + _live_nodes_at_peak = 0; + _active = false; } void ArenaStatCounter::start(size_t limit) { - _peak = _start = _current; + reset(); + _active = true; _limit = limit; - _hit_limit = false; } -void ArenaStatCounter::end(){ +void ArenaStatCounter::end() { _limit = 0; _hit_limit = false; + _active = false; } void ArenaStatCounter::update_c2_node_count() { + assert(_active, "compilaton has not yet started"); #ifdef COMPILER2 CompilerThread* const th = Thread::current()->as_Compiler_thread(); const CompileTask* const task = th->task(); @@ -90,33 +97,27 @@ void ArenaStatCounter::update_c2_node_count() { // Account an arena allocation or de-allocation. bool ArenaStatCounter::account(ssize_t delta, int tag) { + assert(_active, "compilaton has not yet started"); bool rc = false; #ifdef ASSERT // Note: if this fires, we free more arena memory under the scope of the // CompilationMemoryHistoryMark than we allocate. This cannot be since we // assume arena allocations in CompilerThread to be stack bound and symmetric. assert(delta >= 0 || ((ssize_t)_current + delta) >= 0, - "Negative overflow (d=%zd %zu %zu %zu)", delta, _current, _start, _peak); + "Negative overflow (d=%zd %zu %zu)", delta, _current, _peak); #endif // Update totals _current += delta; - // Update detail counter - switch ((Arena::Tag)tag) { - case Arena::Tag::tag_ra: _ra += delta; break; - case Arena::Tag::tag_node: _na += delta; break; - default: // ignore - break; - }; + _current_by_tag.add(tag, delta); // Did we reach a peak? if (_current > _peak) { _peak = _current; - assert(delta > 0, "Sanity (%zu %zu %zu)", _current, _start, _peak); - _na_at_peak = _na; - _ra_at_peak = _ra; + assert(delta > 0, "Sanity (%zu %zu)", _current, _peak); update_c2_node_count(); + _peak_by_tag = _current_by_tag; rc = true; // Did we hit the memory limit? - if (!_hit_limit && _limit > 0 && peak_since_start() > _limit) { + if (!_hit_limit && _limit > 0 && _peak > _limit) { _hit_limit = true; } } @@ -124,9 +125,15 @@ bool ArenaStatCounter::account(ssize_t delta, int tag) { } void ArenaStatCounter::print_on(outputStream* st) const { - st->print("%zu [na %zu ra %zu]", peak_since_start(), _na_at_peak, _ra_at_peak); + st->print("%zu [", _peak); + for (int tag = 0; tag < _peak_by_tag.element_count(); tag++) { + if (_peak_by_tag.counter(tag) > 0) { + st->print("%s %zu ", _peak_by_tag.tag_name(tag), _peak_by_tag.counter(tag)); + } + } + st->print("]"); #ifdef ASSERT - st->print(" (%zu->%zu->%zu)", _start, _peak, _current); + st->print(" (%zu->%zu)", _peak, _current); #endif } @@ -186,10 +193,8 @@ class MemStatEntry : public CHeapObj { // peak usage, bytes, over all arenas size_t _total; - // usage in node arena when total peaked - size_t _na_at_peak; - // usage in resource area when total peaked - size_t _ra_at_peak; + // usage per arena tag when total peaked + ArenaCountersByTag _peak_by_tag; // number of nodes (c2 only) when total peaked unsigned _live_nodes_at_peak; const char* _result; @@ -199,8 +204,9 @@ class MemStatEntry : public CHeapObj { MemStatEntry(FullMethodName method) : _method(method), _comptype(compiler_c1), _time(0), _num_recomp(0), _thread(nullptr), _limit(0), - _total(0), _na_at_peak(0), _ra_at_peak(0), _live_nodes_at_peak(0), + _total(0), _live_nodes_at_peak(0), _result(nullptr) { + _peak_by_tag.clear(); } void set_comptype(CompilerType comptype) { _comptype = comptype; } @@ -210,8 +216,7 @@ class MemStatEntry : public CHeapObj { void inc_recompilation() { _num_recomp++; } void set_total(size_t n) { _total = n; } - void set_na_at_peak(size_t n) { _na_at_peak = n; } - void set_ra_at_peak(size_t n) { _ra_at_peak = n; } + void set_peak_by_tag(ArenaCountersByTag peak_by_tag) { _peak_by_tag = peak_by_tag; } void set_live_nodes_at_peak(unsigned n) { _live_nodes_at_peak = n; } void set_result(const char* s) { _result = s; } @@ -219,21 +224,34 @@ class MemStatEntry : public CHeapObj { size_t total() const { return _total; } static void print_legend(outputStream* st) { +#define LEGEND_KEY_FMT "%11s" st->print_cr("Legend:"); - st->print_cr(" total : memory allocated via arenas while compiling"); - st->print_cr(" NA : ...how much in node arenas (if c2)"); - st->print_cr(" RA : ...how much in resource areas"); - st->print_cr(" result : Result: 'ok' finished successfully, 'oom' hit memory limit, 'err' compilation failed"); - st->print_cr(" #nodes : ...how many nodes (c2 only)"); - st->print_cr(" limit : memory limit, if set"); - st->print_cr(" time : time of last compilation (sec)"); - st->print_cr(" type : compiler type"); - st->print_cr(" #rc : how often recompiled"); - st->print_cr(" thread : compiler thread"); + st->print_cr(" " LEGEND_KEY_FMT ": %s", "total", "memory allocated via arenas while compiling"); + for (int tag = 0; tag < Arena::tag_count(); tag++) { + st->print_cr(" " LEGEND_KEY_FMT ": %s", Arena::tag_name[tag], Arena::tag_desc[tag]); + } + st->print_cr(" " LEGEND_KEY_FMT ": %s", "result", "Result: 'ok' finished successfully, 'oom' hit memory limit, 'err' compilation failed"); + st->print_cr(" " LEGEND_KEY_FMT ": %s", "#nodes", "...how many nodes (c2 only)"); + st->print_cr(" " LEGEND_KEY_FMT ": %s", "limit", "memory limit, if set"); + st->print_cr(" " LEGEND_KEY_FMT ": %s", "time", "time taken for last compilation (sec)"); + st->print_cr(" " LEGEND_KEY_FMT ": %s", "type", "compiler type"); + st->print_cr(" " LEGEND_KEY_FMT ": %s", "#rc", "how often recompiled"); + st->print_cr(" " LEGEND_KEY_FMT ": %s", "thread", "compiler thread"); +#undef LEGEND_KEY_FMT } static void print_header(outputStream* st) { - st->print_cr("total NA RA result #nodes limit time type #rc thread method"); +#define SIZE_FMT "%-10s" + st->print(SIZE_FMT, "total"); + for (int tag = 0; tag < Arena::tag_count(); tag++) { + st->print(SIZE_FMT, Arena::tag_name[tag]); + } +#define HDR_FMT1 "%-8s%-8s%-8s%-8s" +#define HDR_FMT2 "%-6s%-4s%-19s%s" + + st->print(HDR_FMT1, "result", "#nodes", "limit", "time"); + st->print(HDR_FMT2, "type", "#rc", "thread", "method"); + st->print_cr(""); } void print_on(outputStream* st, bool human_readable) const { @@ -247,21 +265,14 @@ class MemStatEntry : public CHeapObj { } col += 10; st->fill_to(col); - // NA - if (human_readable) { - st->print(PROPERFMT " ", PROPERFMTARGS(_na_at_peak)); - } else { - st->print("%zu ", _na_at_peak); - } - col += 10; st->fill_to(col); - - // RA - if (human_readable) { - st->print(PROPERFMT " ", PROPERFMTARGS(_ra_at_peak)); - } else { - st->print("%zu ", _ra_at_peak); + for (int tag = 0; tag < Arena::tag_count(); tag++) { + if (human_readable) { + st->print(PROPERFMT " ", PROPERFMTARGS(_peak_by_tag.counter(tag))); + } else { + st->print("%zu ", _peak_by_tag.counter(tag)); + } + col += 10; st->fill_to(col); } - col += 10; st->fill_to(col); // result? st->print("%s ", _result ? _result : ""); @@ -296,7 +307,7 @@ class MemStatEntry : public CHeapObj { col += 4; st->fill_to(col); // Thread - st->print(PTR_FORMAT " ", p2i(_thread)); + st->print(PTR_FORMAT " ", p2i(_thread)); // MethodName char buf[1024]; @@ -341,7 +352,7 @@ class MemStatTable : public: void add(const FullMethodName& fmn, CompilerType comptype, - size_t total, size_t na_at_peak, size_t ra_at_peak, + size_t total, ArenaCountersByTag peak_by_tag, unsigned live_nodes_at_peak, size_t limit, const char* result) { assert_lock_strong(NMTCompilationCostHistory_lock); MemStatTableKey key(fmn, comptype); @@ -360,8 +371,7 @@ class MemStatTable : e->set_comptype(comptype); e->inc_recompilation(); e->set_total(total); - e->set_na_at_peak(na_at_peak); - e->set_ra_at_peak(ra_at_peak); + e->set_peak_by_tag(peak_by_tag); e->set_live_nodes_at_peak(live_nodes_at_peak); e->set_limit(limit); e->set_result(result); @@ -427,7 +437,7 @@ void CompilationMemoryStatistic::on_end_compilation() { const bool print = directive->should_print_memstat(); // Store memory used in task, for later processing by JFR - task->set_arena_bytes(arena_stat->peak_since_start()); + task->set_arena_bytes(arena_stat->peak()); // Store result // For this to work, we must call on_end_compilation() at a point where @@ -447,9 +457,8 @@ void CompilationMemoryStatistic::on_end_compilation() { assert(_the_table != nullptr, "not initialized"); _the_table->add(fmn, ct, - arena_stat->peak_since_start(), // total - arena_stat->na_at_peak(), - arena_stat->ra_at_peak(), + arena_stat->peak(), // total + arena_stat->peak_by_tag(), arena_stat->live_nodes_at_peak(), arena_stat->limit(), result); @@ -511,7 +520,7 @@ void CompilationMemoryStatistic::on_arena_change(ssize_t diff, const Arena* aren bool hit_limit_before = arena_stat->hit_limit(); - if (arena_stat->account(diff, (int)arena->get_tag())) { // new peak? + if (arena_stat->is_active() && arena_stat->account(diff, (int)arena->get_tag())) { // new peak? // Limit handling if (arena_stat->hit_limit()) { @@ -545,7 +554,7 @@ void CompilationMemoryStatistic::on_arena_change(ssize_t diff, const Arena* aren } ss.print("Hit MemLimit %s(limit: %zu now: %zu)", (hit_limit_before ? "again " : ""), - arena_stat->limit(), arena_stat->peak_since_start()); + arena_stat->limit(), arena_stat->peak()); } // log if needed diff --git a/src/hotspot/share/compiler/compilationMemoryStatistic.hpp b/src/hotspot/share/compiler/compilationMemoryStatistic.hpp index d7cccad17fd..d669cc14722 100644 --- a/src/hotspot/share/compiler/compilationMemoryStatistic.hpp +++ b/src/hotspot/share/compiler/compilationMemoryStatistic.hpp @@ -29,48 +29,70 @@ #include "compiler/compilerDefinitions.hpp" #include "memory/allocation.hpp" #include "memory/allStatic.hpp" +#include "memory/arena.hpp" #include "utilities/globalDefinitions.hpp" class outputStream; class Symbol; class DirectiveSet; -// Counters for allocations from one arena +// Helper class to wrap the array of arena tags for easier processing +class ArenaCountersByTag { +private: + size_t _counter[Arena::tag_count()]; + +public: + int element_count() const { return Arena::tag_count(); } + const char* tag_name(int tag) const { return Arena::tag_name[tag]; } + + size_t counter(int tag) const { + assert(tag < element_count(), "invalid tag %d", tag); + return _counter[tag]; + } + + void add(int tag, size_t value) { + assert(tag < element_count(), "invalid tag %d", tag); + _counter[tag] += value; + } + + void clear() { + memset(_counter, 0, sizeof(size_t) * element_count()); + } +}; + +// Counters for allocations from arenas during compilation class ArenaStatCounter : public CHeapObj { // Current bytes, total size_t _current; - // bytes when compilation started - size_t _start; // bytes at last peak, total size_t _peak; - // Current bytes used for node arenas, total - size_t _na; - // Current bytes used for resource areas - size_t _ra; + // Current bytes used by arenas per tag + ArenaCountersByTag _current_by_tag; + // Peak composition: + ArenaCountersByTag _peak_by_tag; // MemLimit handling size_t _limit; bool _hit_limit; bool _limit_in_process; - // Peak composition: - // Size of node arena when total peaked (c2 only) - size_t _na_at_peak; - // Size of resource area when total peaked - size_t _ra_at_peak; + // When to start accounting + bool _active; + // Number of live nodes when total peaked (c2 only) unsigned _live_nodes_at_peak; void update_c2_node_count(); + void reset(); + public: ArenaStatCounter(); // Size of peak since last compilation - size_t peak_since_start() const; + size_t peak() const { return _peak; } // Peak details - size_t na_at_peak() const { return _na_at_peak; } - size_t ra_at_peak() const { return _ra_at_peak; } + ArenaCountersByTag peak_by_tag() const { return _peak_by_tag; } unsigned live_nodes_at_peak() const { return _live_nodes_at_peak; } // Mark the start and end of a compilation. @@ -89,7 +111,7 @@ class ArenaStatCounter : public CHeapObj { bool hit_limit() const { return _hit_limit; } bool limit_in_process() const { return _limit_in_process; } void set_limit_in_process(bool v) { _limit_in_process = v; } - + bool is_active() const { return _active; } }; class CompilationMemoryStatistic : public AllStatic { diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRebuildAndScrub.cpp b/src/hotspot/share/gc/g1/g1ConcurrentRebuildAndScrub.cpp index 35cff96eb93..f62d4206900 100644 --- a/src/hotspot/share/gc/g1/g1ConcurrentRebuildAndScrub.cpp +++ b/src/hotspot/share/gc/g1/g1ConcurrentRebuildAndScrub.cpp @@ -87,8 +87,9 @@ class G1RebuildRSAndScrubTask : public WorkerTask { _processed_words += processed; } - // Yield if enough has been processed; returns if the concurrent marking cycle - // has been aborted for any reason. + // Yield if enough has been processed. Return whether we should stop + // processing this region because either the concurrent marking cycle has been + // aborted or the region has been reclaimed. bool yield_if_necessary(G1HeapRegion* hr) { if (_processed_words >= ProcessingYieldLimitInWords) { reset_processed_words(); @@ -98,7 +99,7 @@ class G1RebuildRSAndScrubTask : public WorkerTask { return true; } } - return _cm->has_aborted(); + return _cm->has_aborted() || !should_rebuild_or_scrub(hr); } // Returns whether the top at rebuild start value for the given region indicates diff --git a/src/hotspot/share/gc/g1/g1_globals.hpp b/src/hotspot/share/gc/g1/g1_globals.hpp index af1ac1f70c6..c8016ddc0dd 100644 --- a/src/hotspot/share/gc/g1/g1_globals.hpp +++ b/src/hotspot/share/gc/g1/g1_globals.hpp @@ -112,7 +112,7 @@ \ product(uint, G1ConfidencePercent, 50, \ "Confidence level for MMU/pause predictions") \ - range(0, 100) \ + range(1, 100) \ \ product(uintx, G1SummarizeRSetStatsPeriod, 0, DIAGNOSTIC, \ "The period (in number of GCs) at which we will generate " \ diff --git a/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp b/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp index cfdb7e9eb29..288a21fd35d 100644 --- a/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp +++ b/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2024, 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 @@ -205,8 +205,6 @@ void PSAdaptiveSizePolicy::compute_eden_space_size( bool is_full_gc) { // Update statistics - // Time statistics are updated as we go, update footprint stats here - _avg_base_footprint->sample(BaseFootPrintEstimate); avg_young_live()->sample(young_live); avg_eden_live()->sample(eden_live); @@ -363,8 +361,7 @@ void PSAdaptiveSizePolicy::compute_eden_space_size( log_debug(gc, ergo)("Live_space: " SIZE_FORMAT " free_space: " SIZE_FORMAT, live_space(), free_space()); - log_trace(gc, ergo)("Base_footprint: " SIZE_FORMAT " avg_young_live: " SIZE_FORMAT " avg_old_live: " SIZE_FORMAT, - (size_t)_avg_base_footprint->average(), + log_trace(gc, ergo)("avg_young_live: " SIZE_FORMAT " avg_old_live: " SIZE_FORMAT, (size_t)avg_young_live()->average(), (size_t)avg_old_live()->average()); @@ -535,8 +532,7 @@ void PSAdaptiveSizePolicy::compute_old_gen_free_space( log_debug(gc, ergo)("Live_space: " SIZE_FORMAT " free_space: " SIZE_FORMAT, live_space(), free_space()); - log_trace(gc, ergo)("Base_footprint: " SIZE_FORMAT " avg_young_live: " SIZE_FORMAT " avg_old_live: " SIZE_FORMAT, - (size_t)_avg_base_footprint->average(), + log_trace(gc, ergo)("avg_young_live: " SIZE_FORMAT " avg_old_live: " SIZE_FORMAT, (size_t)avg_young_live()->average(), (size_t)avg_old_live()->average()); diff --git a/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.hpp b/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.hpp index 90e1e60db94..4fab160dcb4 100644 --- a/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.hpp +++ b/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2024, 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 @@ -145,8 +145,7 @@ class PSAdaptiveSizePolicy : public AdaptiveSizePolicy { // Footprint accessors size_t live_space() const { - return (size_t)(avg_base_footprint()->average() + - avg_young_live()->average() + + return (size_t)(avg_young_live()->average() + avg_old_live()->average()); } size_t free_space() const { diff --git a/src/hotspot/share/gc/parallel/psCompactionManager.cpp b/src/hotspot/share/gc/parallel/psCompactionManager.cpp index b95c7c619af..d9f2749230e 100644 --- a/src/hotspot/share/gc/parallel/psCompactionManager.cpp +++ b/src/hotspot/share/gc/parallel/psCompactionManager.cpp @@ -54,7 +54,9 @@ Monitor* ParCompactionManager::_shadow_region_monitor = nullptr; PreservedMarksSet* ParCompactionManager::_preserved_marks_set = nullptr; -ParCompactionManager::ParCompactionManager(PreservedMarks* preserved_marks) { +ParCompactionManager::ParCompactionManager(PreservedMarks* preserved_marks, + ReferenceProcessor* ref_processor) + : _mark_and_push_closure(this, ref_processor) { ParallelScavengeHeap* heap = ParallelScavengeHeap::heap(); @@ -66,8 +68,9 @@ ParCompactionManager::ParCompactionManager(PreservedMarks* preserved_marks) { } void ParCompactionManager::initialize(ParMarkBitMap* mbm) { - assert(ParallelScavengeHeap::heap() != nullptr, - "Needed for initialization"); + assert(ParallelScavengeHeap::heap() != nullptr, "Needed for initialization"); + assert(PSParallelCompact::ref_processor() != nullptr, "precondition"); + assert(ParallelScavengeHeap::heap()->workers().max_workers() != 0, "Not initialized?"); _mark_bitmap = mbm; @@ -85,15 +88,13 @@ void ParCompactionManager::initialize(ParMarkBitMap* mbm) { // Create and register the ParCompactionManager(s) for the worker threads. for(uint i=0; iget(i)); + _manager_array[i] = new ParCompactionManager(_preserved_marks_set->get(i), + PSParallelCompact::ref_processor()); oop_task_queues()->register_queue(i, _manager_array[i]->oop_stack()); _objarray_task_queues->register_queue(i, &_manager_array[i]->_objarray_stack); region_task_queues()->register_queue(i, _manager_array[i]->region_stack()); } - assert(ParallelScavengeHeap::heap()->workers().max_workers() != 0, - "Not initialized?"); - _shadow_region_array = new (mtGC) GrowableArray(10, mtGC); _shadow_region_monitor = new Monitor(Mutex::nosafepoint, "CompactionManager_lock"); diff --git a/src/hotspot/share/gc/parallel/psCompactionManager.hpp b/src/hotspot/share/gc/parallel/psCompactionManager.hpp index 0dd68d2e2f7..da1609a32d8 100644 --- a/src/hotspot/share/gc/parallel/psCompactionManager.hpp +++ b/src/hotspot/share/gc/parallel/psCompactionManager.hpp @@ -25,6 +25,7 @@ #ifndef SHARE_GC_PARALLEL_PSCOMPACTIONMANAGER_HPP #define SHARE_GC_PARALLEL_PSCOMPACTIONMANAGER_HPP +#include "classfile/classLoaderData.hpp" #include "gc/parallel/psParallelCompact.hpp" #include "gc/shared/preservedMarks.hpp" #include "gc/shared/stringdedup/stringDedup.hpp" @@ -40,6 +41,19 @@ class ObjectStartArray; class ParallelCompactData; class ParMarkBitMap; +class PCMarkAndPushClosure: public ClaimMetadataVisitingOopIterateClosure { + ParCompactionManager* _compaction_manager; + + template void do_oop_work(T* p); +public: + PCMarkAndPushClosure(ParCompactionManager* cm, ReferenceProcessor* rp) : + ClaimMetadataVisitingOopIterateClosure(ClassLoaderData::_claim_stw_fullgc_mark, rp), + _compaction_manager(cm) { } + + virtual void do_oop(oop* p) { do_oop_work(p); } + virtual void do_oop(narrowOop* p) { do_oop_work(p); } +}; + class ParCompactionManager : public CHeapObj { friend class MarkFromRootsTask; friend class ParallelCompactRefProcProxyTask; @@ -47,6 +61,7 @@ class ParCompactionManager : public CHeapObj { friend class ParMarkBitMap; friend class PSParallelCompact; friend class FillDensePrefixAndCompactionTask; + friend class PCAddThreadRootsMarkingTaskClosure; private: typedef OverflowTaskQueue OopTaskQueue; @@ -71,6 +86,7 @@ class ParCompactionManager : public CHeapObj { ObjArrayTaskQueue _objarray_stack; size_t _next_shadow_region; + PCMarkAndPushClosure _mark_and_push_closure; // Is there a way to reuse the _oop_stack for the // saving empty regions? For now just create a different // type of TaskQueue. @@ -104,7 +120,9 @@ class ParCompactionManager : public CHeapObj { // objArray stack, otherwise returns false and the task is invalid. bool publish_or_pop_objarray_tasks(ObjArrayTask& task); - ParCompactionManager(PreservedMarks* preserved_marks); + ParCompactionManager(PreservedMarks* preserved_marks, + ReferenceProcessor* ref_processor); + // Array of task queues. Needed by the task terminator. static RegionTaskQueueSet* region_task_queues() { return _region_task_queues; } OopTaskQueue* oop_stack() { return &_oop_stack; } diff --git a/src/hotspot/share/gc/parallel/psCompactionManager.inline.hpp b/src/hotspot/share/gc/parallel/psCompactionManager.inline.hpp index 22acc11beec..0b928622238 100644 --- a/src/hotspot/share/gc/parallel/psCompactionManager.inline.hpp +++ b/src/hotspot/share/gc/parallel/psCompactionManager.inline.hpp @@ -41,29 +41,10 @@ #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" -class PCMarkAndPushClosure: public OopClosure { -private: - ParCompactionManager* _compaction_manager; -public: - PCMarkAndPushClosure(ParCompactionManager* cm) : _compaction_manager(cm) { } - - template void do_oop_work(T* p) { _compaction_manager->mark_and_push(p); } - virtual void do_oop(oop* p) { do_oop_work(p); } - virtual void do_oop(narrowOop* p) { do_oop_work(p); } -}; - -class PCIterateMarkAndPushClosure: public ClaimMetadataVisitingOopIterateClosure { -private: - ParCompactionManager* _compaction_manager; -public: - PCIterateMarkAndPushClosure(ParCompactionManager* cm, ReferenceProcessor* rp) : - ClaimMetadataVisitingOopIterateClosure(ClassLoaderData::_claim_stw_fullgc_mark, rp), - _compaction_manager(cm) { } - - template void do_oop_work(T* p) { _compaction_manager->mark_and_push(p); } - virtual void do_oop(oop* p) { do_oop_work(p); } - virtual void do_oop(narrowOop* p) { do_oop_work(p); } -}; +template +inline void PCMarkAndPushClosure::do_oop_work(T* p) { + _compaction_manager->mark_and_push(p); +} inline bool ParCompactionManager::steal(int queue_num, oop& t) { return oop_task_queues()->steal(queue_num, t); @@ -161,13 +142,12 @@ inline void ParCompactionManager::follow_array(objArrayOop obj, int index) { inline void ParCompactionManager::follow_contents(oop obj) { assert(PSParallelCompact::mark_bitmap()->is_marked(obj), "should be marked"); - PCIterateMarkAndPushClosure cl(this, PSParallelCompact::ref_processor()); if (obj->is_objArray()) { - cl.do_klass(obj->klass()); + _mark_and_push_closure.do_klass(obj->klass()); follow_array(objArrayOop(obj), 0); } else { - obj->oop_iterate(&cl); + obj->oop_iterate(&_mark_and_push_closure); } } diff --git a/src/hotspot/share/gc/parallel/psGCAdaptivePolicyCounters.cpp b/src/hotspot/share/gc/parallel/psGCAdaptivePolicyCounters.cpp index 20e14b73ac3..ed96c5c4181 100644 --- a/src/hotspot/share/gc/parallel/psGCAdaptivePolicyCounters.cpp +++ b/src/hotspot/share/gc/parallel/psGCAdaptivePolicyCounters.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, 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 @@ -107,10 +107,6 @@ PSGCAdaptivePolicyCounters::PSGCAdaptivePolicyCounters(const char* name_arg, _free_space = PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, ps_size_policy()->free_space(), CHECK); - cname = PerfDataManager::counter_name(name_space(), "avgBaseFootprint"); - _avg_base_footprint = PerfDataManager::create_variable(SUN_GC, cname, - PerfData::U_Bytes, (jlong) ps_size_policy()->avg_base_footprint()->average(), CHECK); - cname = PerfDataManager::counter_name(name_space(), "liveAtLastFullGc"); _live_at_last_full_gc_counter = PerfDataManager::create_variable(SUN_GC, cname, @@ -157,7 +153,6 @@ void PSGCAdaptivePolicyCounters::update_counters_from_policy() { update_decrement_tenuring_threshold_for_survivor_limit(); update_live_space(); update_free_space(); - update_avg_base_footprint(); update_change_old_gen_for_maj_pauses(); update_change_young_gen_for_maj_pauses(); diff --git a/src/hotspot/share/gc/parallel/psGCAdaptivePolicyCounters.hpp b/src/hotspot/share/gc/parallel/psGCAdaptivePolicyCounters.hpp index 620c761004e..ad84eb4368b 100644 --- a/src/hotspot/share/gc/parallel/psGCAdaptivePolicyCounters.hpp +++ b/src/hotspot/share/gc/parallel/psGCAdaptivePolicyCounters.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, 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 @@ -50,7 +50,6 @@ class PSGCAdaptivePolicyCounters : public GCAdaptivePolicyCounters { PerfVariable* _avg_major_interval; PerfVariable* _live_space; PerfVariable* _free_space; - PerfVariable* _avg_base_footprint; PerfVariable* _live_at_last_full_gc_counter; PerfVariable* _old_capacity; @@ -142,11 +141,6 @@ class PSGCAdaptivePolicyCounters : public GCAdaptivePolicyCounters { _free_space->set_value(ps_size_policy()->free_space()); } - inline void update_avg_base_footprint() { - _avg_base_footprint->set_value( - (jlong)(ps_size_policy()->avg_base_footprint()->average()) - ); - } inline void update_avg_old_live() { _avg_old_live_counter->set_value( (jlong)(ps_size_policy()->avg_old_live()->average()) diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.cpp b/src/hotspot/share/gc/parallel/psParallelCompact.cpp index f41108d1a59..4d54f11805a 100644 --- a/src/hotspot/share/gc/parallel/psParallelCompact.cpp +++ b/src/hotspot/share/gc/parallel/psParallelCompact.cpp @@ -1188,10 +1188,11 @@ class PCAddThreadRootsMarkingTaskClosure : public ThreadClosure { ParCompactionManager* cm = ParCompactionManager::gc_thread_compaction_manager(_worker_id); - PCMarkAndPushClosure mark_and_push_closure(cm); - MarkingNMethodClosure mark_and_push_in_blobs(&mark_and_push_closure, !NMethodToOopClosure::FixRelocations, true /* keepalive nmethods */); + MarkingNMethodClosure mark_and_push_in_blobs(&cm->_mark_and_push_closure, + !NMethodToOopClosure::FixRelocations, + true /* keepalive nmethods */); - thread->oops_do(&mark_and_push_closure, &mark_and_push_in_blobs); + thread->oops_do(&cm->_mark_and_push_closure, &mark_and_push_in_blobs); // Do the real work cm->follow_marking_stacks(); @@ -1232,22 +1233,22 @@ class MarkFromRootsTask : public WorkerTask { virtual void work(uint worker_id) { ParCompactionManager* cm = ParCompactionManager::gc_thread_compaction_manager(worker_id); cm->create_marking_stats_cache(); - PCMarkAndPushClosure mark_and_push_closure(cm); - { - CLDToOopClosure cld_closure(&mark_and_push_closure, ClassLoaderData::_claim_stw_fullgc_mark); + CLDToOopClosure cld_closure(&cm->_mark_and_push_closure, ClassLoaderData::_claim_stw_fullgc_mark); ClassLoaderDataGraph::always_strong_cld_do(&cld_closure); // Do the real work cm->follow_marking_stacks(); } - PCAddThreadRootsMarkingTaskClosure closure(worker_id); - Threads::possibly_parallel_threads_do(true /* is_par */, &closure); + { + PCAddThreadRootsMarkingTaskClosure closure(worker_id); + Threads::possibly_parallel_threads_do(_active_workers > 1 /* is_par */, &closure); + } // Mark from OopStorages { - _oop_storage_set_par_state.oops_do(&mark_and_push_closure); + _oop_storage_set_par_state.oops_do(&cm->_mark_and_push_closure); // Do the real work cm->follow_marking_stacks(); } @@ -1269,10 +1270,9 @@ class ParallelCompactRefProcProxyTask : public RefProcProxyTask { void work(uint worker_id) override { assert(worker_id < _max_workers, "sanity"); ParCompactionManager* cm = (_tm == RefProcThreadModel::Single) ? ParCompactionManager::get_vmthread_cm() : ParCompactionManager::gc_thread_compaction_manager(worker_id); - PCMarkAndPushClosure keep_alive(cm); BarrierEnqueueDiscoveredFieldClosure enqueue; ParCompactionManager::FollowStackClosure complete_gc(cm, (_tm == RefProcThreadModel::Single) ? nullptr : &_terminator, worker_id); - _rp_task->rp_work(worker_id, PSParallelCompact::is_alive_closure(), &keep_alive, &enqueue, &complete_gc); + _rp_task->rp_work(worker_id, PSParallelCompact::is_alive_closure(), &cm->_mark_and_push_closure, &enqueue, &complete_gc); } void prepare_run_task_hook() override { diff --git a/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp b/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp index d03f416ff68..c1485c069c8 100644 --- a/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp +++ b/src/hotspot/share/gc/shared/c2/barrierSetC2.hpp @@ -102,10 +102,10 @@ class C2AccessValuePtr: public C2AccessValue { class C2Access: public StackObj { protected: DecoratorSet _decorators; - BasicType _type; Node* _base; C2AccessValuePtr& _addr; Node* _raw_access; + BasicType _type; uint8_t _barrier_data; void fixup_decorators(); @@ -114,10 +114,10 @@ class C2Access: public StackObj { C2Access(DecoratorSet decorators, BasicType type, Node* base, C2AccessValuePtr& addr) : _decorators(decorators), - _type(type), _base(base), _addr(addr), _raw_access(nullptr), + _type(type), _barrier_data(0) {} diff --git a/src/hotspot/share/gc/shared/freeListAllocator.cpp b/src/hotspot/share/gc/shared/freeListAllocator.cpp index f33c6fcb14b..ef7d12ab024 100644 --- a/src/hotspot/share/gc/shared/freeListAllocator.cpp +++ b/src/hotspot/share/gc/shared/freeListAllocator.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, 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 @@ -88,7 +88,7 @@ void FreeListAllocator::delete_list(FreeNode* list) { FreeListAllocator::~FreeListAllocator() { uint index = Atomic::load(&_active_pending_list); NodeList pending_list = _pending_lists[index].take_all(); - delete_list(Atomic::load(&pending_list._head)); + delete_list(pending_list._head); delete_list(_free_list.pop_all()); } @@ -106,7 +106,7 @@ size_t FreeListAllocator::free_count() const { size_t FreeListAllocator::pending_count() const { uint index = Atomic::load(&_active_pending_list); - return _pending_lists[index].count();; + return _pending_lists[index].count(); } // To solve the ABA problem, popping a node from the _free_list is performed within diff --git a/src/hotspot/share/gc/shared/gc_globals.hpp b/src/hotspot/share/gc/shared/gc_globals.hpp index 66496544b96..34bc638c9ba 100644 --- a/src/hotspot/share/gc/shared/gc_globals.hpp +++ b/src/hotspot/share/gc/shared/gc_globals.hpp @@ -422,10 +422,6 @@ "Initial ratio of young generation/survivor space size") \ range(0, max_uintx) \ \ - product(size_t, BaseFootPrintEstimate, 256*M, \ - "Estimate of footprint other than Java Heap") \ - range(0, max_uintx) \ - \ product(bool, UseGCOverheadLimit, true, \ "Use policy to limit of proportion of time spent in GC " \ "before an OutOfMemory error is thrown") \ diff --git a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp index 30bf1f7a395..6b104e7e199 100644 --- a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp +++ b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp @@ -183,20 +183,11 @@ LIR_Opr ShenandoahBarrierSetC1::ensure_in_register(LIRGenerator* gen, LIR_Opr ob return obj; } -LIR_Opr ShenandoahBarrierSetC1::iu_barrier(LIRGenerator* gen, LIR_Opr obj, CodeEmitInfo* info, DecoratorSet decorators) { - if (ShenandoahIUBarrier) { - obj = ensure_in_register(gen, obj, T_OBJECT); - pre_barrier(gen, info, decorators, LIR_OprFact::illegalOpr, obj); - } - return obj; -} - void ShenandoahBarrierSetC1::store_at_resolved(LIRAccess& access, LIR_Opr value) { if (access.is_oop()) { if (ShenandoahSATBBarrier) { pre_barrier(access.gen(), access.access_emit_info(), access.decorators(), access.resolved_addr(), LIR_OprFact::illegalOpr /* pre_val */); } - value = iu_barrier(access.gen(), value, access.access_emit_info(), access.decorators()); } BarrierSetC1::store_at_resolved(access, value); } diff --git a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp index 71093300e82..98b2aad8871 100644 --- a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp +++ b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp @@ -200,7 +200,6 @@ class ShenandoahBarrierSetC1 : public BarrierSetC1 { void pre_barrier(LIRGenerator* gen, CodeEmitInfo* info, DecoratorSet decorators, LIR_Opr addr_opr, LIR_Opr pre_val); LIR_Opr load_reference_barrier(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators); - LIR_Opr iu_barrier(LIRGenerator* gen, LIR_Opr obj, CodeEmitInfo* info, DecoratorSet decorators); LIR_Opr load_reference_barrier_impl(LIRGenerator* gen, LIR_Opr obj, LIR_Opr addr, DecoratorSet decorators); diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp index 7d1f5cc16a8..d2820f3aa26 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp @@ -48,25 +48,7 @@ ShenandoahBarrierSetC2* ShenandoahBarrierSetC2::bsc2() { } ShenandoahBarrierSetC2State::ShenandoahBarrierSetC2State(Arena* comp_arena) - : _iu_barriers(new (comp_arena) GrowableArray(comp_arena, 8, 0, nullptr)), - _load_reference_barriers(new (comp_arena) GrowableArray(comp_arena, 8, 0, nullptr)) { -} - -int ShenandoahBarrierSetC2State::iu_barriers_count() const { - return _iu_barriers->length(); -} - -ShenandoahIUBarrierNode* ShenandoahBarrierSetC2State::iu_barrier(int idx) const { - return _iu_barriers->at(idx); -} - -void ShenandoahBarrierSetC2State::add_iu_barrier(ShenandoahIUBarrierNode* n) { - assert(!_iu_barriers->contains(n), "duplicate entry in barrier list"); - _iu_barriers->append(n); -} - -void ShenandoahBarrierSetC2State::remove_iu_barrier(ShenandoahIUBarrierNode* n) { - _iu_barriers->remove_if_existing(n); + : _load_reference_barriers(new (comp_arena) GrowableArray(comp_arena, 8, 0, nullptr)) { } int ShenandoahBarrierSetC2State::load_reference_barriers_count() const { @@ -88,13 +70,6 @@ void ShenandoahBarrierSetC2State::remove_load_reference_barrier(ShenandoahLoadRe } } -Node* ShenandoahBarrierSetC2::shenandoah_iu_barrier(GraphKit* kit, Node* obj) const { - if (ShenandoahIUBarrier) { - return kit->gvn().transform(new ShenandoahIUBarrierNode(obj)); - } - return obj; -} - #define __ kit-> bool ShenandoahBarrierSetC2::satb_can_remove_pre_barrier(GraphKit* kit, PhaseValues* phase, Node* adr, @@ -508,21 +483,8 @@ Node* ShenandoahBarrierSetC2::store_at_resolved(C2Access& access, C2AccessValue& uint adr_idx = kit->C->get_alias_index(adr_type); assert(adr_idx != Compile::AliasIdxTop, "use other store_to_memory factory" ); - Node* value = val.node(); - value = shenandoah_iu_barrier(kit, value); - val.set_node(value); shenandoah_write_barrier_pre(kit, true /* do_load */, /*kit->control(),*/ access.base(), adr, adr_idx, val.node(), static_cast(val.type()), nullptr /* pre_val */, access.type()); - } else { - assert(access.is_opt_access(), "only for optimization passes"); - assert(((decorators & C2_TIGHTLY_COUPLED_ALLOC) != 0 || !ShenandoahSATBBarrier) && (decorators & C2_ARRAY_COPY) != 0, "unexpected caller of this code"); - C2OptAccess& opt_access = static_cast(access); - PhaseGVN& gvn = opt_access.gvn(); - - if (ShenandoahIUBarrier) { - Node* enqueue = gvn.transform(new ShenandoahIUBarrierNode(val.node())); - val.set_node(enqueue); - } } return BarrierSetC2::store_at_resolved(access, val); } @@ -598,7 +560,6 @@ Node* ShenandoahBarrierSetC2::atomic_cmpxchg_val_at_resolved(C2AtomicParseAccess Node* new_val, const Type* value_type) const { GraphKit* kit = access.kit(); if (access.is_oop()) { - new_val = shenandoah_iu_barrier(kit, new_val); shenandoah_write_barrier_pre(kit, false /* do_load */, nullptr, nullptr, max_juint, nullptr, nullptr, expected_val /* pre_val */, T_OBJECT); @@ -646,7 +607,6 @@ Node* ShenandoahBarrierSetC2::atomic_cmpxchg_bool_at_resolved(C2AtomicParseAcces Node* new_val, const Type* value_type) const { GraphKit* kit = access.kit(); if (access.is_oop()) { - new_val = shenandoah_iu_barrier(kit, new_val); shenandoah_write_barrier_pre(kit, false /* do_load */, nullptr, nullptr, max_juint, nullptr, nullptr, expected_val /* pre_val */, T_OBJECT); @@ -699,9 +659,6 @@ Node* ShenandoahBarrierSetC2::atomic_cmpxchg_bool_at_resolved(C2AtomicParseAcces Node* ShenandoahBarrierSetC2::atomic_xchg_at_resolved(C2AtomicParseAccess& access, Node* val, const Type* value_type) const { GraphKit* kit = access.kit(); - if (access.is_oop()) { - val = shenandoah_iu_barrier(kit, val); - } Node* result = BarrierSetC2::atomic_xchg_at_resolved(access, val, value_type); if (access.is_oop()) { result = kit->gvn().transform(new ShenandoahLoadReferenceBarrierNode(nullptr, result, access.decorators())); @@ -719,7 +676,7 @@ bool ShenandoahBarrierSetC2::is_gc_pre_barrier_node(Node* node) const { // Support for GC barriers emitted during parsing bool ShenandoahBarrierSetC2::is_gc_barrier_node(Node* node) const { - if (node->Opcode() == Op_ShenandoahLoadReferenceBarrier || node->Opcode() == Op_ShenandoahIUBarrier) return true; + if (node->Opcode() == Op_ShenandoahLoadReferenceBarrier) return true; if (node->Opcode() != Op_CallLeaf && node->Opcode() != Op_CallLeafNoFP) { return false; } @@ -740,9 +697,6 @@ Node* ShenandoahBarrierSetC2::step_over_gc_barrier(Node* c) const { if (c->Opcode() == Op_ShenandoahLoadReferenceBarrier) { return c->in(ShenandoahLoadReferenceBarrierNode::ValueIn); } - if (c->Opcode() == Op_ShenandoahIUBarrier) { - c = c->in(1); - } return c; } @@ -775,9 +729,6 @@ bool ShenandoahBarrierSetC2::array_copy_requires_gc_barriers(bool tightly_couple } return !is_clone; } - if (phase == Optimization) { - return !ShenandoahIUBarrier; - } return true; } @@ -836,11 +787,7 @@ void ShenandoahBarrierSetC2::clone_at_expansion(PhaseMacroExpand* phase, ArrayCo debug_only(gc_state_adr_type = phase->C->get_adr_type(gc_state_idx)); Node* gc_state = phase->transform_later(new LoadBNode(ctrl, mem, gc_state_addr, gc_state_adr_type, TypeInt::BYTE, MemNode::unordered)); - int flags = ShenandoahHeap::HAS_FORWARDED; - if (ShenandoahIUBarrier) { - flags |= ShenandoahHeap::MARKING; - } - Node* stable_and = phase->transform_later(new AndINode(gc_state, phase->igvn().intcon(flags))); + Node* stable_and = phase->transform_later(new AndINode(gc_state, phase->igvn().intcon(ShenandoahHeap::HAS_FORWARDED))); Node* stable_cmp = phase->transform_later(new CmpINode(stable_and, phase->igvn().zerocon(T_INT))); Node* stable_test = phase->transform_later(new BoolNode(stable_cmp, BoolTest::ne)); @@ -889,18 +836,12 @@ void ShenandoahBarrierSetC2::clone_at_expansion(PhaseMacroExpand* phase, ArrayCo // Support for macro expanded GC barriers void ShenandoahBarrierSetC2::register_potential_barrier_node(Node* node) const { - if (node->Opcode() == Op_ShenandoahIUBarrier) { - state()->add_iu_barrier((ShenandoahIUBarrierNode*) node); - } if (node->Opcode() == Op_ShenandoahLoadReferenceBarrier) { state()->add_load_reference_barrier((ShenandoahLoadReferenceBarrierNode*) node); } } void ShenandoahBarrierSetC2::unregister_potential_barrier_node(Node* node) const { - if (node->Opcode() == Op_ShenandoahIUBarrier) { - state()->remove_iu_barrier((ShenandoahIUBarrierNode*) node); - } if (node->Opcode() == Op_ShenandoahLoadReferenceBarrier) { state()->remove_load_reference_barrier((ShenandoahLoadReferenceBarrierNode*) node); } @@ -948,12 +889,7 @@ void ShenandoahBarrierSetC2::eliminate_useless_gc_barriers(Unique_Node_List &use } } } - for (int i = state()->iu_barriers_count() - 1; i >= 0; i--) { - ShenandoahIUBarrierNode* n = state()->iu_barrier(i); - if (!useful.member(n)) { - state()->remove_iu_barrier(n); - } - } + for (int i = state()->load_reference_barriers_count() - 1; i >= 0; i--) { ShenandoahLoadReferenceBarrierNode* n = state()->load_reference_barrier(i); if (!useful.member(n)) { @@ -1190,9 +1126,6 @@ bool ShenandoahBarrierSetC2::escape_add_to_con_graph(ConnectionGraph* conn_graph } return false; } - case Op_ShenandoahIUBarrier: - conn_graph->add_local_var_and_edge(n, PointsToNode::NoEscape, n->in(1), delayed_worklist); - break; case Op_ShenandoahLoadReferenceBarrier: conn_graph->add_local_var_and_edge(n, PointsToNode::NoEscape, n->in(ShenandoahLoadReferenceBarrierNode::ValueIn), delayed_worklist); return true; @@ -1216,9 +1149,6 @@ bool ShenandoahBarrierSetC2::escape_add_final_edges(ConnectionGraph* conn_graph, case Op_ShenandoahWeakCompareAndSwapP: case Op_ShenandoahWeakCompareAndSwapN: return conn_graph->add_final_edges_unsafe_access(n, opcode); - case Op_ShenandoahIUBarrier: - conn_graph->add_local_var_and_edge(n, PointsToNode::NoEscape, n->in(1), nullptr); - return true; case Op_ShenandoahLoadReferenceBarrier: conn_graph->add_local_var_and_edge(n, PointsToNode::NoEscape, n->in(ShenandoahLoadReferenceBarrierNode::ValueIn), nullptr); return true; diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp index 9b8e30c98a1..4619b217e96 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp @@ -31,17 +31,11 @@ class ShenandoahBarrierSetC2State : public ArenaObj { private: - GrowableArray* _iu_barriers; GrowableArray* _load_reference_barriers; public: ShenandoahBarrierSetC2State(Arena* comp_arena); - int iu_barriers_count() const; - ShenandoahIUBarrierNode* iu_barrier(int idx) const; - void add_iu_barrier(ShenandoahIUBarrierNode* n); - void remove_iu_barrier(ShenandoahIUBarrierNode * n); - int load_reference_barriers_count() const; ShenandoahLoadReferenceBarrierNode* load_reference_barrier(int idx) const; void add_load_reference_barrier(ShenandoahLoadReferenceBarrierNode* n); @@ -73,8 +67,6 @@ class ShenandoahBarrierSetC2 : public BarrierSetC2 { Node* pre_val, BasicType bt) const; - Node* shenandoah_iu_barrier(GraphKit* kit, Node* obj) const; - void insert_pre_barrier(GraphKit* kit, Node* base_oop, Node* offset, Node* pre_val, bool need_mem_bar) const; diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp index 8cd76dd3d6b..0a51f742995 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp @@ -46,8 +46,7 @@ bool ShenandoahBarrierC2Support::expand(Compile* C, PhaseIterGVN& igvn) { ShenandoahBarrierSetC2State* state = ShenandoahBarrierSetC2::bsc2()->state(); - if ((state->iu_barriers_count() + - state->load_reference_barriers_count()) > 0) { + if (state->load_reference_barriers_count() > 0) { assert(C->post_loop_opts_phase(), "no loop opts allowed"); C->reset_post_loop_opts_phase(); // ... but we know what we are doing C->clear_major_progress(); @@ -186,28 +185,10 @@ bool ShenandoahBarrierC2Support::verify_helper(Node* in, Node_Stack& phis, Vecto } } else if (in->Opcode() == Op_ShenandoahLoadReferenceBarrier) { if (t == ShenandoahOopStore) { - uint i = 0; - for (; i < phis.size(); i++) { - Node* n = phis.node_at(i); - if (n->Opcode() == Op_ShenandoahIUBarrier) { - break; - } - } - if (i == phis.size()) { - return false; - } + return false; } barriers_used.push(in); if (trace) {tty->print("Found barrier"); in->dump();} - } else if (in->Opcode() == Op_ShenandoahIUBarrier) { - if (t != ShenandoahOopStore) { - in = in->in(1); - continue; - } - if (trace) {tty->print("Found enqueue barrier"); in->dump();} - phis.push(in, in->req()); - in = in->in(1); - continue; } else if (in->is_Proj() && in->in(0)->is_Allocate()) { if (trace) { tty->print("Found alloc"); @@ -326,7 +307,7 @@ void ShenandoahBarrierC2Support::verify(RootNode* root) { } } - if (verify && !verify_helper(n->in(MemNode::ValueIn), phis, visited, ShenandoahIUBarrier ? ShenandoahOopStore : ShenandoahValue, trace, barriers_used)) { + if (verify && !verify_helper(n->in(MemNode::ValueIn), phis, visited, ShenandoahValue, trace, barriers_used)) { report_verify_failure("Shenandoah verification: Store should have barriers", n); } } @@ -368,7 +349,7 @@ void ShenandoahBarrierC2Support::verify(RootNode* root) { } } else if (n->is_LoadStore()) { if (n->in(MemNode::ValueIn)->bottom_type()->make_ptr() && - !verify_helper(n->in(MemNode::ValueIn), phis, visited, ShenandoahIUBarrier ? ShenandoahOopStore : ShenandoahValue, trace, barriers_used)) { + !verify_helper(n->in(MemNode::ValueIn), phis, visited, ShenandoahValue, trace, barriers_used)) { report_verify_failure("Shenandoah verification: LoadStore (value) should have barriers", n); } @@ -532,7 +513,7 @@ void ShenandoahBarrierC2Support::verify(RootNode* root) { } } } - } else if (n->Opcode() == Op_ShenandoahIUBarrier || n->Opcode() == Op_ShenandoahLoadReferenceBarrier) { + } else if (n->Opcode() == Op_ShenandoahLoadReferenceBarrier) { // skip } else if (n->is_AddP() || n->is_Phi() @@ -1113,20 +1094,6 @@ void ShenandoahBarrierC2Support::pin_and_expand(PhaseIdealLoop* phase) { ShenandoahBarrierSetC2State* state = ShenandoahBarrierSetC2::bsc2()->state(); Unique_Node_List uses; - for (int i = 0; i < state->iu_barriers_count(); i++) { - Node* barrier = state->iu_barrier(i); - Node* ctrl = phase->get_ctrl(barrier); - IdealLoopTree* loop = phase->get_loop(ctrl); - Node* head = loop->head(); - if (head->is_OuterStripMinedLoop()) { - // Expanding a barrier here will break loop strip mining - // verification. Transform the loop so the loop nest doesn't - // appear as strip mined. - OuterStripMinedLoopNode* outer = head->as_OuterStripMinedLoop(); - hide_strip_mined_loop(outer, outer->unique_ctrl_out()->as_CountedLoop(), phase); - } - } - Node_Stack stack(0); Node_List clones; for (int i = state->load_reference_barriers_count() - 1; i >= 0; i--) { @@ -1459,157 +1426,6 @@ void ShenandoahBarrierC2Support::pin_and_expand(PhaseIdealLoop* phase) { } // Done expanding load-reference-barriers. assert(ShenandoahBarrierSetC2::bsc2()->state()->load_reference_barriers_count() == 0, "all load reference barrier nodes should have been replaced"); - - for (int i = state->iu_barriers_count() - 1; i >= 0; i--) { - Node* barrier = state->iu_barrier(i); - Node* pre_val = barrier->in(1); - - if (phase->igvn().type(pre_val)->higher_equal(TypePtr::NULL_PTR)) { - ShouldNotReachHere(); - continue; - } - - Node* ctrl = phase->get_ctrl(barrier); - - if (ctrl->is_Proj() && ctrl->in(0)->is_CallJava()) { - assert(is_dominator(phase->get_ctrl(pre_val), ctrl->in(0)->in(0), pre_val, ctrl->in(0), phase), "can't move"); - ctrl = ctrl->in(0)->in(0); - phase->set_ctrl(barrier, ctrl); - } else if (ctrl->is_CallRuntime()) { - assert(is_dominator(phase->get_ctrl(pre_val), ctrl->in(0), pre_val, ctrl, phase), "can't move"); - ctrl = ctrl->in(0); - phase->set_ctrl(barrier, ctrl); - } - - Node* init_ctrl = ctrl; - IdealLoopTree* loop = phase->get_loop(ctrl); - Node* raw_mem = fixer.find_mem(ctrl, barrier); - Node* init_raw_mem = raw_mem; - Node* raw_mem_for_ctrl = fixer.find_mem(ctrl, nullptr); - Node* heap_stable_ctrl = nullptr; - Node* null_ctrl = nullptr; - uint last = phase->C->unique(); - - enum { _heap_stable = 1, _heap_unstable, PATH_LIMIT }; - Node* region = new RegionNode(PATH_LIMIT); - Node* phi = PhiNode::make(region, raw_mem, Type::MEMORY, TypeRawPtr::BOTTOM); - - enum { _fast_path = 1, _slow_path, _null_path, PATH_LIMIT2 }; - Node* region2 = new RegionNode(PATH_LIMIT2); - Node* phi2 = PhiNode::make(region2, raw_mem, Type::MEMORY, TypeRawPtr::BOTTOM); - - // Stable path. - test_gc_state(ctrl, raw_mem, heap_stable_ctrl, phase, ShenandoahHeap::MARKING); - region->init_req(_heap_stable, heap_stable_ctrl); - phi->init_req(_heap_stable, raw_mem); - - // Null path - Node* reg2_ctrl = nullptr; - test_null(ctrl, pre_val, null_ctrl, phase); - if (null_ctrl != nullptr) { - reg2_ctrl = null_ctrl->in(0); - region2->init_req(_null_path, null_ctrl); - phi2->init_req(_null_path, raw_mem); - } else { - region2->del_req(_null_path); - phi2->del_req(_null_path); - } - - const int index_offset = in_bytes(ShenandoahThreadLocalData::satb_mark_queue_index_offset()); - const int buffer_offset = in_bytes(ShenandoahThreadLocalData::satb_mark_queue_buffer_offset()); - Node* thread = new ThreadLocalNode(); - phase->register_new_node(thread, ctrl); - Node* buffer_adr = new AddPNode(phase->C->top(), thread, phase->igvn().MakeConX(buffer_offset)); - phase->register_new_node(buffer_adr, ctrl); - Node* index_adr = new AddPNode(phase->C->top(), thread, phase->igvn().MakeConX(index_offset)); - phase->register_new_node(index_adr, ctrl); - - BasicType index_bt = TypeX_X->basic_type(); - assert(sizeof(size_t) == type2aelembytes(index_bt), "Loading Shenandoah SATBMarkQueue::_index with wrong size."); - const TypePtr* adr_type = TypeRawPtr::BOTTOM; - Node* index = new LoadXNode(ctrl, raw_mem, index_adr, adr_type, TypeX_X, MemNode::unordered); - phase->register_new_node(index, ctrl); - Node* index_cmp = new CmpXNode(index, phase->igvn().MakeConX(0)); - phase->register_new_node(index_cmp, ctrl); - Node* index_test = new BoolNode(index_cmp, BoolTest::ne); - phase->register_new_node(index_test, ctrl); - IfNode* queue_full_iff = new IfNode(ctrl, index_test, PROB_LIKELY(0.999), COUNT_UNKNOWN); - if (reg2_ctrl == nullptr) reg2_ctrl = queue_full_iff; - phase->register_control(queue_full_iff, loop, ctrl); - Node* not_full = new IfTrueNode(queue_full_iff); - phase->register_control(not_full, loop, queue_full_iff); - Node* full = new IfFalseNode(queue_full_iff); - phase->register_control(full, loop, queue_full_iff); - - ctrl = not_full; - - Node* next_index = new SubXNode(index, phase->igvn().MakeConX(sizeof(intptr_t))); - phase->register_new_node(next_index, ctrl); - - Node* buffer = new LoadPNode(ctrl, raw_mem, buffer_adr, adr_type, TypeRawPtr::NOTNULL, MemNode::unordered); - phase->register_new_node(buffer, ctrl); - Node *log_addr = new AddPNode(phase->C->top(), buffer, next_index); - phase->register_new_node(log_addr, ctrl); - Node* log_store = new StorePNode(ctrl, raw_mem, log_addr, adr_type, pre_val, MemNode::unordered); - phase->register_new_node(log_store, ctrl); - // update the index - Node* index_update = new StoreXNode(ctrl, log_store, index_adr, adr_type, next_index, MemNode::unordered); - phase->register_new_node(index_update, ctrl); - - // Fast-path case - region2->init_req(_fast_path, ctrl); - phi2->init_req(_fast_path, index_update); - - ctrl = full; - - Node* base = find_bottom_mem(ctrl, phase); - - MergeMemNode* mm = MergeMemNode::make(base); - mm->set_memory_at(Compile::AliasIdxRaw, raw_mem); - phase->register_new_node(mm, ctrl); - - Node* call = new CallLeafNode(ShenandoahBarrierSetC2::write_ref_field_pre_entry_Type(), CAST_FROM_FN_PTR(address, ShenandoahRuntime::write_ref_field_pre_entry), "shenandoah_wb_pre", TypeRawPtr::BOTTOM); - call->init_req(TypeFunc::Control, ctrl); - call->init_req(TypeFunc::I_O, phase->C->top()); - call->init_req(TypeFunc::Memory, mm); - call->init_req(TypeFunc::FramePtr, phase->C->top()); - call->init_req(TypeFunc::ReturnAdr, phase->C->top()); - call->init_req(TypeFunc::Parms, pre_val); - call->init_req(TypeFunc::Parms+1, thread); - phase->register_control(call, loop, ctrl); - - Node* ctrl_proj = new ProjNode(call, TypeFunc::Control); - phase->register_control(ctrl_proj, loop, call); - Node* mem_proj = new ProjNode(call, TypeFunc::Memory); - phase->register_new_node(mem_proj, call); - - // Slow-path case - region2->init_req(_slow_path, ctrl_proj); - phi2->init_req(_slow_path, mem_proj); - - phase->register_control(region2, loop, reg2_ctrl); - phase->register_new_node(phi2, region2); - - region->init_req(_heap_unstable, region2); - phi->init_req(_heap_unstable, phi2); - - phase->register_control(region, loop, heap_stable_ctrl->in(0)); - phase->register_new_node(phi, region); - - fix_ctrl(barrier, region, fixer, uses, uses_to_ignore, last, phase); - for(uint next = 0; next < uses.size(); next++ ) { - Node *n = uses.at(next); - assert(phase->get_ctrl(n) == init_ctrl, "bad control"); - assert(n != init_raw_mem, "should leave input raw mem above the barrier"); - phase->set_ctrl(n, region); - follow_barrier_uses(n, init_ctrl, uses, phase); - } - fixer.fix_mem(init_ctrl, region, init_raw_mem, raw_mem_for_ctrl, phi, uses); - - phase->igvn().replace_node(barrier, pre_val); - } - assert(state->iu_barriers_count() == 0, "all enqueue barrier nodes should have been replaced"); - } Node* ShenandoahBarrierC2Support::get_load_addr(PhaseIdealLoop* phase, VectorSet& visited, Node* in) { @@ -1662,8 +1478,6 @@ Node* ShenandoahBarrierC2Support::get_load_addr(PhaseIdealLoop* phase, VectorSet } case Op_ShenandoahLoadReferenceBarrier: return get_load_addr(phase, visited, in->in(ShenandoahLoadReferenceBarrierNode::ValueIn)); - case Op_ShenandoahIUBarrier: - return get_load_addr(phase, visited, in->in(1)); case Op_CallDynamicJava: case Op_CallLeaf: case Op_CallStaticJava: @@ -1911,126 +1725,6 @@ void ShenandoahBarrierC2Support::optimize_after_expansion(VectorSet &visited, No } } -ShenandoahIUBarrierNode::ShenandoahIUBarrierNode(Node* val) : Node(nullptr, val) { - ShenandoahBarrierSetC2::bsc2()->state()->add_iu_barrier(this); -} - -const Type* ShenandoahIUBarrierNode::bottom_type() const { - if (in(1) == nullptr || in(1)->is_top()) { - return Type::TOP; - } - const Type* t = in(1)->bottom_type(); - if (t == TypePtr::NULL_PTR) { - return t; - } - return t->is_oopptr(); -} - -const Type* ShenandoahIUBarrierNode::Value(PhaseGVN* phase) const { - if (in(1) == nullptr) { - return Type::TOP; - } - const Type* t = phase->type(in(1)); - if (t == Type::TOP) { - return Type::TOP; - } - if (t == TypePtr::NULL_PTR) { - return t; - } - return t->is_oopptr(); -} - -int ShenandoahIUBarrierNode::needed(Node* n) { - if (n == nullptr || - n->is_Allocate() || - n->Opcode() == Op_ShenandoahIUBarrier || - n->bottom_type() == TypePtr::NULL_PTR || - (n->bottom_type()->make_oopptr() != nullptr && n->bottom_type()->make_oopptr()->const_oop() != nullptr)) { - return NotNeeded; - } - if (n->is_Phi() || - n->is_CMove()) { - return MaybeNeeded; - } - return Needed; -} - -Node* ShenandoahIUBarrierNode::next(Node* n) { - for (;;) { - if (n == nullptr) { - return n; - } else if (n->bottom_type() == TypePtr::NULL_PTR) { - return n; - } else if (n->bottom_type()->make_oopptr() != nullptr && n->bottom_type()->make_oopptr()->const_oop() != nullptr) { - return n; - } else if (n->is_ConstraintCast() || - n->Opcode() == Op_DecodeN || - n->Opcode() == Op_EncodeP) { - n = n->in(1); - } else if (n->is_Proj()) { - n = n->in(0); - } else { - return n; - } - } - ShouldNotReachHere(); - return nullptr; -} - -Node* ShenandoahIUBarrierNode::Identity(PhaseGVN* phase) { - PhaseIterGVN* igvn = phase->is_IterGVN(); - - Node* n = next(in(1)); - - int cont = needed(n); - - if (cont == NotNeeded) { - return in(1); - } else if (cont == MaybeNeeded) { - if (igvn == nullptr) { - phase->record_for_igvn(this); - return this; - } else { - ResourceMark rm; - Unique_Node_List wq; - uint wq_i = 0; - - for (;;) { - if (n->is_Phi()) { - for (uint i = 1; i < n->req(); i++) { - Node* m = n->in(i); - if (m != nullptr) { - wq.push(m); - } - } - } else { - assert(n->is_CMove(), "nothing else here"); - Node* m = n->in(CMoveNode::IfFalse); - wq.push(m); - m = n->in(CMoveNode::IfTrue); - wq.push(m); - } - Node* orig_n = nullptr; - do { - if (wq_i >= wq.size()) { - return in(1); - } - n = wq.at(wq_i); - wq_i++; - orig_n = n; - n = next(n); - cont = needed(n); - if (cont == Needed) { - return this; - } - } while (cont != MaybeNeeded || (orig_n != n && wq.member(n))); - } - } - } - - return this; -} - #ifdef ASSERT static bool has_never_branch(Node* root) { for (uint i = 1; i < root->req(); i++) { @@ -3014,8 +2708,6 @@ bool ShenandoahLoadReferenceBarrierNode::needs_barrier_impl(PhaseGVN* phase, Nod case Op_CMoveP: return needs_barrier_impl(phase, n->in(2), visited) || needs_barrier_impl(phase, n->in(3), visited); - case Op_ShenandoahIUBarrier: - return needs_barrier_impl(phase, n->in(1), visited); case Op_CreateEx: return false; default: diff --git a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.hpp b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.hpp index 7a6ed74f563..164502e6358 100644 --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.hpp +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.hpp @@ -87,23 +87,6 @@ class ShenandoahBarrierC2Support : public AllStatic { #endif }; -class ShenandoahIUBarrierNode : public Node { -public: - ShenandoahIUBarrierNode(Node* val); - - const Type *bottom_type() const; - const Type* Value(PhaseGVN* phase) const; - Node* Identity(PhaseGVN* phase); - - int Opcode() const; - -private: - enum { Needed, NotNeeded, MaybeNeeded }; - - static int needed(Node* n); - static Node* next(Node* n); -}; - class MemoryGraphFixer : public ResourceObj { private: Node_List _memory_nodes; diff --git a/src/hotspot/share/gc/shenandoah/mode/shenandoahIUMode.cpp b/src/hotspot/share/gc/shenandoah/mode/shenandoahIUMode.cpp deleted file mode 100644 index 7a1cf1bbbdf..00000000000 --- a/src/hotspot/share/gc/shenandoah/mode/shenandoahIUMode.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2020, 2022, Red Hat, Inc. 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. - * - */ - -#include "precompiled.hpp" -#include "gc/shenandoah/heuristics/shenandoahAdaptiveHeuristics.hpp" -#include "gc/shenandoah/heuristics/shenandoahAggressiveHeuristics.hpp" -#include "gc/shenandoah/heuristics/shenandoahCompactHeuristics.hpp" -#include "gc/shenandoah/heuristics/shenandoahStaticHeuristics.hpp" -#include "gc/shenandoah/mode/shenandoahIUMode.hpp" -#include "gc/shenandoah/shenandoahHeap.inline.hpp" -#include "logging/log.hpp" -#include "logging/logTag.hpp" -#include "runtime/globals_extension.hpp" -#include "runtime/java.hpp" - -void ShenandoahIUMode::initialize_flags() const { - if (FLAG_IS_CMDLINE(ClassUnloadingWithConcurrentMark) && ClassUnloading) { - log_warning(gc)("Shenandoah I-U mode sets -XX:-ClassUnloadingWithConcurrentMark; see JDK-8261341 for details"); - } - FLAG_SET_DEFAULT(ClassUnloadingWithConcurrentMark, false); - - if (ClassUnloading) { - FLAG_SET_DEFAULT(VerifyBeforeExit, false); - } - - if (FLAG_IS_DEFAULT(ShenandoahIUBarrier)) { - FLAG_SET_DEFAULT(ShenandoahIUBarrier, true); - } - if (FLAG_IS_DEFAULT(ShenandoahSATBBarrier)) { - FLAG_SET_DEFAULT(ShenandoahSATBBarrier, false); - } - - SHENANDOAH_ERGO_ENABLE_FLAG(ExplicitGCInvokesConcurrent); - SHENANDOAH_ERGO_ENABLE_FLAG(ShenandoahImplicitGCInvokesConcurrent); - - // Final configuration checks - SHENANDOAH_CHECK_FLAG_SET(ShenandoahLoadRefBarrier); - SHENANDOAH_CHECK_FLAG_UNSET(ShenandoahSATBBarrier); - SHENANDOAH_CHECK_FLAG_SET(ShenandoahIUBarrier); - SHENANDOAH_CHECK_FLAG_SET(ShenandoahCASBarrier); - SHENANDOAH_CHECK_FLAG_SET(ShenandoahCloneBarrier); - SHENANDOAH_CHECK_FLAG_SET(ShenandoahStackWatermarkBarrier); -} - -ShenandoahHeuristics* ShenandoahIUMode::initialize_heuristics() const { - if (ShenandoahGCHeuristics == nullptr) { - vm_exit_during_initialization("Unknown -XX:ShenandoahGCHeuristics option (null)"); - } - ShenandoahHeap* heap = ShenandoahHeap::heap(); - if (strcmp(ShenandoahGCHeuristics, "aggressive") == 0) { - return new ShenandoahAggressiveHeuristics(heap); - } else if (strcmp(ShenandoahGCHeuristics, "static") == 0) { - return new ShenandoahStaticHeuristics(heap); - } else if (strcmp(ShenandoahGCHeuristics, "adaptive") == 0) { - return new ShenandoahAdaptiveHeuristics(heap); - } else if (strcmp(ShenandoahGCHeuristics, "compact") == 0) { - return new ShenandoahCompactHeuristics(heap); - } - vm_exit_during_initialization("Unknown -XX:ShenandoahGCHeuristics option"); - return nullptr; -} diff --git a/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.cpp b/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.cpp index 39eb475a75d..9b83df5424d 100644 --- a/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.cpp +++ b/src/hotspot/share/gc/shenandoah/mode/shenandoahPassiveMode.cpp @@ -47,7 +47,6 @@ void ShenandoahPassiveMode::initialize_flags() const { // Disable known barriers by default. SHENANDOAH_ERGO_DISABLE_FLAG(ShenandoahLoadRefBarrier); SHENANDOAH_ERGO_DISABLE_FLAG(ShenandoahSATBBarrier); - SHENANDOAH_ERGO_DISABLE_FLAG(ShenandoahIUBarrier); SHENANDOAH_ERGO_DISABLE_FLAG(ShenandoahCASBarrier); SHENANDOAH_ERGO_DISABLE_FLAG(ShenandoahCloneBarrier); SHENANDOAH_ERGO_DISABLE_FLAG(ShenandoahStackWatermarkBarrier); diff --git a/src/hotspot/share/gc/shenandoah/mode/shenandoahSATBMode.cpp b/src/hotspot/share/gc/shenandoah/mode/shenandoahSATBMode.cpp index 2a456012656..c3ea11cf71e 100644 --- a/src/hotspot/share/gc/shenandoah/mode/shenandoahSATBMode.cpp +++ b/src/hotspot/share/gc/shenandoah/mode/shenandoahSATBMode.cpp @@ -44,7 +44,6 @@ void ShenandoahSATBMode::initialize_flags() const { // Final configuration checks SHENANDOAH_CHECK_FLAG_SET(ShenandoahLoadRefBarrier); - SHENANDOAH_CHECK_FLAG_UNSET(ShenandoahIUBarrier); SHENANDOAH_CHECK_FLAG_SET(ShenandoahSATBBarrier); SHENANDOAH_CHECK_FLAG_SET(ShenandoahCASBarrier); SHENANDOAH_CHECK_FLAG_SET(ShenandoahCloneBarrier); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp b/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp index d9b28c1bb4d..bcc370eeb31 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahArguments.cpp @@ -48,7 +48,6 @@ void ShenandoahArguments::initialize() { FLAG_SET_DEFAULT(ShenandoahSATBBarrier, false); FLAG_SET_DEFAULT(ShenandoahLoadRefBarrier, false); - FLAG_SET_DEFAULT(ShenandoahIUBarrier, false); FLAG_SET_DEFAULT(ShenandoahCASBarrier, false); FLAG_SET_DEFAULT(ShenandoahCloneBarrier, false); @@ -142,7 +141,6 @@ void ShenandoahArguments::initialize() { if (ShenandoahVerifyOptoBarriers && (!FLAG_IS_DEFAULT(ShenandoahSATBBarrier) || !FLAG_IS_DEFAULT(ShenandoahLoadRefBarrier) || - !FLAG_IS_DEFAULT(ShenandoahIUBarrier) || !FLAG_IS_DEFAULT(ShenandoahCASBarrier) || !FLAG_IS_DEFAULT(ShenandoahCloneBarrier) )) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp index beb4a1d2892..5215aa749ae 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp @@ -250,6 +250,25 @@ void ShenandoahAsserts::assert_correct(void* interior_loc, oop obj, const char* file, line); } } + + // Do additional checks for special objects: their fields can hold metadata as well. + // We want to check class loading/unloading did not corrupt them. + + if (java_lang_Class::is_instance(obj)) { + Metadata* klass = obj->metadata_field(java_lang_Class::klass_offset()); + if (klass != nullptr && !Metaspace::contains(klass)) { + print_failure(_safe_all, obj, interior_loc, nullptr, "Shenandoah assert_correct failed", + "Instance class mirror should point to Metaspace", + file, line); + } + + Metadata* array_klass = obj->metadata_field(java_lang_Class::array_klass_offset()); + if (array_klass != nullptr && !Metaspace::contains(array_klass)) { + print_failure(_safe_all, obj, interior_loc, nullptr, "Shenandoah assert_correct failed", + "Array class mirror should point to Metaspace", + file, line); + } + } } void ShenandoahAsserts::assert_in_correct_region(void* interior_loc, oop obj, const char* file, int line) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp index d2857daccf6..40b6d70ce45 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp @@ -138,7 +138,7 @@ void ShenandoahBarrierSet::on_thread_detach(Thread *thread) { } void ShenandoahBarrierSet::clone_barrier_runtime(oop src) { - if (_heap->has_forwarded_objects() || (ShenandoahIUBarrier && _heap->is_concurrent_mark_in_progress())) { + if (_heap->has_forwarded_objects()) { clone_barrier(src); } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp index beb6ae492b8..e116e0225db 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp @@ -89,7 +89,6 @@ class ShenandoahBarrierSet: public BarrierSet { template inline void satb_barrier(T* field); inline void satb_enqueue(oop value); - inline void iu_barrier(oop obj); inline void keep_alive_if_weak(DecoratorSet decorators, oop value); @@ -120,7 +119,6 @@ class ShenandoahBarrierSet: public BarrierSet { template inline void arraycopy_update(T* src, size_t count); - inline void clone_marking(oop src); inline void clone_evacuation(oop src); inline void clone_update(oop src); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp index bfb69c38c33..80735f453c6 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp @@ -164,12 +164,6 @@ inline void ShenandoahBarrierSet::satb_enqueue(oop value) { } } -inline void ShenandoahBarrierSet::iu_barrier(oop obj) { - if (ShenandoahIUBarrier && obj != nullptr && _heap->is_concurrent_mark_in_progress()) { - enqueue(obj); - } -} - inline void ShenandoahBarrierSet::keep_alive_if_weak(DecoratorSet decorators, oop value) { assert((decorators & ON_UNKNOWN_OOP_REF) == 0, "Reference strength must be known"); const bool on_strong_oop_ref = (decorators & ON_STRONG_OOP_REF) != 0; @@ -189,7 +183,6 @@ inline oop ShenandoahBarrierSet::oop_load(DecoratorSet decorators, T* addr) { template inline oop ShenandoahBarrierSet::oop_cmpxchg(DecoratorSet decorators, T* addr, oop compare_value, oop new_value) { - iu_barrier(new_value); oop res; oop expected = compare_value; do { @@ -207,7 +200,6 @@ inline oop ShenandoahBarrierSet::oop_cmpxchg(DecoratorSet decorators, T* addr, o template inline oop ShenandoahBarrierSet::oop_xchg(DecoratorSet decorators, T* addr, oop new_value) { - iu_barrier(new_value); oop previous = RawAccess<>::oop_atomic_xchg(addr, new_value); // Note: We don't need a keep-alive-barrier here. We already enqueue any loaded reference for SATB anyway, // because it must be the previous value. @@ -245,7 +237,6 @@ inline void ShenandoahBarrierSet::AccessBarrier::oop_st shenandoah_assert_marked_if(nullptr, value, !CompressedOops::is_null(value) && ShenandoahHeap::heap()->is_evacuation_in_progress()); shenandoah_assert_not_in_cset_if(addr, value, value != nullptr && !ShenandoahHeap::heap()->cancelled_gc()); ShenandoahBarrierSet* const bs = ShenandoahBarrierSet::barrier_set(); - bs->iu_barrier(value); bs->satb_barrier(addr); Raw::oop_store(addr, value); } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetClone.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetClone.inline.hpp index 13371f5e194..3b7bf9864de 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetClone.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSetClone.inline.hpp @@ -74,15 +74,6 @@ class ShenandoahUpdateRefsForOopClosure: public BasicOopIterateClosure { virtual void do_oop(narrowOop* p) { do_oop_work(p); } }; -void ShenandoahBarrierSet::clone_marking(oop obj) { - assert(_heap->is_concurrent_mark_in_progress(), "only during marking"); - assert(ShenandoahIUBarrier, "only with incremental-update"); - if (!_heap->marking_context()->allocated_after_mark_start(obj)) { - ShenandoahUpdateRefsForOopClosure cl; - obj->oop_iterate(&cl); - } -} - void ShenandoahBarrierSet::clone_evacuation(oop obj) { assert(_heap->is_evacuation_in_progress(), "only during evacuation"); if (need_bulk_update(cast_from_oop(obj))) { @@ -105,9 +96,7 @@ void ShenandoahBarrierSet::clone_barrier(oop obj) { shenandoah_assert_correct(nullptr, obj); int gc_state = _heap->gc_state(); - if ((gc_state & ShenandoahHeap::MARKING) != 0) { - clone_marking(obj); - } else if ((gc_state & ShenandoahHeap::EVACUATION) != 0) { + if ((gc_state & ShenandoahHeap::EVACUATION) != 0) { clone_evacuation(obj); } else { clone_update(obj); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp index e61dca2bdbf..657cd681f4f 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahCodeRoots.cpp @@ -103,11 +103,6 @@ class ShenandoahDisarmNMethodsTask : public WorkerTask { WorkerTask("Shenandoah Disarm NMethods"), _iterator(ShenandoahCodeRoots::table()) { assert(SafepointSynchronize::is_at_safepoint(), "Only at a safepoint"); - _iterator.nmethods_do_begin(); - } - - ~ShenandoahDisarmNMethodsTask() { - _iterator.nmethods_do_end(); } virtual void work(uint worker_id) { @@ -175,13 +170,7 @@ class ShenandoahUnlinkTask : public WorkerTask { ShenandoahUnlinkTask(bool unloading_occurred) : WorkerTask("Shenandoah Unlink NMethods"), _cl(unloading_occurred), - _iterator(ShenandoahCodeRoots::table()) { - _iterator.nmethods_do_begin(); - } - - ~ShenandoahUnlinkTask() { - _iterator.nmethods_do_end(); - } + _iterator(ShenandoahCodeRoots::table()) {} virtual void work(uint worker_id) { _iterator.nmethods_do(&_cl); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp index aea0af24575..ac2e732052a 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentGC.cpp @@ -767,16 +767,9 @@ class ShenandoahConcurrentWeakRootsEvacUpdateTask : public WorkerTask { _vm_roots(phase), _cld_roots(phase, ShenandoahHeap::heap()->workers()->active_workers(), false /*heap iteration*/), _nmethod_itr(ShenandoahCodeRoots::table()), - _phase(phase) { - if (ShenandoahHeap::heap()->unload_classes()) { - _nmethod_itr.nmethods_do_begin(); - } - } + _phase(phase) {} ~ShenandoahConcurrentWeakRootsEvacUpdateTask() { - if (ShenandoahHeap::heap()->unload_classes()) { - _nmethod_itr.nmethods_do_end(); - } // Notify runtime data structures of potentially dead oops _vm_roots.report_num_dead(); } @@ -878,17 +871,7 @@ class ShenandoahConcurrentRootsEvacUpdateTask : public WorkerTask { _phase(phase), _vm_roots(phase), _cld_roots(phase, ShenandoahHeap::heap()->workers()->active_workers(), false /*heap iteration*/), - _nmethod_itr(ShenandoahCodeRoots::table()) { - if (!ShenandoahHeap::heap()->unload_classes()) { - _nmethod_itr.nmethods_do_begin(); - } - } - - ~ShenandoahConcurrentRootsEvacUpdateTask() { - if (!ShenandoahHeap::heap()->unload_classes()) { - _nmethod_itr.nmethods_do_end(); - } - } + _nmethod_itr(ShenandoahCodeRoots::table()) {} void work(uint worker_id) { ShenandoahConcurrentWorkerSession worker_session(worker_id); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp index fa37fcbcfd3..31157616e0e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahConcurrentMark.cpp @@ -71,22 +71,14 @@ class ShenandoahConcurrentMarkingTask : public WorkerTask { class ShenandoahSATBAndRemarkThreadsClosure : public ThreadClosure { private: SATBMarkQueueSet& _satb_qset; - OopClosure* const _cl; public: - ShenandoahSATBAndRemarkThreadsClosure(SATBMarkQueueSet& satb_qset, OopClosure* cl) : - _satb_qset(satb_qset), - _cl(cl) {} + explicit ShenandoahSATBAndRemarkThreadsClosure(SATBMarkQueueSet& satb_qset) : + _satb_qset(satb_qset) {} - void do_thread(Thread* thread) { + void do_thread(Thread* thread) override { // Transfer any partial buffer to the qset for completed buffer processing. _satb_qset.flush_queue(ShenandoahThreadLocalData::satb_mark_queue(thread)); - if (thread->is_Java_thread()) { - if (_cl != nullptr) { - ResourceMark rm; - thread->oops_do(_cl, nullptr); - } - } } }; @@ -118,9 +110,7 @@ class ShenandoahFinalMarkingTask : public WorkerTask { while (satb_mq_set.apply_closure_to_completed_buffer(&cl)) {} assert(!heap->has_forwarded_objects(), "Not expected"); - ShenandoahMarkRefsClosure mark_cl(q, rp); - ShenandoahSATBAndRemarkThreadsClosure tc(satb_mq_set, - ShenandoahIUBarrier ? &mark_cl : nullptr); + ShenandoahSATBAndRemarkThreadsClosure tc(satb_mq_set); Threads::possibly_parallel_threads_do(true /* is_par */, &tc); } _cm->mark_loop(worker_id, _terminator, rp, GENERATION, false /*not cancellable*/, diff --git a/src/hotspot/share/gc/shenandoah/shenandoahController.hpp b/src/hotspot/share/gc/shenandoah/shenandoahController.hpp index 2808f30b444..6c28ff4e969 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahController.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahController.hpp @@ -22,8 +22,8 @@ * */ -#ifndef LINUX_X86_64_SERVER_SLOWDEBUG_SHENANDOAHCONTROLLER_HPP -#define LINUX_X86_64_SERVER_SLOWDEBUG_SHENANDOAHCONTROLLER_HPP +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHCONTROLLER_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHCONTROLLER_HPP #include "gc/shared/gcCause.hpp" #include "gc/shared/concurrentGCThread.hpp" @@ -102,4 +102,4 @@ class ShenandoahController: public ConcurrentGCThread { size_t get_gc_id(); void update_gc_id(); }; -#endif //LINUX_X86_64_SERVER_SLOWDEBUG_SHENANDOAHCONTROLLER_HPP +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHCONTROLLER_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp index 582c0d18d3e..b47bb109031 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahFreeSet.cpp @@ -48,19 +48,19 @@ static const char* partition_name(ShenandoahFreeSetPartitionId t) { #ifndef PRODUCT void ShenandoahRegionPartitions::dump_bitmap() const { - log_info(gc)("Mutator range [" SSIZE_FORMAT ", " SSIZE_FORMAT "], Collector range [" SSIZE_FORMAT ", " SSIZE_FORMAT "]", - _leftmosts[int(ShenandoahFreeSetPartitionId::Mutator)], - _rightmosts[int(ShenandoahFreeSetPartitionId::Mutator)], - _leftmosts[int(ShenandoahFreeSetPartitionId::Collector)], - _rightmosts[int(ShenandoahFreeSetPartitionId::Collector)]); - log_info(gc)("Empty Mutator range [" SSIZE_FORMAT ", " SSIZE_FORMAT - "], Empty Collector range [" SSIZE_FORMAT ", " SSIZE_FORMAT "]", - _leftmosts_empty[int(ShenandoahFreeSetPartitionId::Mutator)], - _rightmosts_empty[int(ShenandoahFreeSetPartitionId::Mutator)], - _leftmosts_empty[int(ShenandoahFreeSetPartitionId::Collector)], - _rightmosts_empty[int(ShenandoahFreeSetPartitionId::Collector)]); - - log_info(gc)("%6s: %18s %18s %18s", "index", "Mutator Bits", "Collector Bits", "NotFree Bits"); + log_debug(gc)("Mutator range [" SSIZE_FORMAT ", " SSIZE_FORMAT "], Collector range [" SSIZE_FORMAT ", " SSIZE_FORMAT "]", + _leftmosts[int(ShenandoahFreeSetPartitionId::Mutator)], + _rightmosts[int(ShenandoahFreeSetPartitionId::Mutator)], + _leftmosts[int(ShenandoahFreeSetPartitionId::Collector)], + _rightmosts[int(ShenandoahFreeSetPartitionId::Collector)]); + log_debug(gc)("Empty Mutator range [" SSIZE_FORMAT ", " SSIZE_FORMAT + "], Empty Collector range [" SSIZE_FORMAT ", " SSIZE_FORMAT "]", + _leftmosts_empty[int(ShenandoahFreeSetPartitionId::Mutator)], + _rightmosts_empty[int(ShenandoahFreeSetPartitionId::Mutator)], + _leftmosts_empty[int(ShenandoahFreeSetPartitionId::Collector)], + _rightmosts_empty[int(ShenandoahFreeSetPartitionId::Collector)]); + + log_debug(gc)("%6s: %18s %18s %18s", "index", "Mutator Bits", "Collector Bits", "NotFree Bits"); dump_bitmap_range(0, _max-1); } @@ -83,8 +83,8 @@ void ShenandoahRegionPartitions::dump_bitmap_row(idx_t region_idx) const { uintx collector_bits = _membership[int(ShenandoahFreeSetPartitionId::Collector)].bits_at(aligned_idx); uintx free_bits = mutator_bits | collector_bits; uintx notfree_bits = ~free_bits; - log_info(gc)(SSIZE_FORMAT_W(6) ": " SIZE_FORMAT_X_0 " 0x" SIZE_FORMAT_X_0 " 0x" SIZE_FORMAT_X_0, - aligned_idx, mutator_bits, collector_bits, notfree_bits); + log_debug(gc)(SSIZE_FORMAT_W(6) ": " SIZE_FORMAT_X_0 " 0x" SIZE_FORMAT_X_0 " 0x" SIZE_FORMAT_X_0, + aligned_idx, mutator_bits, collector_bits, notfree_bits); } #endif @@ -1060,8 +1060,8 @@ void ShenandoahFreeSet::move_regions_from_collector_to_mutator(size_t max_xfer_r } size_t collector_xfer = collector_empty_xfer + collector_not_empty_xfer; - log_info(gc)("At start of update refs, moving " SIZE_FORMAT "%s to Mutator free partition from Collector Reserve", - byte_size_in_proper_unit(collector_xfer), proper_unit_for_byte_size(collector_xfer)); + log_info(gc, ergo)("At start of update refs, moving " SIZE_FORMAT "%s to Mutator free partition from Collector Reserve", + byte_size_in_proper_unit(collector_xfer), proper_unit_for_byte_size(collector_xfer)); } void ShenandoahFreeSet::prepare_to_rebuild(size_t &cset_regions) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp index f93ca9d6d7d..eb6de9719dc 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp @@ -66,7 +66,6 @@ #include "gc/shenandoah/shenandoahVMOperations.hpp" #include "gc/shenandoah/shenandoahWorkGroup.hpp" #include "gc/shenandoah/shenandoahWorkerPolicy.hpp" -#include "gc/shenandoah/mode/shenandoahIUMode.hpp" #include "gc/shenandoah/mode/shenandoahPassiveMode.hpp" #include "gc/shenandoah/mode/shenandoahSATBMode.hpp" #if INCLUDE_JFR @@ -446,8 +445,6 @@ void ShenandoahHeap::initialize_mode() { if (ShenandoahGCMode != nullptr) { if (strcmp(ShenandoahGCMode, "satb") == 0) { _gc_mode = new ShenandoahSATBMode(); - } else if (strcmp(ShenandoahGCMode, "iu") == 0) { - _gc_mode = new ShenandoahIUMode(); } else if (strcmp(ShenandoahGCMode, "passive") == 0) { _gc_mode = new ShenandoahPassiveMode(); } else { @@ -1693,19 +1690,20 @@ class ShenandoahParallelHeapRegionTask : public WorkerTask { private: ShenandoahHeap* const _heap; ShenandoahHeapRegionClosure* const _blk; + size_t const _stride; shenandoah_padding(0); volatile size_t _index; shenandoah_padding(1); public: - ShenandoahParallelHeapRegionTask(ShenandoahHeapRegionClosure* blk) : + ShenandoahParallelHeapRegionTask(ShenandoahHeapRegionClosure* blk, size_t stride) : WorkerTask("Shenandoah Parallel Region Operation"), - _heap(ShenandoahHeap::heap()), _blk(blk), _index(0) {} + _heap(ShenandoahHeap::heap()), _blk(blk), _stride(stride), _index(0) {} void work(uint worker_id) { ShenandoahParallelWorkerSession worker_session(worker_id); - size_t stride = ShenandoahParallelRegionStride; + size_t stride = _stride; size_t max = _heap->num_regions(); while (Atomic::load(&_index) < max) { @@ -1724,8 +1722,20 @@ class ShenandoahParallelHeapRegionTask : public WorkerTask { void ShenandoahHeap::parallel_heap_region_iterate(ShenandoahHeapRegionClosure* blk) const { assert(blk->is_thread_safe(), "Only thread-safe closures here"); - if (num_regions() > ShenandoahParallelRegionStride) { - ShenandoahParallelHeapRegionTask task(blk); + const uint active_workers = workers()->active_workers(); + const size_t n_regions = num_regions(); + size_t stride = ShenandoahParallelRegionStride; + if (stride == 0 && active_workers > 1) { + // Automatically derive the stride to balance the work between threads + // evenly. Do not try to split work if below the reasonable threshold. + constexpr size_t threshold = 4096; + stride = n_regions <= threshold ? + threshold : + (n_regions + active_workers - 1) / active_workers; + } + + if (n_regions > stride && active_workers > 1) { + ShenandoahParallelHeapRegionTask task(blk, stride); workers()->run_task(&task); } else { heap_region_iterate(blk); diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.hpp index 40f48bae6f5..06ea6a8e232 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.hpp @@ -23,8 +23,8 @@ * */ -#ifndef SHARE_VM_GC_SHENANDOAH_SHENANDOAHMARKBITMAP_HPP -#define SHARE_VM_GC_SHENANDOAH_SHENANDOAHMARKBITMAP_HPP +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHMARKBITMAP_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHMARKBITMAP_HPP #include "memory/memRegion.hpp" #include "runtime/atomic.hpp" @@ -176,4 +176,4 @@ class ShenandoahMarkBitMap { }; -#endif // SHARE_VM_GC_SHENANDOAH_SHENANDOAHMARKBITMAP_HPP +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHMARKBITMAP_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.inline.hpp b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.inline.hpp index 62c0eaea139..bf9261f47ac 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.inline.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahMarkBitMap.inline.hpp @@ -23,8 +23,8 @@ * */ -#ifndef SHARE_VM_GC_SHENANDOAH_SHENANDOAHMARKBITMAP_INLINE_HPP -#define SHARE_VM_GC_SHENANDOAH_SHENANDOAHMARKBITMAP_INLINE_HPP +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHMARKBITMAP_INLINE_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHMARKBITMAP_INLINE_HPP #include "gc/shenandoah/shenandoahMarkBitMap.hpp" @@ -205,4 +205,4 @@ inline void ShenandoahMarkBitMap::clear_range_of_words(idx_t beg, idx_t end) { } -#endif // SHARE_VM_GC_SHENANDOAH_SHENANDOAHMARKBITMAP_INLINE_HPP +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHMARKBITMAP_INLINE_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp index 54efd658eaf..5eb0b277b5e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp @@ -31,6 +31,7 @@ #include "gc/shenandoah/shenandoahOopClosures.inline.hpp" #include "memory/resourceArea.hpp" #include "runtime/continuation.hpp" +#include "runtime/safepointVerifiers.hpp" ShenandoahNMethod::ShenandoahNMethod(nmethod* nm, GrowableArray& oops, bool non_immediate_oops) : _nm(nm), _oops(nullptr), _oops_count(0), _unregistered(false), _lock(), _ic_lock() { @@ -475,21 +476,40 @@ void ShenandoahNMethodTableSnapshot::concurrent_nmethods_do(NMethodClosure* cl) } ShenandoahConcurrentNMethodIterator::ShenandoahConcurrentNMethodIterator(ShenandoahNMethodTable* table) : - _table(table), _table_snapshot(nullptr) { -} - -void ShenandoahConcurrentNMethodIterator::nmethods_do_begin() { - MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); - _table_snapshot = _table->snapshot_for_iteration(); -} + _table(table), + _table_snapshot(nullptr), + _started_workers(0), + _finished_workers(0) {} void ShenandoahConcurrentNMethodIterator::nmethods_do(NMethodClosure* cl) { - assert(_table_snapshot != nullptr, "Must first call nmethod_do_begin()"); - _table_snapshot->concurrent_nmethods_do(cl); -} + // Cannot safepoint when iteration is running, because this can cause deadlocks + // with other threads waiting on iteration to be over. + NoSafepointVerifier nsv; -void ShenandoahConcurrentNMethodIterator::nmethods_do_end() { - MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); - _table->finish_iteration(_table_snapshot); - CodeCache_lock->notify_all(); + MutexLocker ml(CodeCache_lock, Mutex::_no_safepoint_check_flag); + + if (_finished_workers > 0) { + // Some threads have already finished. We are now in rampdown: we are now + // waiting for all currently recorded workers to finish. No new workers + // should start. + return; + } + + // Record a new worker and initialize the snapshot if it is a first visitor. + if (_started_workers++ == 0) { + _table_snapshot = _table->snapshot_for_iteration(); + } + + // All set, relinquish the lock and go concurrent. + { + MutexUnlocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + _table_snapshot->concurrent_nmethods_do(cl); + } + + // Record completion. Last worker shuts down the iterator and notifies any waiters. + uint count = ++_finished_workers; + if (count == _started_workers) { + _table->finish_iteration(_table_snapshot); + CodeCache_lock->notify_all(); + } } diff --git a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp index 4e05d254db3..c1595396500 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahNMethod.hpp @@ -181,13 +181,13 @@ class ShenandoahConcurrentNMethodIterator { private: ShenandoahNMethodTable* const _table; ShenandoahNMethodTableSnapshot* _table_snapshot; + uint _started_workers; + uint _finished_workers; public: ShenandoahConcurrentNMethodIterator(ShenandoahNMethodTable* table); - void nmethods_do_begin(); void nmethods_do(NMethodClosure* cl); - void nmethods_do_end(); }; #endif // SHARE_GC_SHENANDOAH_SHENANDOAHNMETHOD_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.hpp b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.hpp index c4ecf8d359a..fa523551790 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.hpp @@ -23,8 +23,8 @@ * */ -#ifndef SHARE_VM_GC_SHENANDOAH_SHENANDOAHREFERENCEPROCESSOR_HPP -#define SHARE_VM_GC_SHENANDOAH_SHENANDOAHREFERENCEPROCESSOR_HPP +#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHREFERENCEPROCESSOR_HPP +#define SHARE_GC_SHENANDOAH_SHENANDOAHREFERENCEPROCESSOR_HPP #include "gc/shared/referenceDiscoverer.hpp" #include "gc/shared/referencePolicy.hpp" @@ -188,4 +188,4 @@ class ShenandoahReferenceProcessor : public ReferenceDiscoverer { void abandon_partial_discovery(); }; -#endif // SHARE_VM_GC_SHENANDOAH_SHENANDOAHREFERENCEPROCESSOR_HPP +#endif // SHARE_GC_SHENANDOAH_SHENANDOAHREFERENCEPROCESSOR_HPP diff --git a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp index f67cafdb8fe..694736cea42 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp +++ b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp @@ -212,6 +212,21 @@ class ShenandoahVerifyOopClosure : public BasicOopIterateClosure { fwd_reg = obj_reg; } + // Do additional checks for special objects: their fields can hold metadata as well. + // We want to check class loading/unloading did not corrupt them. + + if (java_lang_Class::is_instance(obj)) { + Metadata* klass = obj->metadata_field(java_lang_Class::klass_offset()); + check(ShenandoahAsserts::_safe_oop, obj, + klass == nullptr || Metaspace::contains(klass), + "Instance class mirror should point to Metaspace"); + + Metadata* array_klass = obj->metadata_field(java_lang_Class::array_klass_offset()); + check(ShenandoahAsserts::_safe_oop, obj, + array_klass == nullptr || Metaspace::contains(array_klass), + "Array class mirror should point to Metaspace"); + } + // ------------ obj and fwd are safe at this point -------------- switch (_options._verify_marked) { diff --git a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp index b8baf6b3ebf..87702afe98e 100644 --- a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp +++ b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp @@ -318,9 +318,10 @@ "checking for cancellation, yielding, etc. Larger values improve "\ "marking performance at expense of responsiveness.") \ \ - product(uintx, ShenandoahParallelRegionStride, 1024, EXPERIMENTAL, \ + product(uintx, ShenandoahParallelRegionStride, 0, EXPERIMENTAL, \ "How many regions to process at once during parallel region " \ - "iteration. Affects heaps with lots of regions.") \ + "iteration. Affects heaps with lots of regions. " \ + "Set to 0 to let Shenandoah to decide the best value.") \ \ product(size_t, ShenandoahSATBBufferSize, 1 * K, EXPERIMENTAL, \ "Number of entries in an SATB log buffer.") \ @@ -333,9 +334,6 @@ product(bool, ShenandoahSATBBarrier, true, DIAGNOSTIC, \ "Turn on/off SATB barriers in Shenandoah") \ \ - product(bool, ShenandoahIUBarrier, false, DIAGNOSTIC, \ - "Turn on/off I-U barriers barriers in Shenandoah") \ - \ product(bool, ShenandoahCASBarrier, true, DIAGNOSTIC, \ "Turn on/off CAS barriers in Shenandoah") \ \ diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h index a18e9bffc85..6082af55487 100644 --- a/src/hotspot/share/include/jvm.h +++ b/src/hotspot/share/include/jvm.h @@ -295,9 +295,6 @@ JVM_HoldsLock(JNIEnv *env, jclass threadClass, jobject obj); JNIEXPORT jobject JNICALL JVM_GetStackTrace(JNIEnv *env, jobject thread); -JNIEXPORT void JNICALL -JVM_DumpAllStacks(JNIEnv *env, jclass unused); - JNIEXPORT jobjectArray JNICALL JVM_GetAllThreads(JNIEnv *env, jclass dummy); diff --git a/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp b/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp index c62257bd23b..79bc97f53ac 100644 --- a/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp +++ b/src/hotspot/share/jvmci/jvmciCodeInstaller.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2024, 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 @@ -664,7 +664,7 @@ JVMCI::CodeInstallResult CodeInstaller::install_runtime_stub(CodeBlob*& cb, GrowableArray *stubs_to_free = nullptr; #ifdef ASSERT const char* val = Arguments::PropertyList_get_value(Arguments::system_properties(), "test.jvmci.forceRuntimeStubAllocFail"); - if (val != nullptr && strstr(name , val) != 0) { + if (val != nullptr && strstr(name , val) != nullptr) { stubs_to_free = new GrowableArray(); JVMCI_event_1("forcing allocation of %s in code cache to fail", name); } diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp index 5583ad8a6f5..5a9b18fc4d3 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp @@ -94,6 +94,12 @@ static void requireInHotSpot(const char* caller, JVMCI_TRAPS) { } } +static void requireNotInHotSpot(const char* caller, JVMCI_TRAPS) { + if (JVMCIENV->is_hotspot()) { + JVMCI_THROW_MSG(IllegalStateException, err_msg("Cannot call %s from HotSpot", caller)); + } +} + class JVMCITraceMark : public StackObj { const char* _msg; public: @@ -418,7 +424,7 @@ C2V_VMENTRY_NULL(jobject, getResolvedJavaMethod, (JNIEnv* env, jobject, jobject C2V_VMENTRY_NULL(jobject, getConstantPool, (JNIEnv* env, jobject, ARGUMENT_PAIR(klass_or_method), jboolean is_klass)) ConstantPool* cp = nullptr; - if (UNPACK_PAIR(address, klass_or_method) == 0) { + if (UNPACK_PAIR(address, klass_or_method) == nullptr) { JVMCI_THROW_NULL(NullPointerException); } if (!is_klass) { @@ -702,6 +708,17 @@ C2V_VMENTRY_NULL(jobject, lookupJClass, (JNIEnv* env, jobject, jlong jclass_valu return JVMCIENV->get_jobject(result); C2V_END +C2V_VMENTRY_0(jlong, getJObjectValue, (JNIEnv* env, jobject, jobject constant_jobject)) + requireNotInHotSpot("getJObjectValue", JVMCI_CHECK_0); + if (!THREAD->has_last_Java_frame()) { + JVMCI_THROW_MSG_0(IllegalStateException, err_msg("Cannot call getJObjectValue without Java frame anchor")); + } + JVMCIObject constant = JVMCIENV->wrap(constant_jobject); + Handle constant_value = JVMCIENV->asConstant(constant, JVMCI_CHECK_0); + jobject jni_handle = JNIHandles::make_local(THREAD, constant_value()); + return reinterpret_cast(jni_handle); +C2V_END + C2V_VMENTRY_NULL(jobject, getUncachedStringInPool, (JNIEnv* env, jobject, ARGUMENT_PAIR(cp), jint index)) constantPoolHandle cp(THREAD, UNPACK_PAIR(ConstantPool, cp)); constantTag tag = cp->tag_at(index); @@ -1081,7 +1098,7 @@ C2V_END C2V_VMENTRY_0(jlong, getMaxCallTargetOffset, (JNIEnv* env, jobject, jlong addr)) address target_addr = (address) addr; - if (target_addr != 0x0) { + if (target_addr != nullptr) { int64_t off_low = (int64_t)target_addr - ((int64_t)CodeCache::low_bound() + sizeof(int)); int64_t off_high = (int64_t)target_addr - ((int64_t)CodeCache::high_bound() + sizeof(int)); return MAX2(ABS(off_low), ABS(off_high)); @@ -3254,6 +3271,7 @@ JNINativeMethod CompilerToVM::methods[] = { {CC "shouldInlineMethod", CC "(" HS_METHOD2 ")Z", FN_PTR(shouldInlineMethod)}, {CC "lookupType", CC "(" STRING HS_KLASS2 "IZ)" HS_RESOLVED_TYPE, FN_PTR(lookupType)}, {CC "lookupJClass", CC "(J)" HS_RESOLVED_TYPE, FN_PTR(lookupJClass)}, + {CC "getJObjectValue", CC "(" OBJECTCONSTANT ")J", FN_PTR(getJObjectValue)}, {CC "getArrayType", CC "(C" HS_KLASS2 ")" HS_KLASS, FN_PTR(getArrayType)}, {CC "lookupClass", CC "(" CLASS ")" HS_RESOLVED_TYPE, FN_PTR(lookupClass)}, {CC "lookupNameInPool", CC "(" HS_CONSTANT_POOL2 "II)" STRING, FN_PTR(lookupNameInPool)}, diff --git a/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp b/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp index f1028b4b2bb..2116133e56e 100644 --- a/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp +++ b/src/hotspot/share/jvmci/jvmciCompilerToVMInit.cpp @@ -240,7 +240,7 @@ void CompilerToVM::Data::initialize(JVMCI_TRAPS) { cardtable_shift = CardTable::card_shift(); } else { // No card mark barriers - cardtable_start_address = 0; + cardtable_start_address = nullptr; cardtable_shift = 0; } diff --git a/src/hotspot/share/jvmci/jvmciRuntime.cpp b/src/hotspot/share/jvmci/jvmciRuntime.cpp index 371a6540898..ad0430787aa 100644 --- a/src/hotspot/share/jvmci/jvmciRuntime.cpp +++ b/src/hotspot/share/jvmci/jvmciRuntime.cpp @@ -628,7 +628,7 @@ JRT_LEAF(oopDesc*, JVMCIRuntime::load_and_clear_exception(JavaThread* thread)) oop exception = thread->exception_oop(); assert(exception != nullptr, "npe"); thread->set_exception_oop(nullptr); - thread->set_exception_pc(0); + thread->set_exception_pc(nullptr); return exception; JRT_END diff --git a/src/hotspot/share/libadt/vectset.cpp b/src/hotspot/share/libadt/vectset.cpp index 9c711a7021b..b0d5d100400 100644 --- a/src/hotspot/share/libadt/vectset.cpp +++ b/src/hotspot/share/libadt/vectset.cpp @@ -48,6 +48,10 @@ void VectorSet::init(Arena* arena) { // Expand the existing set to a bigger size void VectorSet::grow(uint new_word_capacity) { + _nesting.check(_set_arena); // Check if a potential reallocation in the arena is safe + if (new_word_capacity < _size) { + return; // No need to grow + } assert(new_word_capacity < (1U << 30), ""); uint x = next_power_of_2(new_word_capacity); if (x > _data_size) { diff --git a/src/hotspot/share/libadt/vectset.hpp b/src/hotspot/share/libadt/vectset.hpp index d55837c3b2b..eafa60db1fe 100644 --- a/src/hotspot/share/libadt/vectset.hpp +++ b/src/hotspot/share/libadt/vectset.hpp @@ -42,10 +42,11 @@ class VectorSet : public AnyObj { // Used 32-bit words uint _size; - uint32_t* _data; // Allocated words uint _data_size; + uint32_t* _data; Arena* _set_arena; + ReallocMark _nesting; // Safety checks for arena reallocation void init(Arena* arena); // Grow vector to required word capacity @@ -77,10 +78,7 @@ class VectorSet : public AnyObj { // bool test_set(uint elem) { uint32_t word = elem >> word_bits; - if (word >= _size) { - // Then grow - grow(word); - } + grow(word); uint32_t mask = 1U << (elem & bit_mask); uint32_t data = _data[word]; _data[word] = data | mask; @@ -109,9 +107,7 @@ class VectorSet : public AnyObj { // Fast inlined set void set(uint elem) { uint32_t word = elem >> word_bits; - if (word >= _size) { - grow(word); - } + grow(word); uint32_t mask = 1U << (elem & bit_mask); _data[word] |= mask; } diff --git a/src/hotspot/share/logging/logConfiguration.cpp b/src/hotspot/share/logging/logConfiguration.cpp index 3a54d1f4e63..87b195f4fbf 100644 --- a/src/hotspot/share/logging/logConfiguration.cpp +++ b/src/hotspot/share/logging/logConfiguration.cpp @@ -370,7 +370,7 @@ bool LogConfiguration::parse_command_line_arguments(const char* opts) { // Split the option string to its colon separated components. char* str = copy; - char* substrings[4] = {0}; + char* substrings[4] = {}; for (int i = 0 ; i < 4; i++) { substrings[i] = str; diff --git a/src/hotspot/share/memory/allocation.cpp b/src/hotspot/share/memory/allocation.cpp index 0f05bdda461..0f2ff7840b8 100644 --- a/src/hotspot/share/memory/allocation.cpp +++ b/src/hotspot/share/memory/allocation.cpp @@ -237,9 +237,10 @@ ReallocMark::ReallocMark() { #endif } -void ReallocMark::check() { +void ReallocMark::check(Arena* arena) { #ifdef ASSERT - if (_nesting != Thread::current()->resource_area()->nesting()) { + if ((arena == nullptr || arena == Thread::current()->resource_area()) && + _nesting != Thread::current()->resource_area()->nesting()) { fatal("allocation bug: array could grow within nested ResourceMark"); } #endif diff --git a/src/hotspot/share/memory/allocation.hpp b/src/hotspot/share/memory/allocation.hpp index 495ca66d867..da5f7b19830 100644 --- a/src/hotspot/share/memory/allocation.hpp +++ b/src/hotspot/share/memory/allocation.hpp @@ -556,8 +556,8 @@ class ReallocMark: public StackObj { NOT_PRODUCT(int _nesting;) public: - ReallocMark() PRODUCT_RETURN; - void check() PRODUCT_RETURN; + ReallocMark() PRODUCT_RETURN; + void check(Arena* arena = nullptr) PRODUCT_RETURN; }; // Uses mmapped memory for all allocations. All allocations are initially diff --git a/src/hotspot/share/memory/arena.cpp b/src/hotspot/share/memory/arena.cpp index 0399c6922e3..435a8cfdd58 100644 --- a/src/hotspot/share/memory/arena.cpp +++ b/src/hotspot/share/memory/arena.cpp @@ -44,6 +44,19 @@ STATIC_ASSERT(is_aligned((int)Chunk::init_size, ARENA_AMALLOC_ALIGNMENT)); STATIC_ASSERT(is_aligned((int)Chunk::medium_size, ARENA_AMALLOC_ALIGNMENT)); STATIC_ASSERT(is_aligned((int)Chunk::size, ARENA_AMALLOC_ALIGNMENT)); + +const char* Arena::tag_name[] = { +#define ARENA_TAG_STRING(name, str, desc) XSTR(name), + DO_ARENA_TAG(ARENA_TAG_STRING) +#undef ARENA_TAG_STRING +}; + +const char* Arena::tag_desc[] = { +#define ARENA_TAG_DESC(name, str, desc) XSTR(desc), + DO_ARENA_TAG(ARENA_TAG_DESC) +#undef ARENA_TAG_DESC +}; + // MT-safe pool of same-sized chunks to reduce malloc/free thrashing // NB: not using Mutex because pools are used before Threads are initialized class ChunkPool { diff --git a/src/hotspot/share/memory/arena.hpp b/src/hotspot/share/memory/arena.hpp index 1ca8a787825..1f3c5eb4e8b 100644 --- a/src/hotspot/share/memory/arena.hpp +++ b/src/hotspot/share/memory/arena.hpp @@ -83,17 +83,29 @@ class Chunk { bool contains(char* p) const { return bottom() <= p && p <= top(); } }; +#define DO_ARENA_TAG(FN) \ + FN(other, Others, Other arenas) \ + FN(ra, RA, Resource areas) \ + FN(ha, HA, Handle area) \ + FN(node, NA, Node arena) \ + // Fast allocation of memory class Arena : public CHeapObjBase { public: - - enum class Tag : uint8_t { - tag_other = 0, - tag_ra, // resource area - tag_ha, // handle area - tag_node // C2 Node arena + enum class Tag: uint8_t { +#define ARENA_TAG_ENUM(name, str, desc) tag_##name, + DO_ARENA_TAG(ARENA_TAG_ENUM) +#undef ARENA_TAG_ENUM + tag_count }; + constexpr static int tag_count() { + return static_cast(Tag::tag_count); + } + + static const char* tag_name[static_cast(Arena::Tag::tag_count)]; + static const char* tag_desc[static_cast(Arena::Tag::tag_count)]; + private: const MEMFLAGS _flags; // Memory tracking flags const Tag _tag; diff --git a/src/hotspot/share/memory/metaspace/metachunk.cpp b/src/hotspot/share/memory/metaspace/metachunk.cpp index 3d67aac8016..0bf7e98f130 100644 --- a/src/hotspot/share/memory/metaspace/metachunk.cpp +++ b/src/hotspot/share/memory/metaspace/metachunk.cpp @@ -253,7 +253,7 @@ void Metachunk::verify_neighborhood() const { } } -volatile MetaWord dummy = 0; +volatile MetaWord dummy = nullptr; void Metachunk::verify() const { // Note. This should be called under CLD lock protection. diff --git a/src/hotspot/share/memory/metaspace/rootChunkArea.cpp b/src/hotspot/share/memory/metaspace/rootChunkArea.cpp index ee3af35a5b8..24377ec25ad 100644 --- a/src/hotspot/share/memory/metaspace/rootChunkArea.cpp +++ b/src/hotspot/share/memory/metaspace/rootChunkArea.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2022 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -59,7 +59,7 @@ RootChunkArea::~RootChunkArea() { // root chunk header. It will be partly initialized. // Note: this just allocates a memory-less header; memory itself is allocated inside VirtualSpaceNode. Metachunk* RootChunkArea::alloc_root_chunk_header(VirtualSpaceNode* node) { - assert(_first_chunk == 0, "already have a root"); + assert(_first_chunk == nullptr, "already have a root"); Metachunk* c = ChunkHeaderPool::pool()->allocate_chunk_header(); c->initialize(node, const_cast(_base), chunklevel::ROOT_CHUNK_LEVEL); _first_chunk = c; diff --git a/src/hotspot/share/memory/virtualspace.cpp b/src/hotspot/share/memory/virtualspace.cpp index 7df35bbeec8..c27e607353a 100644 --- a/src/hotspot/share/memory/virtualspace.cpp +++ b/src/hotspot/share/memory/virtualspace.cpp @@ -159,7 +159,7 @@ static char* reserve_memory(char* requested_address, const size_t size, // If the memory was requested at a particular address, use // os::attempt_reserve_memory_at() to avoid mapping over something // important. If the reservation fails, return null. - if (requested_address != 0) { + if (requested_address != nullptr) { assert(is_aligned(requested_address, alignment), "Requested address " PTR_FORMAT " must be aligned to " SIZE_FORMAT, p2i(requested_address), alignment); diff --git a/src/hotspot/share/oops/constantPool.cpp b/src/hotspot/share/oops/constantPool.cpp index 35a1d95b6e6..1c295e10b21 100644 --- a/src/hotspot/share/oops/constantPool.cpp +++ b/src/hotspot/share/oops/constantPool.cpp @@ -2065,7 +2065,7 @@ static void print_cpool_bytes(jint cnt, u1 *bytes) { size += ent_size; } printf("Cpool size: %d\n", size); - fflush(0); + fflush(nullptr); return; } /* end print_cpool_bytes */ diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp index 930f8cf1771..650279db1be 100644 --- a/src/hotspot/share/oops/instanceKlass.cpp +++ b/src/hotspot/share/oops/instanceKlass.cpp @@ -2779,9 +2779,9 @@ void InstanceKlass::release_C_heap_structures(bool release_sub_metadata) { #if INCLUDE_JVMTI // Deallocate breakpoint records - if (breakpoints() != 0x0) { + if (breakpoints() != nullptr) { methods_do(clear_all_breakpoints); - assert(breakpoints() == 0x0, "should have cleared breakpoints"); + assert(breakpoints() == nullptr, "should have cleared breakpoints"); } // deallocate the cached class file diff --git a/src/hotspot/share/oops/method.cpp b/src/hotspot/share/oops/method.cpp index e5ba0d61031..f4dcd4f1493 100644 --- a/src/hotspot/share/oops/method.cpp +++ b/src/hotspot/share/oops/method.cpp @@ -322,7 +322,7 @@ void Method::mask_for(const methodHandle& this_mh, int bci, InterpreterOopMap* m } int Method::bci_from(address bcp) const { - if (is_native() && bcp == 0) { + if (is_native() && bcp == nullptr) { return 0; } // Do not have a ResourceMark here because AsyncGetCallTrace stack walking code @@ -345,7 +345,7 @@ int Method::validate_bci(int bci) const { int Method::validate_bci_from_bcp(address bcp) const { // keep bci as -1 if not a valid bci int bci = -1; - if (bcp == 0 || bcp == code_base()) { + if (bcp == nullptr || bcp == code_base()) { // code_size() may return 0 and we allow 0 here // the method may be native bci = 0; diff --git a/src/hotspot/share/oops/stackChunkOop.cpp b/src/hotspot/share/oops/stackChunkOop.cpp index 8d230b334c8..344fef15308 100644 --- a/src/hotspot/share/oops/stackChunkOop.cpp +++ b/src/hotspot/share/oops/stackChunkOop.cpp @@ -125,7 +125,7 @@ static int num_java_frames(const StackChunkFrameStream& f) { int stackChunkOopDesc::num_java_frames() const { int n = 0; for (StackChunkFrameStream f(const_cast(this)); !f.is_done(); - f.next(SmallRegisterMap::instance)) { + f.next(SmallRegisterMap::instance())) { if (!f.is_stub()) { n += ::num_java_frames(f); } diff --git a/src/hotspot/share/oops/stackChunkOop.inline.hpp b/src/hotspot/share/oops/stackChunkOop.inline.hpp index 54f1423fd1c..a54b8159e7e 100644 --- a/src/hotspot/share/oops/stackChunkOop.inline.hpp +++ b/src/hotspot/share/oops/stackChunkOop.inline.hpp @@ -201,7 +201,7 @@ inline void stackChunkOopDesc::iterate_stack(StackChunkFrameClosureType* closure template inline void stackChunkOopDesc::iterate_stack(StackChunkFrameClosureType* closure) { - const SmallRegisterMap* map = SmallRegisterMap::instance; + const SmallRegisterMap* map = SmallRegisterMap::instance(); assert(!map->in_cont(), ""); StackChunkFrameStream f(this); diff --git a/src/hotspot/share/opto/block.cpp b/src/hotspot/share/opto/block.cpp index 78774ff2fd1..1af085cd128 100644 --- a/src/hotspot/share/opto/block.cpp +++ b/src/hotspot/share/opto/block.cpp @@ -39,7 +39,10 @@ #include "utilities/powerOfTwo.hpp" void Block_Array::grow( uint i ) { - assert(i >= Max(), "must be an overflow"); + _nesting.check(_arena); // Check if a potential reallocation in the arena is safe + if (i < Max()) { + return; // No need to grow + } debug_only(_limit = i+1); if( i < _size ) return; if( !_size ) { @@ -374,6 +377,7 @@ void Block::dump(const PhaseCFG* cfg) const { PhaseCFG::PhaseCFG(Arena* arena, RootNode* root, Matcher& matcher) : Phase(CFG) , _root(root) +, _blocks(arena) , _block_arena(arena) , _regalloc(nullptr) , _scheduling_for_pressure(false) @@ -1417,7 +1421,7 @@ UnionFind::UnionFind( uint max ) : _cnt(max), _max(max), _indices(NEW_RESOURCE_A } void UnionFind::extend( uint from_idx, uint to_idx ) { - _nesting.check(); + _nesting.check(); // Check if a potential reallocation in the resource arena is safe if( from_idx >= _max ) { uint size = 16; while( size <= from_idx ) size <<=1; diff --git a/src/hotspot/share/opto/block.hpp b/src/hotspot/share/opto/block.hpp index e48b50b06ae..231c0994903 100644 --- a/src/hotspot/share/opto/block.hpp +++ b/src/hotspot/share/opto/block.hpp @@ -51,6 +51,7 @@ class Block_Array : public ArenaObj { uint _size; // allocated size, as opposed to formal limit debug_only(uint _limit;) // limit to formal domain Arena *_arena; // Arena to allocate in + ReallocMark _nesting; // Safety checks for arena reallocation protected: Block **_blocks; void grow( uint i ); // Grow array node to fit @@ -68,7 +69,7 @@ class Block_Array : public ArenaObj { Block *operator[] ( uint i ) const // Lookup, or assert for not mapped { assert( i < Max(), "oob" ); return _blocks[i]; } // Extend the mapping: index i maps to Block *n. - void map( uint i, Block *n ) { if( i>=Max() ) grow(i); _blocks[i] = n; } + void map( uint i, Block *n ) { grow(i); _blocks[i] = n; } uint Max() const { debug_only(return _limit); return _size; } }; @@ -77,7 +78,9 @@ class Block_List : public Block_Array { friend class VMStructs; public: uint _cnt; - Block_List() : Block_Array(Thread::current()->resource_area()), _cnt(0) {} + Block_List() : Block_List(Thread::current()->resource_area()) { } + Block_List(Arena* a) : Block_Array(a), _cnt(0) { } + void push( Block *b ) { map(_cnt++,b); } Block *pop() { return _blocks[--_cnt]; } Block *rpop() { Block *b = _blocks[0]; _blocks[0]=_blocks[--_cnt]; return b;} @@ -655,7 +658,7 @@ class PhaseCFG : public Phase { class UnionFind : public ResourceObj { uint _cnt, _max; uint* _indices; - ReallocMark _nesting; // assertion check for reallocations + ReallocMark _nesting; // Safety checks for arena reallocation public: UnionFind( uint max ); void reset( uint max ); // Reset to identity map for [0..max] diff --git a/src/hotspot/share/opto/callnode.hpp b/src/hotspot/share/opto/callnode.hpp index 818640a6f65..120bb112ec5 100644 --- a/src/hotspot/share/opto/callnode.hpp +++ b/src/hotspot/share/opto/callnode.hpp @@ -754,18 +754,18 @@ class CallJavaNode : public CallNode { virtual bool cmp( const Node &n ) const; virtual uint size_of() const; // Size is bigger + ciMethod* _method; // Method being direct called bool _optimized_virtual; bool _method_handle_invoke; bool _override_symbolic_info; // Override symbolic call site info from bytecode - ciMethod* _method; // Method being direct called bool _arg_escape; // ArgEscape in parameter list public: CallJavaNode(const TypeFunc* tf , address addr, ciMethod* method) : CallNode(tf, addr, TypePtr::BOTTOM), + _method(method), _optimized_virtual(false), _method_handle_invoke(false), _override_symbolic_info(false), - _method(method), _arg_escape(false) { init_class_id(Class_CallJava); diff --git a/src/hotspot/share/opto/castnode.hpp b/src/hotspot/share/opto/castnode.hpp index 3e20a92e848..87989235c67 100644 --- a/src/hotspot/share/opto/castnode.hpp +++ b/src/hotspot/share/opto/castnode.hpp @@ -101,12 +101,9 @@ class CastIINode: public ConstraintCastNode { virtual uint size_of() const; public: - CastIINode(Node* n, const Type* t, DependencyType dependency = RegularDependency, bool range_check_dependency = false, const TypeTuple* types = nullptr) - : ConstraintCastNode(nullptr, n, t, dependency, types), _range_check_dependency(range_check_dependency) { - init_class_id(Class_CastII); - } CastIINode(Node* ctrl, Node* n, const Type* t, DependencyType dependency = RegularDependency, bool range_check_dependency = false, const TypeTuple* types = nullptr) : ConstraintCastNode(ctrl, n, t, dependency, types), _range_check_dependency(range_check_dependency) { + assert(ctrl != nullptr, "control must be set"); init_class_id(Class_CastII); } virtual int Opcode() const; @@ -134,6 +131,7 @@ class CastLLNode: public ConstraintCastNode { public: CastLLNode(Node* ctrl, Node* n, const Type* t, DependencyType dependency = RegularDependency, const TypeTuple* types = nullptr) : ConstraintCastNode(ctrl, n, t, dependency, types) { + assert(ctrl != nullptr, "control must be set"); init_class_id(Class_CastLL); } @@ -147,6 +145,7 @@ class CastFFNode: public ConstraintCastNode { public: CastFFNode(Node* ctrl, Node* n, const Type* t, DependencyType dependency = RegularDependency, const TypeTuple* types = nullptr) : ConstraintCastNode(ctrl, n, t, dependency, types) { + assert(ctrl != nullptr, "control must be set"); init_class_id(Class_CastFF); } virtual int Opcode() const; @@ -157,6 +156,7 @@ class CastDDNode: public ConstraintCastNode { public: CastDDNode(Node* ctrl, Node* n, const Type* t, DependencyType dependency = RegularDependency, const TypeTuple* types = nullptr) : ConstraintCastNode(ctrl, n, t, dependency, types) { + assert(ctrl != nullptr, "control must be set"); init_class_id(Class_CastDD); } virtual int Opcode() const; @@ -167,6 +167,7 @@ class CastVVNode: public ConstraintCastNode { public: CastVVNode(Node* ctrl, Node* n, const Type* t, DependencyType dependency = RegularDependency, const TypeTuple* types = nullptr) : ConstraintCastNode(ctrl, n, t, dependency, types) { + assert(ctrl != nullptr, "control must be set"); init_class_id(Class_CastVV); } virtual int Opcode() const; @@ -192,6 +193,7 @@ class CheckCastPPNode: public ConstraintCastNode { public: CheckCastPPNode(Node* ctrl, Node* n, const Type* t, DependencyType dependency = RegularDependency, const TypeTuple* types = nullptr) : ConstraintCastNode(ctrl, n, t, dependency, types) { + assert(ctrl != nullptr, "control must be set"); init_class_id(Class_CheckCastPP); } diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index 7908f21de10..dc613776873 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -324,7 +324,6 @@ shmacro(ShenandoahCompareAndSwapN) shmacro(ShenandoahCompareAndSwapP) shmacro(ShenandoahWeakCompareAndSwapN) shmacro(ShenandoahWeakCompareAndSwapP) -shmacro(ShenandoahIUBarrier) shmacro(ShenandoahLoadReferenceBarrier) macro(SCMemProj) macro(CopySignD) diff --git a/src/hotspot/share/opto/domgraph.cpp b/src/hotspot/share/opto/domgraph.cpp index 46cf3316af5..363005d1181 100644 --- a/src/hotspot/share/opto/domgraph.cpp +++ b/src/hotspot/share/opto/domgraph.cpp @@ -61,9 +61,6 @@ struct Tarjan { // Compute the dominator tree of the CFG. The CFG must already have been // constructed. This is the Lengauer & Tarjan O(E-alpha(E,V)) algorithm. void PhaseCFG::build_dominator_tree() { - // Pre-grow the blocks array, prior to the ResourceMark kicking in - _blocks.map(number_of_blocks(), nullptr); - ResourceMark rm; // Setup mappings from my Graph to Tarjan's stuff and back // Note: Tarjan uses 1-based arrays diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index 2ca722148b6..eb6887f11ba 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -3879,7 +3879,7 @@ PhiNode *ConnectionGraph::create_split_phi(PhiNode *orig_phi, int alias_idx, Gro // Return a new version of Memory Phi "orig_phi" with the inputs having the // specified alias index. // -PhiNode *ConnectionGraph::split_memory_phi(PhiNode *orig_phi, int alias_idx, GrowableArray &orig_phi_worklist) { +PhiNode *ConnectionGraph::split_memory_phi(PhiNode *orig_phi, int alias_idx, GrowableArray &orig_phi_worklist, uint rec_depth) { assert(alias_idx != Compile::AliasIdxBot, "can't split out bottom memory"); Compile *C = _compile; PhaseGVN* igvn = _igvn; @@ -3895,7 +3895,7 @@ PhiNode *ConnectionGraph::split_memory_phi(PhiNode *orig_phi, int alias_idx, Gro bool finished = false; while(!finished) { while (idx < phi->req()) { - Node *mem = find_inst_mem(phi->in(idx), alias_idx, orig_phi_worklist); + Node *mem = find_inst_mem(phi->in(idx), alias_idx, orig_phi_worklist, rec_depth + 1); if (mem != nullptr && mem->is_Phi()) { PhiNode *newphi = create_split_phi(mem->as_Phi(), alias_idx, orig_phi_worklist, new_phi_created); if (new_phi_created) { @@ -4037,7 +4037,12 @@ void ConnectionGraph::move_inst_mem(Node* n, GrowableArray &orig_phi // Search memory chain of "mem" to find a MemNode whose address // is the specified alias index. // -Node* ConnectionGraph::find_inst_mem(Node *orig_mem, int alias_idx, GrowableArray &orig_phis) { +#define FIND_INST_MEM_RECURSION_DEPTH_LIMIT 1000 +Node* ConnectionGraph::find_inst_mem(Node *orig_mem, int alias_idx, GrowableArray &orig_phis, uint rec_depth) { + if (rec_depth > FIND_INST_MEM_RECURSION_DEPTH_LIMIT) { + _compile->record_failure(_invocation > 0 ? C2Compiler::retry_no_iterative_escape_analysis() : C2Compiler::retry_no_escape_analysis()); + return nullptr; + } if (orig_mem == nullptr) { return orig_mem; } @@ -4111,7 +4116,7 @@ Node* ConnectionGraph::find_inst_mem(Node *orig_mem, int alias_idx, GrowableArra if (result == mmem->base_memory()) { // Didn't find instance memory, search through general slice recursively. result = mmem->memory_at(C->get_general_index(alias_idx)); - result = find_inst_mem(result, alias_idx, orig_phis); + result = find_inst_mem(result, alias_idx, orig_phis, rec_depth + 1); if (C->failing()) { return nullptr; } @@ -4179,7 +4184,7 @@ Node* ConnectionGraph::find_inst_mem(Node *orig_mem, int alias_idx, GrowableArra orig_phis.append_if_missing(mphi); } else if (C->get_alias_index(t) != alias_idx) { // Create a new Phi with the specified alias index type. - result = split_memory_phi(mphi, alias_idx, orig_phis); + result = split_memory_phi(mphi, alias_idx, orig_phis, rec_depth + 1); } } // the result is either MemNode, PhiNode, InitializeNode. diff --git a/src/hotspot/share/opto/escape.hpp b/src/hotspot/share/opto/escape.hpp index 658c8f6e8fe..32e70be219a 100644 --- a/src/hotspot/share/opto/escape.hpp +++ b/src/hotspot/share/opto/escape.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2024, 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 @@ -549,10 +549,10 @@ class ConnectionGraph: public ArenaObj { bool split_AddP(Node *addp, Node *base); PhiNode *create_split_phi(PhiNode *orig_phi, int alias_idx, GrowableArray &orig_phi_worklist, bool &new_created); - PhiNode *split_memory_phi(PhiNode *orig_phi, int alias_idx, GrowableArray &orig_phi_worklist); + PhiNode *split_memory_phi(PhiNode *orig_phi, int alias_idx, GrowableArray &orig_phi_worklist, uint rec_depth); void move_inst_mem(Node* n, GrowableArray &orig_phis); - Node* find_inst_mem(Node* mem, int alias_idx,GrowableArray &orig_phi_worklist); + Node* find_inst_mem(Node* mem, int alias_idx,GrowableArray &orig_phi_worklist, uint rec_depth = 0); Node* step_through_mergemem(MergeMemNode *mmem, int alias_idx, const TypeOopPtr *toop); Node_Array _node_map; // used for bookkeeping during type splitting diff --git a/src/hotspot/share/opto/loopPredicate.cpp b/src/hotspot/share/opto/loopPredicate.cpp index f8d3fa0b6d9..b22931e5663 100644 --- a/src/hotspot/share/opto/loopPredicate.cpp +++ b/src/hotspot/share/opto/loopPredicate.cpp @@ -1341,8 +1341,8 @@ IfTrueNode* PhaseIdealLoop::add_template_assertion_predicate(IfNode* iff, IdealL // init + (current stride - initial stride) is within the loop so narrow its type by leveraging the type of the iv Phi const Type* type_iv = loop->_head->as_CountedLoop()->phi()->bottom_type(); assert(!type_iv->is_int()->is_con(), "constant indicates one loop iteration for which we bailed out earlier"); - max_value = new CastIINode(max_value, type_iv); - register_new_node(max_value, parse_predicate_proj); + max_value = new CastIINode(new_proj, max_value, type_iv); + register_new_node(max_value, new_proj); bol = rc_predicate(new_proj, scale, offset, max_value, limit, stride, rng, (stride > 0) != (scale > 0), overflow); diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index 1b29d31ba68..6ca7bb51fb4 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -2871,6 +2871,7 @@ void PhaseIdealLoop::do_range_check(IdealLoopTree *loop, Node_List &old_new) { // Must know if its a count-up or count-down loop int stride_con = cl->stride_con(); + bool abs_stride_is_one = stride_con == 1 || stride_con == -1; Node* zero = _igvn.longcon(0); Node* one = _igvn.longcon(1); // Use symmetrical int range [-max_jint,max_jint] @@ -2882,6 +2883,15 @@ void PhaseIdealLoop::do_range_check(IdealLoopTree *loop, Node_List &old_new) { Node* loop_entry = cl->skip_strip_mined()->in(LoopNode::EntryControl); assert(loop_entry->is_Proj() && loop_entry->in(0)->is_If(), "if projection only"); + // if abs(stride) == 1, an Assertion Predicate for the final iv value is added. We don't know the final iv value until + // we're done with range check elimination so use a place holder. + Node* final_iv_placeholder = nullptr; + if (abs_stride_is_one) { + final_iv_placeholder = new Node(1); + _igvn.set_type(final_iv_placeholder, TypeInt::INT); + final_iv_placeholder->init_req(0, loop_entry); + } + // Check loop body for tests of trip-counter plus loop-invariant vs loop-variant. for (uint i = 0; i < loop->_body.size(); i++) { Node *iff = loop->_body[i]; @@ -2985,6 +2995,20 @@ void PhaseIdealLoop::do_range_check(IdealLoopTree *loop, Node_List &old_new) { Node* opaque_init = new OpaqueLoopInitNode(C, init); register_new_node(opaque_init, loop_entry); + if (abs_stride_is_one) { + // If the main loop becomes empty and the array access for this range check is sunk out of the loop, the index + // for the array access will be set to the index value of the final iteration which could be out of loop. + // Add an Assertion Predicate for that corner case. The final iv is computed from LoopLimit which is the + // LoopNode::limit() only if abs(stride) == 1 otherwise the computation depends on LoopNode::init_trip() as + // well. When LoopLimit only depends on LoopNode::limit(), there are cases where the zero trip guard for the + // main loop doesn't constant fold after range check elimination but, the array access for the final + // iteration of the main loop is out of bound and the index for that access is out of range for the range + // check CastII. + loop_entry = add_range_check_elimination_assertion_predicate(loop, loop_entry, scale_con, int_offset, + int_limit, stride_con, final_iv_placeholder, false); + assert(!assertion_predicate_has_loop_opaque_node(loop_entry->in(0)->as_If()), "unexpected"); + } + // Initialized Assertion Predicate for the value of the initial main-loop. loop_entry = add_range_check_elimination_assertion_predicate(loop, loop_entry, scale_con, int_offset, int_limit, stride_con, init, false); @@ -3004,7 +3028,7 @@ void PhaseIdealLoop::do_range_check(IdealLoopTree *loop, Node_List &old_new) { max_value = new AddINode(opaque_init, max_value); register_new_node(max_value, loop_entry); // init + (current stride - initial stride) is within the loop so narrow its type by leveraging the type of the iv Phi - max_value = new CastIINode(max_value, loop->_head->as_CountedLoop()->phi()->bottom_type()); + max_value = new CastIINode(loop_entry, max_value, loop->_head->as_CountedLoop()->phi()->bottom_type()); register_new_node(max_value, loop_entry); loop_entry = add_range_check_elimination_assertion_predicate( loop, loop_entry, scale_con, int_offset, int_limit, stride_con, max_value, true @@ -3116,11 +3140,14 @@ void PhaseIdealLoop::do_range_check(IdealLoopTree *loop, Node_List &old_new) { register_new_node(main_limit, pre_ctrl); // Hack the now-private loop bounds _igvn.replace_input_of(main_cmp, 2, main_limit); + if (abs_stride_is_one) { + Node* final_iv = new SubINode(main_limit, cl->stride()); + register_new_node(final_iv, loop_entry); + _igvn.replace_node(final_iv_placeholder, final_iv); + } // The OpaqueNode is unshared by design assert(opqzm->outcnt() == 1, "cannot hack shared node"); _igvn.replace_input_of(opqzm, 1, main_limit); - - return; } bool IdealLoopTree::compute_has_range_checks() const { diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index 85e6f03d428..45e59f4e216 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -5248,6 +5248,7 @@ bool IdealLoopTree::verify_tree(IdealLoopTree* loop_verify) const { //------------------------------set_idom--------------------------------------- void PhaseIdealLoop::set_idom(Node* d, Node* n, uint dom_depth) { + _nesting.check(); // Check if a potential reallocation in the resource arena is safe uint idx = d->_idx; if (idx >= _idom_size) { uint newsize = next_power_of_2(idx); diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index 7d78bf5021c..24101ea07a0 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -611,6 +611,7 @@ class IdealLoopTree : public ResourceObj { _head(head), _tail(tail), _phase(phase), _local_loop_unroll_limit(0), _local_loop_unroll_factor(0), + _body(Compile::current()->comp_arena()), _nest(0), _irreducible(0), _has_call(0), _has_sfpt(0), _rce_candidate(0), _has_range_checks(0), _has_range_checks_computed(0), _safepts(nullptr), @@ -840,6 +841,8 @@ class PhaseIdealLoop : public PhaseTransform { uint *_preorders; uint _max_preorder; + ReallocMark _nesting; // Safety checks for arena reallocation + const PhaseIdealLoop* _verify_me; bool _verify_only; @@ -852,6 +855,7 @@ class PhaseIdealLoop : public PhaseTransform { // Allocate _preorders[] array void reallocate_preorders() { + _nesting.check(); // Check if a potential re-allocation in the resource arena is safe if ( _max_preorder < C->unique() ) { _preorders = REALLOC_RESOURCE_ARRAY(uint, _preorders, _max_preorder, C->unique()); _max_preorder = C->unique(); @@ -862,6 +866,7 @@ class PhaseIdealLoop : public PhaseTransform { // Check to grow _preorders[] array for the case when build_loop_tree_impl() // adds new nodes. void check_grow_preorders( ) { + _nesting.check(); // Check if a potential re-allocation in the resource arena is safe if ( _max_preorder < C->unique() ) { uint newsize = _max_preorder<<1; // double size of array _preorders = REALLOC_RESOURCE_ARRAY(uint, _preorders, _max_preorder, newsize); diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index ba0ce344122..361e03f06a2 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -4670,6 +4670,9 @@ void DataNodeGraph::clone(Node* node, Node* new_ctrl) { _phase->igvn().register_new_node_with_optimizer(clone); _orig_to_new.put(node, clone); _phase->set_ctrl(clone, new_ctrl); + if (node->is_CastII()) { + clone->set_req(0, new_ctrl); + } } // Rewire the data inputs of all (unprocessed) cloned nodes, whose inputs are still pointing to the same inputs as their diff --git a/src/hotspot/share/opto/machnode.cpp b/src/hotspot/share/opto/machnode.cpp index 57361313f85..39b804f7e5c 100644 --- a/src/hotspot/share/opto/machnode.cpp +++ b/src/hotspot/share/opto/machnode.cpp @@ -395,7 +395,14 @@ const class TypePtr *MachNode::adr_type() const { // 32-bit unscaled narrow oop can be the base of any address expression t = t->make_ptr(); } - if (t->isa_intptr_t() && offset != 0 && offset != Type::OffsetBot) { + + if (t->isa_intptr_t() && +#if !defined(AARCH64) + // AArch64 supports the addressing mode: + // [base, 0], in which [base] is converted from a long value + offset != 0 && +#endif + offset != Type::OffsetBot) { // We cannot assert that the offset does not look oop-ish here. // Depending on the heap layout the cardmark base could land // inside some oopish region. It definitely does for Win2K. diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index 438dc5c1f64..c7f0fb9fc32 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -2723,13 +2723,13 @@ uint StoreNode::hash() const { // class ArrayPointer { private: - const bool _is_valid; // The parsing succeeded const Node* _pointer; // The final pointer to the position in the array const Node* _base; // Base address of the array const jlong _constant_offset; // Sum of collected constant offsets const Node* _int_offset; // (optional) Offset behind LShiftL and ConvI2L - const jint _int_offset_shift; // (optional) Shift value for int_offset const GrowableArray* _other_offsets; // List of other AddP offsets + const jint _int_offset_shift; // (optional) Shift value for int_offset + const bool _is_valid; // The parsing succeeded ArrayPointer(const bool is_valid, const Node* pointer, @@ -2738,13 +2738,13 @@ class ArrayPointer { const Node* int_offset, const jint int_offset_shift, const GrowableArray* other_offsets) : - _is_valid(is_valid), _pointer(pointer), _base(base), _constant_offset(constant_offset), _int_offset(int_offset), + _other_offsets(other_offsets), _int_offset_shift(int_offset_shift), - _other_offsets(other_offsets) + _is_valid(is_valid) { assert(_pointer != nullptr, "must always have pointer"); assert(is_valid == (_base != nullptr), "have base exactly if valid"); diff --git a/src/hotspot/share/opto/movenode.cpp b/src/hotspot/share/opto/movenode.cpp index eb0a914b994..dc65afff26f 100644 --- a/src/hotspot/share/opto/movenode.cpp +++ b/src/hotspot/share/opto/movenode.cpp @@ -91,17 +91,20 @@ Node *CMoveNode::Ideal(PhaseGVN *phase, bool can_reshape) { phase->type(in(IfTrue)) == Type::TOP) { return nullptr; } - // Canonicalize the node by moving constants to the right input. - if (in(Condition)->is_Bool() && phase->type(in(IfFalse))->singleton() && !phase->type(in(IfTrue))->singleton()) { - BoolNode* b = in(Condition)->as_Bool()->negate(phase); - return make(in(Control), phase->transform(b), in(IfTrue), in(IfFalse), _type); - } + // Check for Min/Max patterns. This is called before constants are pushed to the right input, as that transform can + // make BoolTests non-canonical. Node* minmax = Ideal_minmax(phase, this); if (minmax != nullptr) { return minmax; } + // Canonicalize the node by moving constants to the right input. + if (in(Condition)->is_Bool() && phase->type(in(IfFalse))->singleton() && !phase->type(in(IfTrue))->singleton()) { + BoolNode* b = in(Condition)->as_Bool()->negate(phase); + return make(in(Control), phase->transform(b), in(IfTrue), in(IfFalse), _type); + } + return nullptr; } diff --git a/src/hotspot/share/opto/node.cpp b/src/hotspot/share/opto/node.cpp index 6cf6ff2094e..f36770e67ea 100644 --- a/src/hotspot/share/opto/node.cpp +++ b/src/hotspot/share/opto/node.cpp @@ -2766,6 +2766,10 @@ const RegMask &Node::in_RegMask(uint) const { } void Node_Array::grow(uint i) { + _nesting.check(_a); // Check if a potential reallocation in the arena is safe + if (i < _max) { + return; // No need to grow + } assert(_max > 0, "invariant"); uint old = _max; _max = next_power_of_2(i); @@ -2973,6 +2977,10 @@ void Unique_Node_List::remove_useless_nodes(VectorSet &useful) { //============================================================================= void Node_Stack::grow() { + _nesting.check(_a); // Check if a potential reallocation in the arena is safe + if (_inode_top < _inode_max) { + return; // No need to grow + } size_t old_top = pointer_delta(_inode_top,_inodes,sizeof(INode)); // save _top size_t old_max = pointer_delta(_inode_max,_inodes,sizeof(INode)); size_t max = old_max << 1; // max * 2 diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index ae379c4833a..10e1e7b1006 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -1596,6 +1596,8 @@ class Node_Array : public AnyObj { Arena* _a; // Arena to allocate in uint _max; Node** _nodes; + ReallocMark _nesting; // Safety checks for arena reallocation + void grow( uint i ); // Grow array node to fit public: Node_Array(Arena* a, uint max = OptoNodeListSize) : _a(a), _max(max) { @@ -1614,7 +1616,7 @@ class Node_Array : public AnyObj { Node* at(uint i) const { assert(i<_max,"oob"); return _nodes[i]; } Node** adr() { return _nodes; } // Extend the mapping: index i maps to Node *n. - void map( uint i, Node *n ) { if( i>=_max ) grow(i); _nodes[i] = n; } + void map( uint i, Node *n ) { grow(i); _nodes[i] = n; } void insert( uint i, Node *n ); void remove( uint i ); // Remove, preserving order // Clear all entries in _nodes to null but keep storage @@ -1844,6 +1846,7 @@ class Node_Stack { INode *_inode_max; // End of _inodes == _inodes + _max INode *_inodes; // Array storage for the stack Arena *_a; // Arena to allocate in + ReallocMark _nesting; // Safety checks for arena reallocation void grow(); public: Node_Stack(int size) { @@ -1867,7 +1870,7 @@ class Node_Stack { } void push(Node *n, uint i) { ++_inode_top; - if (_inode_top >= _inode_max) grow(); + grow(); INode *top = _inode_top; // optimization top->node = n; top->indx = i; diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 96e9f108808..dae4a5c68a3 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -2351,6 +2351,7 @@ void Node::replace_by(Node *new_node) { //============================================================================= //----------------------------------------------------------------------------- void Type_Array::grow( uint i ) { + assert(_a == Compile::current()->comp_arena(), "Should be allocated in comp_arena"); if( !_max ) { _max = 1; _types = (const Type**)_a->Amalloc( _max * sizeof(Type*) ); diff --git a/src/hotspot/share/opto/vector.cpp b/src/hotspot/share/opto/vector.cpp index 1b4d64e20b1..9ab62c282d1 100644 --- a/src/hotspot/share/opto/vector.cpp +++ b/src/hotspot/share/opto/vector.cpp @@ -184,7 +184,7 @@ void PhaseVector::scalarize_vbox_node(VectorBoxNode* vec_box) { // Process merged VBAs if (EnableVectorAggressiveReboxing) { - Unique_Node_List calls(C->comp_arena()); + Unique_Node_List calls; for (DUIterator_Fast imax, i = vec_box->fast_outs(imax); i < imax; i++) { Node* use = vec_box->fast_out(i); if (use->is_CallJava()) { @@ -238,9 +238,9 @@ void PhaseVector::scalarize_vbox_node(VectorBoxNode* vec_box) { } // Process debug uses at safepoints - Unique_Node_List safepoints(C->comp_arena()); + Unique_Node_List safepoints; - Unique_Node_List worklist(C->comp_arena()); + Unique_Node_List worklist; worklist.push(vec_box); while (worklist.size() > 0) { Node* n = worklist.pop(); diff --git a/src/hotspot/share/opto/vectorIntrinsics.cpp b/src/hotspot/share/opto/vectorIntrinsics.cpp index b31f6ace5a6..cfcd903e79d 100644 --- a/src/hotspot/share/opto/vectorIntrinsics.cpp +++ b/src/hotspot/share/opto/vectorIntrinsics.cpp @@ -2484,12 +2484,10 @@ bool LibraryCallKit::inline_vector_insert() { // Convert insert value back to its appropriate type. switch (elem_bt) { case T_BYTE: - insert_val = gvn().transform(new ConvL2INode(insert_val)); - insert_val = gvn().transform(new CastIINode(insert_val, TypeInt::BYTE)); + insert_val = gvn().transform(new ConvL2INode(insert_val, TypeInt::BYTE)); break; case T_SHORT: - insert_val = gvn().transform(new ConvL2INode(insert_val)); - insert_val = gvn().transform(new CastIINode(insert_val, TypeInt::SHORT)); + insert_val = gvn().transform(new ConvL2INode(insert_val, TypeInt::SHORT)); break; case T_INT: insert_val = gvn().transform(new ConvL2INode(insert_val)); diff --git a/src/hotspot/share/prims/jni.cpp b/src/hotspot/share/prims/jni.cpp index ae040d66138..a91d7375761 100644 --- a/src/hotspot/share/prims/jni.cpp +++ b/src/hotspot/share/prims/jni.cpp @@ -1148,7 +1148,7 @@ JNI_ENTRY(ResultType, \ jni_Call##Result##Method(JNIEnv *env, jobject obj, jmethodID methodID, ...)) \ \ EntryProbe; \ - ResultType ret = 0;\ + ResultType ret{}; \ DT_RETURN_MARK_FOR(Result, Call##Result##Method, ResultType, \ (const ResultType&)ret);\ \ @@ -1203,7 +1203,7 @@ JNI_ENTRY(ResultType, \ jni_Call##Result##MethodV(JNIEnv *env, jobject obj, jmethodID methodID, va_list args)) \ \ EntryProbe;\ - ResultType ret = 0;\ + ResultType ret{}; \ DT_RETURN_MARK_FOR(Result, Call##Result##MethodV, ResultType, \ (const ResultType&)ret);\ \ @@ -1254,7 +1254,7 @@ DEFINE_CALLMETHODV(jdouble, Double, T_DOUBLE JNI_ENTRY(ResultType, \ jni_Call##Result##MethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args)) \ EntryProbe; \ - ResultType ret = 0;\ + ResultType ret{}; \ DT_RETURN_MARK_FOR(Result, Call##Result##MethodA, ResultType, \ (const ResultType&)ret);\ \ @@ -1546,7 +1546,7 @@ JNI_ENTRY(ResultType, \ jni_CallStatic##Result##Method(JNIEnv *env, jclass cls, jmethodID methodID, ...)) \ \ EntryProbe; \ - ResultType ret = 0;\ + ResultType ret{}; \ DT_RETURN_MARK_FOR(Result, CallStatic##Result##Method, ResultType, \ (const ResultType&)ret);\ \ @@ -1601,7 +1601,7 @@ JNI_ENTRY(ResultType, \ jni_CallStatic##Result##MethodV(JNIEnv *env, jclass cls, jmethodID methodID, va_list args)) \ \ EntryProbe; \ - ResultType ret = 0;\ + ResultType ret{}; \ DT_RETURN_MARK_FOR(Result, CallStatic##Result##MethodV, ResultType, \ (const ResultType&)ret);\ \ @@ -1657,7 +1657,7 @@ JNI_ENTRY(ResultType, \ jni_CallStatic##Result##MethodA(JNIEnv *env, jclass cls, jmethodID methodID, const jvalue *args)) \ \ EntryProbe; \ - ResultType ret = 0;\ + ResultType ret{}; \ DT_RETURN_MARK_FOR(Result, CallStatic##Result##MethodA, ResultType, \ (const ResultType&)ret);\ \ @@ -1750,7 +1750,7 @@ DT_RETURN_MARK_DECL(GetFieldID, jfieldID JNI_ENTRY(jfieldID, jni_GetFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig)) HOTSPOT_JNI_GETFIELDID_ENTRY(env, clazz, (char *) name, (char *) sig); - jfieldID ret = 0; + jfieldID ret = nullptr; DT_RETURN_MARK(GetFieldID, jfieldID, (const jfieldID&)ret); Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(clazz)); @@ -3035,12 +3035,12 @@ extern "C" void* JNICALL jni_GetDirectBufferAddress(JNIEnv *env, jobject buf) if (!directBufferSupportInitializeEnded) { if (!initializeDirectBufferSupport(env, thread)) { - return 0; + return nullptr; } } if ((buf != nullptr) && (!env->IsInstanceOf(buf, directBufferClass))) { - return 0; + return nullptr; } ret = (void*)(intptr_t)env->GetLongField(buf, directBufferAddressField); @@ -3647,8 +3647,8 @@ static jint JNI_CreateJavaVM_inner(JavaVM **vm, void **penv, void *args) { } // Creation failed. We must reset vm_created - *vm = 0; - *(JNIEnv**)penv = 0; + *vm = nullptr; + *(JNIEnv**)penv = nullptr; // reset vm_created last to avoid race condition. Use OrderAccess to // control both compiler and architectural-based reordering. assert(vm_created == IN_PROGRESS, "must be"); diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp index 7a8c07477dc..4f63ebdf9d5 100644 --- a/src/hotspot/share/prims/jvm.cpp +++ b/src/hotspot/share/prims/jvm.cpp @@ -3119,14 +3119,6 @@ JVM_ENTRY(jobject, JVM_GetStackTrace(JNIEnv *env, jobject jthread)) return JNIHandles::make_local(THREAD, trace); JVM_END -JVM_ENTRY(void, JVM_DumpAllStacks(JNIEnv* env, jclass)) - VM_PrintThreads op; - VMThread::execute(&op); - if (JvmtiExport::should_post_data_dump()) { - JvmtiExport::post_data_dump(); - } -JVM_END - JVM_ENTRY(void, JVM_SetNativeThreadName(JNIEnv* env, jobject jthread, jstring name)) // We don't use a ThreadsListHandle here because the current thread // must be alive. @@ -3755,7 +3747,7 @@ JVM_ENTRY(jobjectArray, JVM_DumpThreads(JNIEnv *env, jclass threadClass, jobject // Check if threads is null if (threads == nullptr) { - THROW_(vmSymbols::java_lang_NullPointerException(), 0); + THROW_NULL(vmSymbols::java_lang_NullPointerException()); } objArrayOop a = objArrayOop(JNIHandles::resolve_non_null(threads)); @@ -3763,13 +3755,13 @@ JVM_ENTRY(jobjectArray, JVM_DumpThreads(JNIEnv *env, jclass threadClass, jobject int num_threads = ah->length(); // check if threads is non-empty array if (num_threads == 0) { - THROW_(vmSymbols::java_lang_IllegalArgumentException(), 0); + THROW_NULL(vmSymbols::java_lang_IllegalArgumentException()); } // check if threads is not an array of objects of Thread class Klass* k = ObjArrayKlass::cast(ah->klass())->element_klass(); if (k != vmClasses::Thread_klass()) { - THROW_(vmSymbols::java_lang_IllegalArgumentException(), 0); + THROW_NULL(vmSymbols::java_lang_IllegalArgumentException()); } ResourceMark rm(THREAD); diff --git a/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp b/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp index 8d7ffdf4835..ceed1d8ad95 100644 --- a/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp +++ b/src/hotspot/share/prims/jvmtiClassFileReconstituter.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2024, 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 @@ -119,10 +119,10 @@ void JvmtiClassFileReconstituter::write_field_infos() { write_signature_attribute(generic_signature_index); } if (anno != nullptr) { - write_annotations_attribute("RuntimeVisibleAnnotations", "RuntimeInvisibleAnnotations", anno); + write_annotations_attribute("RuntimeVisibleAnnotations", anno); } if (type_anno != nullptr) { - write_annotations_attribute("RuntimeVisibleTypeAnnotations", "RuntimeInvisibleTypeAnnotations", type_anno); + write_annotations_attribute("RuntimeVisibleTypeAnnotations", type_anno); } } } @@ -380,20 +380,6 @@ void JvmtiClassFileReconstituter::write_annotations_attribute(const char* attr_n memcpy(writeable_address(length), annos->adr_at(0), length); } -void JvmtiClassFileReconstituter::write_annotations_attribute(const char* attr_name, - const char* fallback_attr_name, - AnnotationArray* annos) { - TempNewSymbol sym = SymbolTable::probe(attr_name, (int)strlen(attr_name)); - if (sym != nullptr) { - if (symbol_to_cpool_index(sym) != 0) { - write_annotations_attribute(attr_name, annos); - return; - } - } - // use fallback name - write_annotations_attribute(fallback_attr_name, annos); -} - // BootstrapMethods_attribute { // u2 attribute_name_index; // u4 attribute_length; @@ -533,10 +519,10 @@ void JvmtiClassFileReconstituter::write_record_attribute() { write_signature_attribute(component->generic_signature_index()); } if (component->annotations() != nullptr) { - write_annotations_attribute("RuntimeVisibleAnnotations", "RuntimeInvisibleAnnotations", component->annotations()); + write_annotations_attribute("RuntimeVisibleAnnotations", component->annotations()); } if (component->type_annotations() != nullptr) { - write_annotations_attribute("RuntimeVisibleTypeAnnotations", "RuntimeInvisibleTypeAnnotations", component->type_annotations()); + write_annotations_attribute("RuntimeVisibleTypeAnnotations", component->type_annotations()); } } } @@ -775,13 +761,13 @@ void JvmtiClassFileReconstituter::write_method_info(const methodHandle& method) write_signature_attribute(generic_signature_index); } if (anno != nullptr) { - write_annotations_attribute("RuntimeVisibleAnnotations", "RuntimeInvisibleAnnotations", anno); + write_annotations_attribute("RuntimeVisibleAnnotations", anno); } if (param_anno != nullptr) { - write_annotations_attribute("RuntimeVisibleParameterAnnotations", "RuntimeInvisibleParameterAnnotations", param_anno); + write_annotations_attribute("RuntimeVisibleParameterAnnotations", param_anno); } if (type_anno != nullptr) { - write_annotations_attribute("RuntimeVisibleTypeAnnotations", "RuntimeInvisibleTypeAnnotations", type_anno); + write_annotations_attribute("RuntimeVisibleTypeAnnotations", type_anno); } } @@ -841,10 +827,10 @@ void JvmtiClassFileReconstituter::write_class_attributes() { write_source_debug_extension_attribute(); } if (anno != nullptr) { - write_annotations_attribute("RuntimeVisibleAnnotations", "RuntimeInvisibleAnnotations", anno); + write_annotations_attribute("RuntimeVisibleAnnotations", anno); } if (type_anno != nullptr) { - write_annotations_attribute("RuntimeVisibleTypeAnnotations", "RuntimeInvisibleTypeAnnotations", type_anno); + write_annotations_attribute("RuntimeVisibleTypeAnnotations", type_anno); } if (ik()->nest_host_index() != 0) { write_nest_host_attribute(); diff --git a/src/hotspot/share/prims/jvmtiClassFileReconstituter.hpp b/src/hotspot/share/prims/jvmtiClassFileReconstituter.hpp index 2f36a2877d2..015042a8543 100644 --- a/src/hotspot/share/prims/jvmtiClassFileReconstituter.hpp +++ b/src/hotspot/share/prims/jvmtiClassFileReconstituter.hpp @@ -117,13 +117,6 @@ class JvmtiClassFileReconstituter : public JvmtiConstantPoolReconstituter { void write_signature_attribute(u2 generic_signaure_index); void write_attribute_name_index(const char* name); void write_annotations_attribute(const char* attr_name, AnnotationArray* annos); - // With PreserveAllAnnotations option "runtime invisible" annotations - // (RuntimeInvisibleAnnotations/RuntimeInvisibleTypeAnnotations/RuntimeInvisibleParameterAnnotations) - // are considered "runtime visible" and ClassFileReconstituter writes them as - // RuntimeVisibleAnnotations/RuntimeVisibleTypeAnnotations/RuntimeVisibleParameterAnnotations. - // This helper method is for the corner case when "runtime visible" attribute name is not presents - // in the class constant pool and the annotations are written with fallback "runtime invisible" name. - void write_annotations_attribute(const char* attr_name, const char* fallback_attr_name, AnnotationArray* annos); void write_bootstrapmethod_attribute(); void write_nest_host_attribute(); void write_nest_members_attribute(); diff --git a/src/hotspot/share/prims/jvmtiExport.cpp b/src/hotspot/share/prims/jvmtiExport.cpp index c2b6d27986b..f79116f5ebe 100644 --- a/src/hotspot/share/prims/jvmtiExport.cpp +++ b/src/hotspot/share/prims/jvmtiExport.cpp @@ -2068,7 +2068,7 @@ void JvmtiExport::post_exception_throw(JavaThread *thread, Method* method, addre jmethodID catch_jmethodID; if (current_bci < 0) { - catch_jmethodID = 0; + catch_jmethodID = nullptr; current_bci = 0; } else { catch_jmethodID = jem.to_jmethodID(current_mh); @@ -2105,8 +2105,8 @@ void JvmtiExport::notice_unwind_due_to_exception(JavaThread *thread, Method* met JvmtiTrace::safe_get_thread_name(thread), (mh() == nullptr) ? "null" : mh()->klass_name()->as_C_string(), (mh() == nullptr) ? "null" : mh()->name()->as_C_string(), - location==0? "no location:" : "", - location==0? 0 : location - mh()->code_base(), + location == nullptr ? "no location:" : "", + location == nullptr ? 0 : location - mh()->code_base(), in_handler_frame? "in handler frame" : "not handler frame" )); if (state->is_exception_detected()) { diff --git a/src/hotspot/share/prims/jvmtiTrace.cpp b/src/hotspot/share/prims/jvmtiTrace.cpp index 002f59957ea..c5fb95c931b 100644 --- a/src/hotspot/share/prims/jvmtiTrace.cpp +++ b/src/hotspot/share/prims/jvmtiTrace.cpp @@ -261,7 +261,7 @@ void JvmtiTrace::shutdown() { const char* JvmtiTrace::enum_name(const char** names, const jint* values, jint value) { - for (int index = 0; names[index] != 0; ++index) { + for (int index = 0; names[index] != nullptr; ++index) { if (values[index] == value) { return names[index]; } diff --git a/src/hotspot/share/prims/methodHandles.cpp b/src/hotspot/share/prims/methodHandles.cpp index fd10f0723bf..473ac396997 100644 --- a/src/hotspot/share/prims/methodHandles.cpp +++ b/src/hotspot/share/prims/methodHandles.cpp @@ -436,7 +436,6 @@ Symbol* MethodHandles::signature_polymorphic_intrinsic_name(vmIntrinsics::ID iid case vmIntrinsics::_linkToNative: return vmSymbols::linkToNative_name(); default: fatal("unexpected intrinsic id: %d %s", vmIntrinsics::as_int(iid), vmIntrinsics::name_at(iid)); - return 0; } } @@ -449,7 +448,6 @@ Bytecodes::Code MethodHandles::signature_polymorphic_intrinsic_bytecode(vmIntrin case vmIntrinsics::_invokeBasic: return Bytecodes::_invokehandle; default: fatal("unexpected id: (%d) %s", (uint)id, vmIntrinsics::name_at(id)); - return Bytecodes::_illegal; } } @@ -463,7 +461,6 @@ int MethodHandles::signature_polymorphic_intrinsic_ref_kind(vmIntrinsics::ID iid case vmIntrinsics::_linkToInterface: return JVM_REF_invokeInterface; default: fatal("unexpected intrinsic id: %d %s", vmIntrinsics::as_int(iid), vmIntrinsics::name_at(iid)); - return 0; } } diff --git a/src/hotspot/share/prims/perf.cpp b/src/hotspot/share/prims/perf.cpp index f10854b5294..9ff831dded9 100644 --- a/src/hotspot/share/prims/perf.cpp +++ b/src/hotspot/share/prims/perf.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2024, 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 @@ -68,7 +68,7 @@ PERF_ENTRY(jobject, Perf_Attach(JNIEnv *env, jobject unused, int vmid)) PerfWrapper("Perf_Attach"); - char* address = 0; + char* address = nullptr; size_t capacity = 0; // attach to the PerfData memory region for the specified VM @@ -90,7 +90,7 @@ PERF_ENTRY(void, Perf_Detach(JNIEnv *env, jobject unused, jobject buffer)) return; } - void* address = 0; + void* address = nullptr; jlong capacity = 0; // get buffer address and capacity diff --git a/src/hotspot/share/prims/unsafe.cpp b/src/hotspot/share/prims/unsafe.cpp index 942d9100c29..1be157b2e44 100644 --- a/src/hotspot/share/prims/unsafe.cpp +++ b/src/hotspot/share/prims/unsafe.cpp @@ -652,7 +652,7 @@ static jclass Unsafe_DefineClass_impl(JNIEnv *env, jstring name, jbyteArray data jbyte *body; char *utfName = nullptr; - jclass result = 0; + jclass result = nullptr; char buf[128]; assert(data != nullptr, "Class bytes must not be null"); @@ -665,7 +665,7 @@ static jclass Unsafe_DefineClass_impl(JNIEnv *env, jstring name, jbyteArray data body = NEW_C_HEAP_ARRAY_RETURN_NULL(jbyte, length, mtInternal); if (body == nullptr) { throw_new(env, "java/lang/OutOfMemoryError"); - return 0; + return nullptr; } env->GetByteArrayRegion(data, offset, length, body); diff --git a/src/hotspot/share/prims/wbtestmethods/parserTests.cpp b/src/hotspot/share/prims/wbtestmethods/parserTests.cpp index 2845aa9aa10..efcf2b070d0 100644 --- a/src/hotspot/share/prims/wbtestmethods/parserTests.cpp +++ b/src/hotspot/share/prims/wbtestmethods/parserTests.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2024, 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 @@ -127,6 +127,14 @@ static void fill_in_parser(DCmdParser* parser, oop argument) } else { parser->add_dcmd_option(argument); } + } else if (strcmp(type, "FILE") == 0) { + DCmdArgument* argument = + new DCmdArgument(name, desc, "FILE", mandatory); + if (isarg) { + parser->add_dcmd_argument(argument); + } else { + parser->add_dcmd_option(argument); + } } } diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index d353c5a162a..23b53300441 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -102,7 +102,7 @@ size_t Arguments::_default_SharedBaseAddress = SharedBaseAddress; bool Arguments::_enable_preview = false; -LegacyGCLogging Arguments::_legacyGCLogging = { 0, 0 }; +LegacyGCLogging Arguments::_legacyGCLogging = { nullptr, 0 }; // These are not set by the JDK's built-in launchers, but they can be set by // programs that embed the JVM using JNI_CreateJavaVM. See comments around @@ -503,7 +503,6 @@ static SpecialFlag const special_jvm_flags[] = { { "RequireSharedSpaces", JDK_Version::jdk(18), JDK_Version::jdk(19), JDK_Version::undefined() }, { "UseSharedSpaces", JDK_Version::jdk(18), JDK_Version::jdk(19), JDK_Version::undefined() }, { "DontYieldALot", JDK_Version::jdk(23), JDK_Version::jdk(24), JDK_Version::jdk(25) }, - { "PreserveAllAnnotations", JDK_Version::jdk(23), JDK_Version::jdk(24), JDK_Version::jdk(25) }, { "UseNotificationThread", JDK_Version::jdk(23), JDK_Version::jdk(24), JDK_Version::jdk(25) }, { "LockingMode", JDK_Version::jdk(24), JDK_Version::jdk(26), JDK_Version::jdk(27) }, // --- Deprecated alias flags (see also aliased_jvm_flags) - sorted by obsolete_in then expired_in: @@ -513,6 +512,7 @@ static SpecialFlag const special_jvm_flags[] = { { "MetaspaceReclaimPolicy", JDK_Version::undefined(), JDK_Version::jdk(21), JDK_Version::undefined() }, + { "PreserveAllAnnotations", JDK_Version::jdk(23), JDK_Version::jdk(24), JDK_Version::jdk(25) }, { "UseEmptySlotsInSupers", JDK_Version::jdk(23), JDK_Version::jdk(24), JDK_Version::jdk(25) }, { "OldSize", JDK_Version::jdk(23), JDK_Version::jdk(24), JDK_Version::jdk(25) }, #if defined(X86) @@ -521,6 +521,8 @@ static SpecialFlag const special_jvm_flags[] = { { "RTMRetryCount", JDK_Version::jdk(23), JDK_Version::jdk(24), JDK_Version::jdk(25) }, #endif // X86 + + { "BaseFootPrintEstimate", JDK_Version::undefined(), JDK_Version::jdk(24), JDK_Version::jdk(25) }, { "HeapFirstMaximumCompactionCount", JDK_Version::undefined(), JDK_Version::jdk(24), JDK_Version::jdk(25) }, { "UseVtableBasedCHA", JDK_Version::undefined(), JDK_Version::jdk(24), JDK_Version::jdk(25) }, #ifdef ASSERT @@ -1653,9 +1655,6 @@ jint Arguments::set_aggressive_heap_flags() { #endif // Increase some data structure sizes for efficiency - if (FLAG_SET_CMDLINE(BaseFootPrintEstimate, MaxHeapSize) != JVMFlag::SUCCESS) { - return JNI_EINVAL; - } if (FLAG_SET_CMDLINE(ResizeTLAB, false) != JVMFlag::SUCCESS) { return JNI_EINVAL; } @@ -1850,11 +1849,11 @@ bool Arguments::is_bad_option(const JavaVMOption* option, jboolean ignore, } static const char* user_assertion_options[] = { - "-da", "-ea", "-disableassertions", "-enableassertions", 0 + "-da", "-ea", "-disableassertions", "-enableassertions", nullptr }; static const char* system_assertion_options[] = { - "-dsa", "-esa", "-disablesystemassertions", "-enablesystemassertions", 0 + "-dsa", "-esa", "-disablesystemassertions", "-enablesystemassertions", nullptr }; bool Arguments::parse_uint(const char* value, diff --git a/src/hotspot/share/runtime/continuationFreezeThaw.cpp b/src/hotspot/share/runtime/continuationFreezeThaw.cpp index 87f60debc14..0e4640b6a9f 100644 --- a/src/hotspot/share/runtime/continuationFreezeThaw.cpp +++ b/src/hotspot/share/runtime/continuationFreezeThaw.cpp @@ -1865,7 +1865,7 @@ int ThawBase::remove_top_compiled_frame_from_chunk(stackChunkOop chunk, int &arg const int frame_size = f.cb()->frame_size(); argsize = f.stack_argsize(); - f.next(SmallRegisterMap::instance, true /* stop */); + f.next(SmallRegisterMap::instance(), true /* stop */); empty = f.is_done(); assert(!empty || argsize == chunk->argsize(), ""); @@ -2075,7 +2075,7 @@ bool ThawBase::recurse_thaw_java_frame(frame& caller, int num_frames) { int argsize = _stream.stack_argsize(); - _stream.next(SmallRegisterMap::instance); + _stream.next(SmallRegisterMap::instance()); assert(_stream.to_frame().is_empty() == _stream.is_done(), ""); // we never leave a compiled caller of an interpreted frame as the top frame in the chunk @@ -2183,7 +2183,7 @@ NOINLINE void ThawBase::recurse_thaw_interpreted_frame(const frame& hf, frame& c assert(hf.is_interpreted_frame(), ""); if (UNLIKELY(seen_by_gc())) { - _cont.tail()->do_barriers(_stream, SmallRegisterMap::instance); + _cont.tail()->do_barriers(_stream, SmallRegisterMap::instance()); } const bool is_bottom_frame = recurse_thaw_java_frame(caller, num_frames); @@ -2226,7 +2226,7 @@ NOINLINE void ThawBase::recurse_thaw_interpreted_frame(const frame& hf, frame& c if (!is_bottom_frame) { // can only fix caller once this frame is thawed (due to callee saved regs) - _cont.tail()->fix_thawed_frame(caller, SmallRegisterMap::instance); + _cont.tail()->fix_thawed_frame(caller, SmallRegisterMap::instance()); } else if (_cont.tail()->has_bitmap() && locals > 0) { assert(hf.is_heap_frame(), "should be"); address start = (address)(heap_frame_bottom - locals); @@ -2243,7 +2243,7 @@ void ThawBase::recurse_thaw_compiled_frame(const frame& hf, frame& caller, int n assert(_cont.is_preempted() || !stub_caller, "stub caller not at preemption"); if (!stub_caller && UNLIKELY(seen_by_gc())) { // recurse_thaw_stub_frame already invoked our barriers with a full regmap - _cont.tail()->do_barriers(_stream, SmallRegisterMap::instance); + _cont.tail()->do_barriers(_stream, SmallRegisterMap::instance()); } const bool is_bottom_frame = recurse_thaw_java_frame(caller, num_frames); @@ -2302,7 +2302,7 @@ void ThawBase::recurse_thaw_compiled_frame(const frame& hf, frame& caller, int n if (!is_bottom_frame) { // can only fix caller once this frame is thawed (due to callee saved regs); this happens on the stack - _cont.tail()->fix_thawed_frame(caller, SmallRegisterMap::instance); + _cont.tail()->fix_thawed_frame(caller, SmallRegisterMap::instance()); } else if (_cont.tail()->has_bitmap() && added_argsize > 0) { address start = (address)(heap_frame_top + ContinuationHelper::CompiledFrame::size(hf) + frame::metadata_words_at_top); int stack_args_slots = f.cb()->as_nmethod()->num_stack_arg_slots(false /* rounded */); @@ -2384,7 +2384,7 @@ void ThawBase::finish_thaw(frame& f) { f.set_sp(align_down(f.sp(), frame::frame_alignment)); } push_return_frame(f); - chunk->fix_thawed_frame(f, SmallRegisterMap::instance); // can only fix caller after push_return_frame (due to callee saved regs) + chunk->fix_thawed_frame(f, SmallRegisterMap::instance()); // can only fix caller after push_return_frame (due to callee saved regs) assert(_cont.is_empty() == _cont.last_frame().is_empty(), ""); @@ -2450,8 +2450,8 @@ static inline intptr_t* thaw_internal(JavaThread* thread, const Continuation::th intptr_t* const sp = thw.thaw(kind); assert(is_aligned(sp, frame::frame_alignment), ""); - // All the frames have been thawed so we know they don't hold any monitors - assert(thread->held_monitor_count() == 0, "Must be"); + // All or part of the frames have been thawed so we know they don't hold any monitors except JNI monitors. + assert(thread->held_monitor_count() == thread->jni_monitor_count(), "Must be"); #ifdef ASSERT intptr_t* sp0 = sp; diff --git a/src/hotspot/share/runtime/flags/jvmFlag.cpp b/src/hotspot/share/runtime/flags/jvmFlag.cpp index 1917cd4b198..c465a942e5c 100644 --- a/src/hotspot/share/runtime/flags/jvmFlag.cpp +++ b/src/hotspot/share/runtime/flags/jvmFlag.cpp @@ -31,6 +31,7 @@ #include "runtime/flags/jvmFlagAccess.hpp" #include "runtime/flags/jvmFlagLookup.hpp" #include "runtime/globals_extension.hpp" +#include "utilities/bitMap.hpp" #include "utilities/defaultStream.hpp" #include "utilities/stringUtils.hpp" @@ -692,7 +693,7 @@ void JVMFlag::printFlags(outputStream* out, bool withComments, bool printRanges, // called as part of error reporting, so handle native OOMs gracefully. // The last entry is the null entry. - const size_t length = JVMFlag::numFlags - 1; + constexpr size_t length = (sizeof(flagTable) / sizeof(JVMFlag)) - 1; // Print if (!printRanges) { @@ -701,26 +702,26 @@ void JVMFlag::printFlags(outputStream* out, bool withComments, bool printRanges, out->print_cr("[Global flags ranges]"); } - // Sort - JVMFlag** array = NEW_C_HEAP_ARRAY_RETURN_NULL(JVMFlag*, length, mtArguments); - if (array != nullptr) { - for (size_t i = 0; i < length; i++) { - array[i] = &flagTable[i]; - } - qsort(array, length, sizeof(JVMFlag*), compare_flags); - + BitMap::bm_word_t iteratorArray[BitMap::calc_size_in_words(length)]; + BitMapView iteratorMarkers(iteratorArray, length); + iteratorMarkers.clear_range(0, length); + // Print the flag with best sort value, then mark it. + for (size_t j = 0; j < length; j++) { + JVMFlag* bestFlag = nullptr; + size_t bestFlagIndex = 0; for (size_t i = 0; i < length; i++) { - if (array[i]->is_unlocked() && !(skipDefaults && array[i]->is_default())) { - array[i]->print_on(out, withComments, printRanges); + const bool skip = (skipDefaults && flagTable[i].is_default()); + const bool visited = iteratorMarkers.at(i); + if (!visited && flagTable[i].is_unlocked() && !skip) { + if ((bestFlag == nullptr) || (strcmp(bestFlag->name(), flagTable[i].name()) > 0)) { + bestFlag = &flagTable[i]; + bestFlagIndex = i; + } } } - FREE_C_HEAP_ARRAY(JVMFlag*, array); - } else { - // OOM? Print unsorted. - for (size_t i = 0; i < length; i++) { - if (flagTable[i].is_unlocked() && !(skipDefaults && flagTable[i].is_default())) { - flagTable[i].print_on(out, withComments, printRanges); - } + if (bestFlag != nullptr) { + bestFlag->print_on(out, withComments, printRanges); + iteratorMarkers.at_put(bestFlagIndex, true); } } } diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index 7a7a3769ac9..562549f247e 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -795,10 +795,6 @@ const int ObjectAlignmentInBytes = 8; "but not all -Xrun libraries may support the state of the VM " \ "at this time") \ \ - product(bool, PreserveAllAnnotations, false, \ - "(Deprecated) Preserve RuntimeInvisibleAnnotations as well " \ - "as RuntimeVisibleAnnotations") \ - \ develop(uintx, PreallocatedOutOfMemoryErrorCount, 4, \ "Number of OutOfMemoryErrors preallocated with backtrace") \ \ diff --git a/src/hotspot/share/runtime/javaThread.cpp b/src/hotspot/share/runtime/javaThread.cpp index a3eef07ba0a..b69ab708afa 100644 --- a/src/hotspot/share/runtime/javaThread.cpp +++ b/src/hotspot/share/runtime/javaThread.cpp @@ -470,8 +470,8 @@ JavaThread::JavaThread(MEMFLAGS flags) : #endif // INCLUDE_JVMCI _exception_oop(oop()), - _exception_pc(0), - _exception_handler_pc(0), + _exception_pc(nullptr), + _exception_handler_pc(nullptr), _is_method_handle_return(0), _jni_active_critical(0), @@ -483,7 +483,7 @@ JavaThread::JavaThread(MEMFLAGS flags) : _frames_to_pop_failed_realloc(0), _cont_entry(nullptr), - _cont_fastpath(0), + _cont_fastpath(nullptr), _cont_fastpath_thread_state(1), _held_monitor_count(0), _jni_monitor_count(0), diff --git a/src/hotspot/share/runtime/synchronizer.cpp b/src/hotspot/share/runtime/synchronizer.cpp index 047f6703b54..2d45163d0f4 100644 --- a/src/hotspot/share/runtime/synchronizer.cpp +++ b/src/hotspot/share/runtime/synchronizer.cpp @@ -2074,7 +2074,7 @@ void ObjectSynchronizer::chk_in_use_entry(ObjectMonitor* n, outputStream* out, void ObjectSynchronizer::log_in_use_monitor_details(outputStream* out, bool log_all) { if (_in_use_list.count() > 0) { stringStream ss; - out->print_cr("In-use monitor info:"); + out->print_cr("In-use monitor info%s:", log_all ? "" : " (eliding idle monitors)"); out->print_cr("(B -> is_busy, H -> has hash code, L -> lock status)"); out->print_cr("%18s %s %18s %18s", "monitor", "BHL", "object", "object type"); diff --git a/src/hotspot/share/runtime/vframeArray.cpp b/src/hotspot/share/runtime/vframeArray.cpp index d3bbbc28399..1b26abf7402 100644 --- a/src/hotspot/share/runtime/vframeArray.cpp +++ b/src/hotspot/share/runtime/vframeArray.cpp @@ -321,7 +321,7 @@ void vframeArrayElement::unpack_on_stack(int caller_actual_parameters, "should be held, after move_to"); } if (ProfileInterpreter) { - iframe()->interpreter_frame_set_mdp(0); // clear out the mdp. + iframe()->interpreter_frame_set_mdp(nullptr); // clear out the mdp. } iframe()->interpreter_frame_set_bcp(bcp); if (ProfileInterpreter) { diff --git a/src/hotspot/share/services/diagnosticArgument.cpp b/src/hotspot/share/services/diagnosticArgument.cpp index 94f2d3e1eb1..5fd565a605a 100644 --- a/src/hotspot/share/services/diagnosticArgument.cpp +++ b/src/hotspot/share/services/diagnosticArgument.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2024, 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 @@ -189,9 +189,18 @@ template <> void DCmdArgument::parse_value(const char* str, destroy_value(); } else { // Use realloc as we may have a default set. - _value = REALLOC_C_HEAP_ARRAY(char, _value, len + 1, mtInternal); - int n = os::snprintf(_value, len + 1, "%.*s", (int)len, str); - assert((size_t)n <= len, "Unexpected number of characters in string"); + if (strcmp(type(), "FILE") == 0) { + _value = REALLOC_C_HEAP_ARRAY(char, _value, JVM_MAXPATHLEN, mtInternal); + if (!Arguments::copy_expand_pid(str, len, _value, JVM_MAXPATHLEN)) { + stringStream error_msg; + error_msg.print("File path invalid or too long: %s", str); + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), error_msg.base()); + } + } else { + _value = REALLOC_C_HEAP_ARRAY(char, _value, len + 1, mtInternal); + int n = os::snprintf(_value, len + 1, "%.*s", (int)len, str); + assert((size_t)n <= len, "Unexpected number of characters in string"); + } } } diff --git a/src/hotspot/share/services/diagnosticArgument.hpp b/src/hotspot/share/services/diagnosticArgument.hpp index c9683ce4a21..1451ea34f86 100644 --- a/src/hotspot/share/services/diagnosticArgument.hpp +++ b/src/hotspot/share/services/diagnosticArgument.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, 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 @@ -27,6 +27,7 @@ #include "classfile/vmSymbols.hpp" #include "memory/allocation.hpp" +#include "runtime/arguments.hpp" #include "runtime/javaThread.hpp" #include "runtime/os.hpp" #include "utilities/exceptions.hpp" diff --git a/src/hotspot/share/services/diagnosticCommand.cpp b/src/hotspot/share/services/diagnosticCommand.cpp index e6328e95abd..7a2c0838146 100644 --- a/src/hotspot/share/services/diagnosticCommand.cpp +++ b/src/hotspot/share/services/diagnosticCommand.cpp @@ -473,7 +473,7 @@ void FinalizerInfoDCmd::execute(DCmdSource source, TRAPS) { #if INCLUDE_SERVICES // Heap dumping/inspection supported HeapDumpDCmd::HeapDumpDCmd(outputStream* output, bool heap) : DCmdWithParser(output, heap), - _filename("filename","Name of the dump file", "STRING",true), + _filename("filename","Name of the dump file", "FILE",true), _all("-all", "Dump all objects, including unreachable objects", "BOOLEAN", false, "false"), _gzip("-gz", "If specified, the heap dump is written in gzipped format " @@ -852,20 +852,15 @@ void CodeCacheDCmd::execute(DCmdSource source, TRAPS) { } #ifdef LINUX -#define DEFAULT_PERFMAP_FILENAME "/tmp/perf-.map" - PerfMapDCmd::PerfMapDCmd(outputStream* output, bool heap) : DCmdWithParser(output, heap), - _filename("filename", "Name of the map file", "STRING", false, DEFAULT_PERFMAP_FILENAME) + _filename("filename", "Name of the map file", "FILE", false, DEFAULT_PERFMAP_FILENAME) { _dcmdparser.add_dcmd_argument(&_filename); } void PerfMapDCmd::execute(DCmdSource source, TRAPS) { - // The check for _filename.is_set() is because we don't want to use - // DEFAULT_PERFMAP_FILENAME, since it is meant as a description - // of the default, not the actual default. - CodeCache::write_perf_map(_filename.is_set() ? _filename.value() : nullptr, output()); + CodeCache::write_perf_map(_filename.value(), output()); } #endif // LINUX @@ -997,12 +992,12 @@ void ClassesDCmd::execute(DCmdSource source, TRAPS) { } #if INCLUDE_CDS -#define DEFAULT_CDS_ARCHIVE_FILENAME "java_pid_.jsa" +#define DEFAULT_CDS_ARCHIVE_FILENAME "java_pid%p_.jsa" DumpSharedArchiveDCmd::DumpSharedArchiveDCmd(outputStream* output, bool heap) : DCmdWithParser(output, heap), _suboption("subcmd", "static_dump | dynamic_dump", "STRING", true), - _filename("filename", "Name of shared archive to be dumped", "STRING", false, + _filename("filename", "Name of shared archive to be dumped", "FILE", false, DEFAULT_CDS_ARCHIVE_FILENAME) { _dcmdparser.add_dcmd_argument(&_suboption); @@ -1040,7 +1035,7 @@ void DumpSharedArchiveDCmd::execute(DCmdSource source, TRAPS) { // call CDS.dumpSharedArchive Handle fileh; if (file != nullptr) { - fileh = java_lang_String::create_from_str(_filename.value(), CHECK); + fileh = java_lang_String::create_from_str(file, CHECK); } Symbol* cds_name = vmSymbols::jdk_internal_misc_CDS(); Klass* cds_klass = SystemDictionary::resolve_or_fail(cds_name, true /*throw error*/, CHECK); @@ -1105,7 +1100,7 @@ ThreadDumpToFileDCmd::ThreadDumpToFileDCmd(outputStream* output, bool heap) : DCmdWithParser(output, heap), _overwrite("-overwrite", "May overwrite existing file", "BOOLEAN", false, "false"), _format("-format", "Output format (\"plain\" or \"json\")", "STRING", false, "plain"), - _filepath("filepath", "The file path to the output file", "STRING", true) { + _filepath("filepath", "The file path to the output file", "FILE", true) { _dcmdparser.add_dcmd_option(&_overwrite); _dcmdparser.add_dcmd_option(&_format); _dcmdparser.add_dcmd_argument(&_filepath); @@ -1186,23 +1181,16 @@ void SystemMapDCmd::execute(DCmdSource source, TRAPS) { MemMapPrinter::print_all_mappings(output()); } -static constexpr char default_filename[] = "vm_memory_map_.txt"; +static constexpr char default_filename[] = "vm_memory_map_%p.txt"; SystemDumpMapDCmd::SystemDumpMapDCmd(outputStream* output, bool heap) : DCmdWithParser(output, heap), - _filename("-F", "file path", "STRING", false, default_filename) { + _filename("-F", "file path", "FILE", false, default_filename) { _dcmdparser.add_dcmd_option(&_filename); } void SystemDumpMapDCmd::execute(DCmdSource source, TRAPS) { - stringStream defaultname; - const char* name = nullptr; - if (_filename.is_set()) { - name = _filename.value(); - } else { - defaultname.print("vm_memory_map_%d.txt", os::current_process_id()); - name = defaultname.base(); - } + const char* name = _filename.value(); fileStream fs(name); if (fs.is_open()) { if (!MemTracker::enabled()) { diff --git a/src/hotspot/share/services/diagnosticFramework.hpp b/src/hotspot/share/services/diagnosticFramework.hpp index e8881c23646..357482ec5a1 100644 --- a/src/hotspot/share/services/diagnosticFramework.hpp +++ b/src/hotspot/share/services/diagnosticFramework.hpp @@ -67,7 +67,7 @@ class CmdLine : public StackObj { const char* cmd_addr() const { return _cmd; } size_t cmd_len() const { return _cmd_len; } bool is_empty() const { return _cmd_len == 0; } - bool is_executable() const { return is_empty() || _cmd[0] != '#'; } + bool is_executable() const { return !is_empty() && _cmd[0] != '#'; } bool is_stop() const { return !is_empty() && strncmp("stop", _cmd, _cmd_len) == 0; } }; diff --git a/src/hotspot/share/utilities/bitMap.cpp b/src/hotspot/share/utilities/bitMap.cpp index 9bd5c8e0006..a0780707091 100644 --- a/src/hotspot/share/utilities/bitMap.cpp +++ b/src/hotspot/share/utilities/bitMap.cpp @@ -197,11 +197,6 @@ bm_word_t* CHeapBitMap::reallocate(bm_word_t* map, size_t old_size_in_words, siz } #ifdef ASSERT -void BitMap::verify_size(idx_t size_in_bits) { - assert(size_in_bits <= max_size_in_bits(), - "out of bounds: " SIZE_FORMAT, size_in_bits); -} - void BitMap::verify_index(idx_t bit) const { assert(bit < _size, "BitMap index out of bounds: " SIZE_FORMAT " >= " SIZE_FORMAT, diff --git a/src/hotspot/share/utilities/bitMap.hpp b/src/hotspot/share/utilities/bitMap.hpp index 0d592de7cd0..5db12badbc9 100644 --- a/src/hotspot/share/utilities/bitMap.hpp +++ b/src/hotspot/share/utilities/bitMap.hpp @@ -67,16 +67,16 @@ class BitMap { protected: // The maximum allowable size of a bitmap, in words or bits. // Limit max_size_in_bits so aligning up to a word boundary never overflows. - static idx_t max_size_in_words() { return raw_to_words_align_down(~idx_t(0)); } - static idx_t max_size_in_bits() { return max_size_in_words() * BitsPerWord; } + constexpr static idx_t max_size_in_words() { return raw_to_words_align_down(~idx_t(0)); } + constexpr static idx_t max_size_in_bits() { return max_size_in_words() * BitsPerWord; } // Assumes relevant validity checking for bit has already been done. - static idx_t raw_to_words_align_up(idx_t bit) { + constexpr static idx_t raw_to_words_align_up(idx_t bit) { return raw_to_words_align_down(bit + (BitsPerWord - 1)); } // Assumes relevant validity checking for bit has already been done. - static idx_t raw_to_words_align_down(idx_t bit) { + constexpr static idx_t raw_to_words_align_down(idx_t bit) { return bit >> LogBitsPerWord; } @@ -195,7 +195,7 @@ class BitMap { void pretouch(); // Accessing - static idx_t calc_size_in_words(size_t size_in_bits) { + constexpr static idx_t calc_size_in_words(size_t size_in_bits) { verify_size(size_in_bits); return raw_to_words_align_up(size_in_bits); } @@ -257,7 +257,13 @@ class BitMap { // Verification. // Verify size_in_bits does not exceed max_size_in_bits(). - static void verify_size(idx_t size_in_bits) NOT_DEBUG_RETURN; + constexpr static void verify_size(idx_t size_in_bits) { +#ifdef ASSERT + assert(size_in_bits <= max_size_in_bits(), + "out of bounds: " SIZE_FORMAT, size_in_bits); +#endif + } + // Verify bit is less than size(). void verify_index(idx_t bit) const NOT_DEBUG_RETURN; // Verify bit is not greater than size(). diff --git a/src/hotspot/share/utilities/exceptions.cpp b/src/hotspot/share/utilities/exceptions.cpp index c8f458dfa31..034444839ab 100644 --- a/src/hotspot/share/utilities/exceptions.cpp +++ b/src/hotspot/share/utilities/exceptions.cpp @@ -43,6 +43,7 @@ #include "runtime/atomic.hpp" #include "utilities/events.hpp" #include "utilities/exceptions.hpp" +#include "utilities/utf8.hpp" // Limit exception message components to 64K (the same max as Symbols) #define MAX_LEN 65535 @@ -262,8 +263,32 @@ void Exceptions::fthrow(JavaThread* thread, const char* file, int line, Symbol* va_list ap; va_start(ap, format); char msg[max_msg_size]; - os::vsnprintf(msg, max_msg_size, format, ap); + int ret = os::vsnprintf(msg, max_msg_size, format, ap); va_end(ap); + + // If ret == -1 then either there was a format conversion error, or the required buffer size + // exceeds INT_MAX and so couldn't be returned (undocumented behaviour of vsnprintf). Depending + // on the platform the buffer may be filled to its capacity (Linux), filled to the conversion + // that encountered the overflow (macOS), or is empty (Windows), so it is possible we + // have a truncated UTF-8 sequence. Similarly, if the buffer was too small and ret >= max_msg_size + // we may also have a truncated UTF-8 sequence. In such cases we need to fix the buffer so the UTF-8 + // sequence is valid. + if (ret == -1 || ret >= max_msg_size) { + int len = (int) strlen(msg); + if (len > 0) { + // Truncation will only happen if the buffer was filled by vsnprintf, + // otherwise vsnprintf already terminated filling it at a well-defined point. + // But as this is not a clearly specified area we will perform our own UTF8 + // truncation anyway - though for those well-defined termination points it + // will be a no-op. + UTF8::truncate_to_legal_utf8((unsigned char*)msg, len + 1); + } + } + // UTF8::is_legal_utf8 should actually be called is_legal_utf8_class_name as the final + // parameter controls a check for a specific character appearing in the "name", which is only + // allowed for classfile versions <= 47. We pass `true` so that we allow such strings as this code + // know nothing about the actual string content. + assert(UTF8::is_legal_utf8((const unsigned char*)msg, (int)strlen(msg), true), "must be"); _throw_msg(thread, file, line, h_name, msg); } diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp index 74817a35e77..b7be6dc04bf 100644 --- a/src/hotspot/share/utilities/globalDefinitions.hpp +++ b/src/hotspot/share/utilities/globalDefinitions.hpp @@ -222,9 +222,9 @@ FORBID_C_FUNCTION(void* reallocf(void *ptr, size_t size), "don't use"); const int LogBytesPerShort = 1; const int LogBytesPerInt = 2; #ifdef _LP64 -const int LogBytesPerWord = 3; +constexpr int LogBytesPerWord = 3; #else -const int LogBytesPerWord = 2; +constexpr int LogBytesPerWord = 2; #endif const int LogBytesPerLong = 3; @@ -233,16 +233,16 @@ const int BytesPerInt = 1 << LogBytesPerInt; const int BytesPerWord = 1 << LogBytesPerWord; const int BytesPerLong = 1 << LogBytesPerLong; -const int LogBitsPerByte = 3; +constexpr int LogBitsPerByte = 3; const int LogBitsPerShort = LogBitsPerByte + LogBytesPerShort; const int LogBitsPerInt = LogBitsPerByte + LogBytesPerInt; -const int LogBitsPerWord = LogBitsPerByte + LogBytesPerWord; +constexpr int LogBitsPerWord = LogBitsPerByte + LogBytesPerWord; const int LogBitsPerLong = LogBitsPerByte + LogBytesPerLong; const int BitsPerByte = 1 << LogBitsPerByte; const int BitsPerShort = 1 << LogBitsPerShort; const int BitsPerInt = 1 << LogBitsPerInt; -const int BitsPerWord = 1 << LogBitsPerWord; +constexpr int BitsPerWord = 1 << LogBitsPerWord; const int BitsPerLong = 1 << LogBitsPerLong; const int WordAlignmentMask = (1 << LogBytesPerWord) - 1; diff --git a/src/hotspot/share/utilities/utf8.cpp b/src/hotspot/share/utilities/utf8.cpp index 6fd877120df..47cbb04da4b 100644 --- a/src/hotspot/share/utilities/utf8.cpp +++ b/src/hotspot/share/utilities/utf8.cpp @@ -392,6 +392,69 @@ bool UTF8::is_legal_utf8(const unsigned char* buffer, int length, return true; } +// Return true if `b` could be the starting byte of an encoded 2,3 or 6 +// byte sequence. +static bool is_starting_byte(unsigned char b) { + return b >= 0xC0 && b <= 0xEF; +} + +// Takes an incoming buffer that was valid UTF-8, but which has been truncated such that +// the last encoding may be partial, and returns the same buffer with a NUL-terminator +// inserted such that any partial encoding has gone. +// Note: if the incoming buffer is already valid then we may still drop the last encoding. +// To avoid that the caller can choose to check for validity first. +// The incoming buffer is still expected to be NUL-terminated. +// The incoming buffer is expected to be a realistic size - we assert if it is too small. +void UTF8::truncate_to_legal_utf8(unsigned char* buffer, int length) { + assert(length > 5, "invalid length"); + assert(buffer[length - 1] == '\0', "Buffer should be NUL-terminated"); + + if (buffer[length - 2] < 128) { // valid "ascii" - common case + return; + } + + // Modified UTF-8 encodes characters in sequences of 1, 2, 3 or 6 bytes. + // The last byte is invalid if it is: + // - the 1st byte of a 2, 3 or 6 byte sequence + // 0b110xxxxx + // 0b1110xxxx + // 0b11101101 + // - the 2nd byte of a 3 or 6 byte sequence + // 0b10xxxxxx + // 0b1010xxxx + // - the 3rd, 4th or 5th byte of a 6 byte sequence + // 0b10xxxxxx + // 0b11101101 + // 0b1011xxxx + // + // Rather than checking all possible situations we simplify things noting that as we have already + // got a truncated string, then dropping one more character is not significant. So we work from the + // end of the buffer looking for the first byte that can be the starting byte of a UTF-8 encoded sequence, + // then we insert NUL at that location to terminate the buffer. There is an added complexity with 6 byte + // encodings as the first and fourth bytes are the same and overlap with the 3 byte encoding. + + for (int index = length - 2; index > 0; index--) { + if (is_starting_byte(buffer[index])) { + if (buffer[index] == 0xED) { + // Could be first byte of 3 or 6, or fourth byte of 6. + // If fourth the previous three bytes will encode a high + // surrogate value in the range EDA080 to EDAFBF. We only + // need to check for EDA to establish this as the "missing" + // values in EDAxxx would not be valid 3 byte encodings. + if ((index - 3) >= 0 && + (buffer[index - 3] == 0xED) && + ((buffer[index - 2] & 0xF0) == 0xA0)) { + assert(buffer[index - 1] >= 0x80 && buffer[index - 1] <= 0xBF, "sanity check"); + // It was fourth byte so truncate 3 bytes earlier + index -= 3; + } + } + buffer[index] = '\0'; + break; + } + } +} + //------------------------------------------------------------------------------------- bool UNICODE::is_latin1(jchar c) { diff --git a/src/hotspot/share/utilities/utf8.hpp b/src/hotspot/share/utilities/utf8.hpp index 80346f7da7d..9a18dd0ff93 100644 --- a/src/hotspot/share/utilities/utf8.hpp +++ b/src/hotspot/share/utilities/utf8.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, 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 @@ -88,6 +88,7 @@ class UTF8 : AllStatic { static bool is_legal_utf8(const unsigned char* buffer, int length, bool version_leq_47); + static void truncate_to_legal_utf8(unsigned char* buffer, int length); }; diff --git a/src/java.base/linux/native/libnio/ch/FileDispatcherImpl.c b/src/java.base/linux/native/libnio/ch/FileDispatcherImpl.c index 02d3c57f1f3..efbd0ca5684 100644 --- a/src/java.base/linux/native/libnio/ch/FileDispatcherImpl.c +++ b/src/java.base/linux/native/libnio/ch/FileDispatcherImpl.c @@ -114,7 +114,7 @@ Java_sun_nio_ch_FileDispatcherImpl_transferTo0(JNIEnv *env, jobject this, return n; } - n = sendfile64(dstFD, srcFD, &offset, (size_t)count); + n = sendfile(dstFD, srcFD, &offset, (size_t)count); if (n < 0) { if (errno == EAGAIN) return IOS_UNAVAILABLE; diff --git a/src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c b/src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c index 95f78ffa135..c90e99dda07 100644 --- a/src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c +++ b/src/java.base/linux/native/libnio/fs/LinuxNativeDispatcher.c @@ -211,7 +211,7 @@ Java_sun_nio_fs_LinuxNativeDispatcher_directCopy0 } do { - RESTARTABLE(sendfile64(dst, src, NULL, count), bytes_sent); + RESTARTABLE(sendfile(dst, src, NULL, count), bytes_sent); if (bytes_sent < 0) { if (errno == EAGAIN) return IOS_UNAVAILABLE; diff --git a/src/java.base/share/classes/java/lang/AbstractStringBuilder.java b/src/java.base/share/classes/java/lang/AbstractStringBuilder.java index 7e5056a539b..e0c3aa19f54 100644 --- a/src/java.base/share/classes/java/lang/AbstractStringBuilder.java +++ b/src/java.base/share/classes/java/lang/AbstractStringBuilder.java @@ -27,6 +27,7 @@ import jdk.internal.math.DoubleToDecimal; import jdk.internal.math.FloatToDecimal; +import jdk.internal.util.DecimalDigits; import java.io.IOException; import java.nio.CharBuffer; @@ -836,7 +837,7 @@ public AbstractStringBuilder append(char c) { */ public AbstractStringBuilder append(int i) { int count = this.count; - int spaceNeeded = count + Integer.stringSize(i); + int spaceNeeded = count + DecimalDigits.stringSize(i); ensureCapacityInternal(spaceNeeded); if (isLatin1()) { StringLatin1.getChars(i, spaceNeeded, value); @@ -861,7 +862,7 @@ public AbstractStringBuilder append(int i) { */ public AbstractStringBuilder append(long l) { int count = this.count; - int spaceNeeded = count + Long.stringSize(l); + int spaceNeeded = count + DecimalDigits.stringSize(l); ensureCapacityInternal(spaceNeeded); if (isLatin1()) { StringLatin1.getChars(l, spaceNeeded, value); diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index 1c5b3c414ba..7a65046181f 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -27,6 +27,7 @@ import jdk.internal.misc.CDS; import jdk.internal.misc.VM; +import jdk.internal.util.DecimalDigits; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.IntrinsicCandidate; import jdk.internal.vm.annotation.Stable; @@ -427,7 +428,7 @@ private static void formatUnsignedIntUTF16(int val, int shift, byte[] buf, int l */ @IntrinsicCandidate public static String toString(int i) { - int size = stringSize(i); + int size = DecimalDigits.stringSize(i); if (COMPACT_STRINGS) { byte[] buf = new byte[size]; StringLatin1.getChars(i, size, buf); @@ -457,32 +458,6 @@ public static String toUnsignedString(int i) { return Long.toString(toUnsignedLong(i)); } - /** - * Returns the string representation size for a given int value. - * - * @param x int value - * @return string size - * - * @implNote There are other ways to compute this: e.g. binary search, - * but values are biased heavily towards zero, and therefore linear search - * wins. The iteration results are also routinely inlined in the generated - * code after loop unrolling. - */ - static int stringSize(int x) { - int d = 1; - if (x >= 0) { - d = 0; - x = -x; - } - int p = -10; - for (int i = 1; i < 10; i++) { - if (x > p) - return i + d; - p = 10 * p; - } - return 10 + d; - } - /** * Parses the string argument as a signed integer in the radix * specified by the second argument. The characters in the string diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index 70bd2d62add..ee9533b29eb 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -34,6 +34,7 @@ import java.util.Optional; import jdk.internal.misc.CDS; +import jdk.internal.util.DecimalDigits; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.IntrinsicCandidate; import jdk.internal.vm.annotation.Stable; @@ -457,7 +458,7 @@ private static void formatUnsignedLong0UTF16(long val, int shift, byte[] buf, in * @return a string representation of the argument in base 10. */ public static String toString(long i) { - int size = stringSize(i); + int size = DecimalDigits.stringSize(i); if (COMPACT_STRINGS) { byte[] buf = new byte[size]; StringLatin1.getChars(i, size, buf); @@ -487,32 +488,6 @@ public static String toUnsignedString(long i) { return toUnsignedString(i, 10); } - /** - * Returns the string representation size for a given long value. - * - * @param x long value - * @return string size - * - * @implNote There are other ways to compute this: e.g. binary search, - * but values are biased heavily towards zero, and therefore linear search - * wins. The iteration results are also routinely inlined in the generated - * code after loop unrolling. - */ - static int stringSize(long x) { - int d = 1; - if (x >= 0) { - d = 0; - x = -x; - } - long p = -10; - for (int i = 1; i < 19; i++) { - if (x > p) - return i + d; - p = 10 * p; - } - return 19 + d; - } - /** * Parses the string argument as a signed {@code long} in the * radix specified by the second argument. The characters in the diff --git a/src/java.base/share/classes/java/lang/StringConcatHelper.java b/src/java.base/share/classes/java/lang/StringConcatHelper.java index bcd9c65890f..486e115369e 100644 --- a/src/java.base/share/classes/java/lang/StringConcatHelper.java +++ b/src/java.base/share/classes/java/lang/StringConcatHelper.java @@ -26,6 +26,7 @@ package java.lang; import jdk.internal.misc.Unsafe; +import jdk.internal.util.DecimalDigits; import jdk.internal.vm.annotation.ForceInline; import java.lang.invoke.MethodHandle; @@ -96,7 +97,7 @@ static long mix(long lengthCoder, char value) { * @return new length and coder */ static long mix(long lengthCoder, int value) { - return checkOverflow(lengthCoder + Integer.stringSize(value)); + return checkOverflow(lengthCoder + DecimalDigits.stringSize(value)); } /** @@ -107,7 +108,7 @@ static long mix(long lengthCoder, int value) { * @return new length and coder */ static long mix(long lengthCoder, long value) { - return checkOverflow(lengthCoder + Long.stringSize(value)); + return checkOverflow(lengthCoder + DecimalDigits.stringSize(value)); } /** @@ -184,7 +185,7 @@ static long prepend(long indexCoder, byte[] buf, boolean value, String prefix) { * @param indexCoder final char index in the buffer, along with coder packed * into higher bits. * @param buf buffer to append to - * @param value boolean value to encode + * @param value char value to encode * @param prefix a constant to prepend before value * @return updated index (coder value retained) */ @@ -210,7 +211,7 @@ static long prepend(long indexCoder, byte[] buf, char value, String prefix) { * @param indexCoder final char index in the buffer, along with coder packed * into higher bits. * @param buf buffer to append to - * @param value boolean value to encode + * @param value int value to encode * @param prefix a constant to prepend before value * @return updated index (coder value retained) */ @@ -236,7 +237,7 @@ static long prepend(long indexCoder, byte[] buf, int value, String prefix) { * @param indexCoder final char index in the buffer, along with coder packed * into higher bits. * @param buf buffer to append to - * @param value boolean value to encode + * @param value long value to encode * @param prefix a constant to prepend before value * @return updated index (coder value retained) */ diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java index 3157a200278..0947da8ded7 100644 --- a/src/java.base/share/classes/java/lang/System.java +++ b/src/java.base/share/classes/java/lang/System.java @@ -65,7 +65,6 @@ import java.util.ResourceBundle; import java.util.Set; import java.util.WeakHashMap; -import java.util.concurrent.Callable; import java.util.function.Supplier; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Stream; @@ -2264,6 +2263,7 @@ private static class Out extends FileOutputStream { super(fd); } + @Override public void write(int b) throws IOException { boolean attempted = Blocker.begin(); try { @@ -2623,10 +2623,6 @@ public long stringConcatMix(long lengthCoder, char value) { return StringConcatHelper.mix(lengthCoder, value); } - public int stringSize(long i) { - return Long.stringSize(i); - } - public int getCharsLatin1(long i, int index, byte[] buf) { return StringLatin1.getChars(i, index, buf); } @@ -2677,14 +2673,6 @@ public Thread currentCarrierThread() { return Thread.currentCarrierThread(); } - public V executeOnCarrierThread(Callable task) throws Exception { - if (Thread.currentThread() instanceof VirtualThread vthread) { - return vthread.executeOnCarrierThread(task); - } else { - return task.call(); - } - } - public T getCarrierThreadLocal(CarrierThreadLocal local) { return ((ThreadLocal)local).getCarrierThreadLocal(); } diff --git a/src/java.base/share/classes/java/lang/VirtualThread.java b/src/java.base/share/classes/java/lang/VirtualThread.java index cf2fed3bf5d..da05e77f12b 100644 --- a/src/java.base/share/classes/java/lang/VirtualThread.java +++ b/src/java.base/share/classes/java/lang/VirtualThread.java @@ -40,6 +40,7 @@ import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import jdk.internal.event.VirtualThreadEndEvent; import jdk.internal.event.VirtualThreadPinnedEvent; import jdk.internal.event.VirtualThreadStartEvent; @@ -62,14 +63,13 @@ import static java.util.concurrent.TimeUnit.*; /** - * A thread that is scheduled by the Java virtual machine rather than the operating - * system. + * A thread that is scheduled by the Java virtual machine rather than the operating system. */ final class VirtualThread extends BaseVirtualThread { private static final Unsafe U = Unsafe.getUnsafe(); private static final ContinuationScope VTHREAD_SCOPE = new ContinuationScope("VirtualThreads"); private static final ForkJoinPool DEFAULT_SCHEDULER = createDefaultScheduler(); - private static final ScheduledExecutorService UNPARKER = createDelayedTaskScheduler(); + private static final ScheduledExecutorService[] DELAYED_TASK_SCHEDULERS = createDelayedTaskSchedulers(); private static final int TRACE_PINNING_MODE = tracePinningMode(); private static final long STATE = U.objectFieldOffset(VirtualThread.class, "state"); @@ -217,7 +217,7 @@ public void run() { * on the current thread before the task runs or continues. It unmounts when the * task completes or yields. */ - @ChangesCurrentThread + @ChangesCurrentThread // allow mount/unmount to be inlined private void runContinuation() { // the carrier must be a platform thread if (Thread.currentThread().isVirtual()) { @@ -257,42 +257,109 @@ private void runContinuation() { * Submits the runContinuation task to the scheduler. For the default scheduler, * and calling it on a worker thread, the task will be pushed to the local queue, * otherwise it will be pushed to an external submission queue. + * @param scheduler the scheduler + * @param retryOnOOME true to retry indefinitely if OutOfMemoryError is thrown * @throws RejectedExecutionException */ - private void submitRunContinuation() { - try { - scheduler.execute(runContinuation); - } catch (RejectedExecutionException ree) { - submitFailed(ree); - throw ree; + @ChangesCurrentThread + private void submitRunContinuation(Executor scheduler, boolean retryOnOOME) { + boolean done = false; + while (!done) { + try { + // The scheduler's execute method is invoked in the context of the + // carrier thread. For the default scheduler this ensures that the + // current thread is a ForkJoinWorkerThread so the task will be pushed + // to the local queue. For other schedulers, it avoids deadlock that + // would arise due to platform and virtual threads contending for a + // lock on the scheduler's submission queue. + if (currentThread() instanceof VirtualThread vthread) { + vthread.switchToCarrierThread(); + try { + scheduler.execute(runContinuation); + } finally { + switchToVirtualThread(vthread); + } + } else { + scheduler.execute(runContinuation); + } + done = true; + } catch (RejectedExecutionException ree) { + submitFailed(ree); + throw ree; + } catch (OutOfMemoryError e) { + if (retryOnOOME) { + U.park(false, 100_000_000); // 100ms + } else { + throw e; + } + } } } /** * Submits the runContinuation task to given scheduler with a lazy submit. + * If OutOfMemoryError is thrown then the submit will be retried until it succeeds. * @throws RejectedExecutionException * @see ForkJoinPool#lazySubmit(ForkJoinTask) */ private void lazySubmitRunContinuation(ForkJoinPool pool) { + assert Thread.currentThread() instanceof CarrierThread; try { pool.lazySubmit(ForkJoinTask.adapt(runContinuation)); } catch (RejectedExecutionException ree) { submitFailed(ree); throw ree; + } catch (OutOfMemoryError e) { + submitRunContinuation(pool, true); } } /** * Submits the runContinuation task to the given scheduler as an external submit. + * If OutOfMemoryError is thrown then the submit will be retried until it succeeds. * @throws RejectedExecutionException * @see ForkJoinPool#externalSubmit(ForkJoinTask) */ private void externalSubmitRunContinuation(ForkJoinPool pool) { + assert Thread.currentThread() instanceof CarrierThread; try { pool.externalSubmit(ForkJoinTask.adapt(runContinuation)); } catch (RejectedExecutionException ree) { submitFailed(ree); throw ree; + } catch (OutOfMemoryError e) { + submitRunContinuation(pool, true); + } + } + + /** + * Submits the runContinuation task to the scheduler. For the default scheduler, + * and calling it on a worker thread, the task will be pushed to the local queue, + * otherwise it will be pushed to an external submission queue. + * If OutOfMemoryError is thrown then the submit will be retried until it succeeds. + * @throws RejectedExecutionException + */ + private void submitRunContinuation() { + submitRunContinuation(scheduler, true); + } + + /** + * Submits the runContinuation task to the scheduler. For the default scheduler, and + * calling it a virtual thread that uses the default scheduler, the task will be + * pushed to an external submission queue. This method may throw OutOfMemoryError. + * @throws RejectedExecutionException + * @throws OutOfMemoryError + */ + private void externalSubmitRunContinuationOrThrow() { + if (scheduler == DEFAULT_SCHEDULER && currentCarrierThread() instanceof CarrierThread ct) { + try { + ct.getPool().externalSubmit(ForkJoinTask.adapt(runContinuation)); + } catch (RejectedExecutionException ree) { + submitFailed(ree); + throw ree; + } + } else { + submitRunContinuation(scheduler, false); } } @@ -385,6 +452,8 @@ private void mount() { @ChangesCurrentThread @ReservedStackAccess private void unmount() { + assert !Thread.holdsLock(interruptLock); + // set Thread.currentThread() to return the platform thread Thread carrier = this.carrierThread; carrier.setCurrentThread(carrier); @@ -417,7 +486,7 @@ private void switchToCarrierThread() { */ @ChangesCurrentThread @JvmtiMountTransition - private void switchToVirtualThread(VirtualThread vthread) { + private static void switchToVirtualThread(VirtualThread vthread) { Thread carrier = vthread.carrierThread; assert carrier == Thread.currentCarrierThread(); carrier.setCurrentThread(vthread); @@ -474,13 +543,12 @@ private void afterYield() { // may have been unparked while parking if (parkPermit && compareAndSetState(newState, UNPARKED)) { - // lazy submit to continue on the current thread as carrier if possible - if (currentThread() instanceof CarrierThread ct) { + // lazy submit to continue on the current carrier if possible + if (currentThread() instanceof CarrierThread ct && ct.getQueuedTaskCount() == 0) { lazySubmitRunContinuation(ct.getPool()); } else { submitRunContinuation(); } - } return; } @@ -561,8 +629,8 @@ void start(ThreadContainer container) { // scoped values may be inherited inheritScopedValueBindings(container); - // submit task to run thread - submitRunContinuation(); + // submit task to run thread, using externalSubmit if possible + externalSubmitRunContinuationOrThrow(); started = true; } finally { if (!started) { @@ -707,7 +775,7 @@ private Future scheduleUnpark(long nanos) { // need to switch to current carrier thread to avoid nested parking switchToCarrierThread(); try { - return UNPARKER.schedule(this::unpark, nanos, NANOSECONDS); + return schedule(this::unpark, nanos, NANOSECONDS); } finally { switchToVirtualThread(this); } @@ -718,6 +786,7 @@ private Future scheduleUnpark(long nanos) { */ @ChangesCurrentThread private void cancel(Future future) { + assert Thread.currentThread() == this; if (!future.isDone()) { // need to switch to current carrier thread to avoid nested parking switchToCarrierThread(); @@ -730,33 +799,26 @@ private void cancel(Future future) { } /** - * Re-enables this virtual thread for scheduling. If the virtual thread was - * {@link #park() parked} then it will be unblocked, otherwise its next call - * to {@code park} or {@linkplain #parkNanos(long) parkNanos} is guaranteed - * not to block. + * Re-enables this virtual thread for scheduling. If this virtual thread is parked + * then its task is scheduled to continue, otherwise its next call to {@code park} or + * {@linkplain #parkNanos(long) parkNanos} is guaranteed not to block. * @throws RejectedExecutionException if the scheduler cannot accept a task */ @Override - @ChangesCurrentThread void unpark() { - Thread currentThread = Thread.currentThread(); - if (!getAndSetParkPermit(true) && currentThread != this) { + if (!getAndSetParkPermit(true) && currentThread() != this) { int s = state(); - boolean parked = (s == PARKED) || (s == TIMED_PARKED); - if (parked && compareAndSetState(s, UNPARKED)) { - if (currentThread instanceof VirtualThread vthread) { - vthread.switchToCarrierThread(); - try { - submitRunContinuation(); - } finally { - switchToVirtualThread(vthread); - } - } else { - submitRunContinuation(); - } - } else if ((s == PINNED) || (s == TIMED_PINNED)) { + + // unparked while parked + if ((s == PARKED || s == TIMED_PARKED) && compareAndSetState(s, UNPARKED)) { + submitRunContinuation(); + return; + } + + // unparked while parked when pinned + if (s == PINNED || s == TIMED_PINNED) { // unpark carrier thread when pinned - notifyJvmtiDisableSuspend(true); + disableSuspendAndPreempt(); try { synchronized (carrierThreadAccessLock()) { Thread carrier = carrierThread; @@ -765,8 +827,9 @@ void unpark() { } } } finally { - notifyJvmtiDisableSuspend(false); + enableSuspendAndPreempt(); } + return; } } } @@ -859,11 +922,11 @@ boolean joinNanos(long nanos) throws InterruptedException { @Override void blockedOn(Interruptible b) { - notifyJvmtiDisableSuspend(true); + disableSuspendAndPreempt(); try { super.blockedOn(b); } finally { - notifyJvmtiDisableSuspend(false); + enableSuspendAndPreempt(); } } @@ -874,9 +937,9 @@ public void interrupt() { checkAccess(); // if current thread is a virtual thread then prevent it from being - // suspended when entering or holding interruptLock + // suspended or unmounted when entering or holding interruptLock Interruptible blocker; - notifyJvmtiDisableSuspend(true); + disableSuspendAndPreempt(); try { synchronized (interruptLock) { interrupted = true; @@ -890,18 +953,22 @@ public void interrupt() { if (carrier != null) carrier.setInterrupt(); } } finally { - notifyJvmtiDisableSuspend(false); + enableSuspendAndPreempt(); } // notify blocker after releasing interruptLock if (blocker != null) { blocker.postInterrupt(); } + + // make available parking permit, unpark thread if parked + unpark(); + } else { interrupted = true; carrierThread.setInterrupt(); + setParkPermit(true); } - unpark(); } @Override @@ -914,14 +981,14 @@ boolean getAndClearInterrupt() { assert Thread.currentThread() == this; boolean oldValue = interrupted; if (oldValue) { - notifyJvmtiDisableSuspend(true); + disableSuspendAndPreempt(); try { synchronized (interruptLock) { interrupted = false; carrierThread.clearInterrupt(); } } finally { - notifyJvmtiDisableSuspend(false); + enableSuspendAndPreempt(); } } return oldValue; @@ -946,16 +1013,18 @@ Thread.State threadState() { return Thread.State.RUNNABLE; case RUNNING: // if mounted then return state of carrier thread - notifyJvmtiDisableSuspend(true); - try { - synchronized (carrierThreadAccessLock()) { - Thread carrierThread = this.carrierThread; - if (carrierThread != null) { - return carrierThread.threadState(); + if (Thread.currentThread() != this) { + disableSuspendAndPreempt(); + try { + synchronized (carrierThreadAccessLock()) { + Thread carrierThread = this.carrierThread; + if (carrierThread != null) { + return carrierThread.threadState(); + } } + } finally { + enableSuspendAndPreempt(); } - } finally { - notifyJvmtiDisableSuspend(false); } // runnable, mounted return Thread.State.RUNNABLE; @@ -1068,32 +1137,49 @@ public String toString() { sb.append(name); } sb.append("]/"); - Thread carrier = carrierThread; - if (carrier != null) { - // include the carrier thread state and name when mounted - notifyJvmtiDisableSuspend(true); + + // add the carrier state and thread name when mounted + boolean mounted; + if (Thread.currentThread() == this) { + mounted = appendCarrierInfo(sb); + } else { + disableSuspendAndPreempt(); try { synchronized (carrierThreadAccessLock()) { - carrier = carrierThread; - if (carrier != null) { - String stateAsString = carrier.threadState().toString(); - sb.append(stateAsString.toLowerCase(Locale.ROOT)); - sb.append('@'); - sb.append(carrier.getName()); - } + mounted = appendCarrierInfo(sb); } } finally { - notifyJvmtiDisableSuspend(false); + enableSuspendAndPreempt(); } } - // include virtual thread state when not mounted - if (carrier == null) { + + // add virtual thread state when not mounted + if (!mounted) { String stateAsString = threadState().toString(); sb.append(stateAsString.toLowerCase(Locale.ROOT)); } + return sb.toString(); } + /** + * Appends the carrier state and thread name to the string buffer if mounted. + * @return true if mounted, false if not mounted + */ + private boolean appendCarrierInfo(StringBuilder sb) { + assert Thread.currentThread() == this || Thread.holdsLock(carrierThreadAccessLock()); + Thread carrier = carrierThread; + if (carrier != null) { + String stateAsString = carrier.threadState().toString(); + sb.append(stateAsString.toLowerCase(Locale.ROOT)); + sb.append('@'); + sb.append(carrier.getName()); + return true; + } else { + return false; + } + } + @Override public int hashCode() { return (int) threadId(); @@ -1127,6 +1213,22 @@ private Object carrierThreadAccessLock() { return interruptLock; } + /** + * Disallow the current thread be suspended or preempted. + */ + private void disableSuspendAndPreempt() { + notifyJvmtiDisableSuspend(true); + Continuation.pin(); + } + + /** + * Allow the current thread be suspended or preempted. + */ + private void enableSuspendAndPreempt() { + Continuation.unpin(); + notifyJvmtiDisableSuspend(false); + } + // -- wrappers for get/set of state, parking permit, and carrier thread -- private int state() { @@ -1188,10 +1290,16 @@ private void setCarrierThread(Thread carrier) { private static native void registerNatives(); static { registerNatives(); + + // ensure VTHREAD_GROUP is created, may be accessed by JVMTI + var group = Thread.virtualThreadGroup(); + + // ensure VirtualThreadPinnedEvent is loaded/initialized + U.ensureClassInitialized(VirtualThreadPinnedEvent.class); } /** - * Creates the default scheduler. + * Creates the default ForkJoinPool scheduler. */ @SuppressWarnings("removal") private static ForkJoinPool createDefaultScheduler() { @@ -1229,22 +1337,42 @@ private static ForkJoinPool createDefaultScheduler() { } /** - * Creates the ScheduledThreadPoolExecutor used for timed unpark. + * Schedule a runnable task to run after a delay. */ - private static ScheduledExecutorService createDelayedTaskScheduler() { - String propValue = GetPropertyAction.privilegedGetProperty("jdk.unparker.maxPoolSize"); - int poolSize; + private static Future schedule(Runnable command, long delay, TimeUnit unit) { + long tid = Thread.currentThread().threadId(); + int index = (int) tid & (DELAYED_TASK_SCHEDULERS.length - 1); + return DELAYED_TASK_SCHEDULERS[index].schedule(command, delay, unit); + } + + /** + * Creates the ScheduledThreadPoolExecutors used to execute delayed tasks. + */ + private static ScheduledExecutorService[] createDelayedTaskSchedulers() { + String propName = "jdk.virtualThreadScheduler.timerQueues"; + String propValue = GetPropertyAction.privilegedGetProperty(propName); + int queueCount; if (propValue != null) { - poolSize = Integer.parseInt(propValue); + queueCount = Integer.parseInt(propValue); + if (queueCount != Integer.highestOneBit(queueCount)) { + throw new RuntimeException("Value of " + propName + " must be power of 2"); + } } else { - poolSize = 1; + int ncpus = Runtime.getRuntime().availableProcessors(); + queueCount = Math.max(Integer.highestOneBit(ncpus / 4), 1); + } + var schedulers = new ScheduledExecutorService[queueCount]; + for (int i = 0; i < queueCount; i++) { + ScheduledThreadPoolExecutor stpe = (ScheduledThreadPoolExecutor) + Executors.newScheduledThreadPool(1, task -> { + Thread t = InnocuousThread.newThread("VirtualThread-unparker", task); + t.setDaemon(true); + return t; + }); + stpe.setRemoveOnCancelPolicy(true); + schedulers[i] = stpe; } - ScheduledThreadPoolExecutor stpe = (ScheduledThreadPoolExecutor) - Executors.newScheduledThreadPool(poolSize, task -> { - return InnocuousThread.newThread("VirtualThread-unparker", task); - }); - stpe.setRemoveOnCancelPolicy(true); - return stpe; + return schedulers; } /** diff --git a/src/java.base/share/classes/java/lang/classfile/AccessFlags.java b/src/java.base/share/classes/java/lang/classfile/AccessFlags.java index 283094b81d6..6c6ac75e648 100644 --- a/src/java.base/share/classes/java/lang/classfile/AccessFlags.java +++ b/src/java.base/share/classes/java/lang/classfile/AccessFlags.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, 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 @@ -64,52 +64,4 @@ public sealed interface AccessFlags * method, or field} */ AccessFlag.Location location(); - - /** - * {@return an {@linkplain AccessFlags} for a class} - * @param mask the flags to be set, as a bit mask - */ - static AccessFlags ofClass(int mask) { - return new AccessFlagsImpl(AccessFlag.Location.CLASS, mask); - } - - /** - * {@return an {@linkplain AccessFlags} for a class} - * @param flags the flags to be set - */ - static AccessFlags ofClass(AccessFlag... flags) { - return new AccessFlagsImpl(AccessFlag.Location.CLASS, flags); - } - - /** - * {@return an {@linkplain AccessFlags} for a field} - * @param mask the flags to be set, as a bit mask - */ - static AccessFlags ofField(int mask) { - return new AccessFlagsImpl(AccessFlag.Location.FIELD, mask); - } - - /** - * {@return an {@linkplain AccessFlags} for a field} - * @param flags the flags to be set - */ - static AccessFlags ofField(AccessFlag... flags) { - return new AccessFlagsImpl(AccessFlag.Location.FIELD, flags); - } - - /** - * {@return an {@linkplain AccessFlags} for a method} - * @param mask the flags to be set, as a bit mask - */ - static AccessFlags ofMethod(int mask) { - return new AccessFlagsImpl(AccessFlag.Location.METHOD, mask); - } - - /** - * {@return an {@linkplain AccessFlags} for a method} - * @param flags the flags to be set - */ - static AccessFlags ofMethod(AccessFlag... flags) { - return new AccessFlagsImpl(AccessFlag.Location.METHOD, flags); - } } diff --git a/src/java.base/share/classes/java/lang/classfile/AttributedElement.java b/src/java.base/share/classes/java/lang/classfile/AttributedElement.java index 63b2f1e03a9..d66806ca93b 100644 --- a/src/java.base/share/classes/java/lang/classfile/AttributedElement.java +++ b/src/java.base/share/classes/java/lang/classfile/AttributedElement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, 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 @@ -25,6 +25,7 @@ package java.lang.classfile; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Optional; @@ -83,6 +84,6 @@ default > List findAttributes(AttributeMapper attr) list.add(t); } } - return list; + return Collections.unmodifiableList(list); } } diff --git a/src/java.base/share/classes/java/lang/classfile/ClassBuilder.java b/src/java.base/share/classes/java/lang/classfile/ClassBuilder.java index a55b93e7c07..7ad6e94df3f 100644 --- a/src/java.base/share/classes/java/lang/classfile/ClassBuilder.java +++ b/src/java.base/share/classes/java/lang/classfile/ClassBuilder.java @@ -34,6 +34,8 @@ import java.lang.classfile.constantpool.ClassEntry; import java.lang.classfile.constantpool.Utf8Entry; + +import jdk.internal.classfile.impl.AccessFlagsImpl; import jdk.internal.classfile.impl.ChainedClassBuilder; import jdk.internal.classfile.impl.DirectClassBuilder; import jdk.internal.classfile.impl.Util; @@ -73,7 +75,7 @@ default ClassBuilder withVersion(int major, int minor) { * @return this builder */ default ClassBuilder withFlags(int flags) { - return with(AccessFlags.ofClass(flags)); + return with(new AccessFlagsImpl(AccessFlag.Location.CLASS, flags)); } /** @@ -82,7 +84,7 @@ default ClassBuilder withFlags(int flags) { * @return this builder */ default ClassBuilder withFlags(AccessFlag... flags) { - return with(AccessFlags.ofClass(flags)); + return with(new AccessFlagsImpl(AccessFlag.Location.CLASS, flags)); } /** diff --git a/src/java.base/share/classes/java/lang/classfile/ClassSignature.java b/src/java.base/share/classes/java/lang/classfile/ClassSignature.java index 852d5f9baf4..5c55950547e 100644 --- a/src/java.base/share/classes/java/lang/classfile/ClassSignature.java +++ b/src/java.base/share/classes/java/lang/classfile/ClassSignature.java @@ -76,7 +76,7 @@ public static ClassSignature of(List typeParameters, Signature.ClassTypeSig superclassSignature, Signature.ClassTypeSig... superinterfaceSignatures) { return new SignaturesImpl.ClassSignatureImpl( - requireNonNull(typeParameters), + List.copyOf(requireNonNull(typeParameters)), requireNonNull(superclassSignature), List.of(superinterfaceSignatures)); } diff --git a/src/java.base/share/classes/java/lang/classfile/CodeModel.java b/src/java.base/share/classes/java/lang/classfile/CodeModel.java index 45488561c1a..ba816f20805 100644 --- a/src/java.base/share/classes/java/lang/classfile/CodeModel.java +++ b/src/java.base/share/classes/java/lang/classfile/CodeModel.java @@ -43,17 +43,7 @@ @PreviewFeature(feature = PreviewFeature.Feature.CLASSFILE_API) public sealed interface CodeModel extends CompoundElement, AttributedElement, MethodElement - permits CodeAttribute, BufferedCodeBuilder.Model, CodeImpl { - - /** - * {@return the maximum size of the local variable table} - */ - int maxLocals(); - - /** - * {@return the maximum size of the operand stack} - */ - int maxStack(); + permits CodeAttribute, BufferedCodeBuilder.Model { /** * {@return the enclosing method, if known} diff --git a/src/java.base/share/classes/java/lang/classfile/CompoundElement.java b/src/java.base/share/classes/java/lang/classfile/CompoundElement.java index 12904c9dd59..9fcf02204cb 100644 --- a/src/java.base/share/classes/java/lang/classfile/CompoundElement.java +++ b/src/java.base/share/classes/java/lang/classfile/CompoundElement.java @@ -25,6 +25,7 @@ package java.lang.classfile; import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Spliterator; @@ -90,7 +91,7 @@ public void accept(E e) { list.add(e); } }); - return list; + return Collections.unmodifiableList(list); } } diff --git a/src/java.base/share/classes/java/lang/classfile/FieldBuilder.java b/src/java.base/share/classes/java/lang/classfile/FieldBuilder.java index c0e733676f7..d9a8f6b2fc3 100644 --- a/src/java.base/share/classes/java/lang/classfile/FieldBuilder.java +++ b/src/java.base/share/classes/java/lang/classfile/FieldBuilder.java @@ -26,6 +26,8 @@ package java.lang.classfile; import java.lang.classfile.constantpool.Utf8Entry; + +import jdk.internal.classfile.impl.AccessFlagsImpl; import jdk.internal.classfile.impl.ChainedFieldBuilder; import jdk.internal.classfile.impl.TerminalFieldBuilder; import java.lang.reflect.AccessFlag; @@ -55,7 +57,7 @@ public sealed interface FieldBuilder * @return this builder */ default FieldBuilder withFlags(int flags) { - return with(AccessFlags.ofField(flags)); + return with(new AccessFlagsImpl(AccessFlag.Location.FIELD, flags)); } /** @@ -64,7 +66,7 @@ default FieldBuilder withFlags(int flags) { * @return this builder */ default FieldBuilder withFlags(AccessFlag... flags) { - return with(AccessFlags.ofField(flags)); + return with(new AccessFlagsImpl(AccessFlag.Location.FIELD, flags)); } } diff --git a/src/java.base/share/classes/java/lang/classfile/MethodBuilder.java b/src/java.base/share/classes/java/lang/classfile/MethodBuilder.java index 4cf34c95ed0..7c230760f69 100644 --- a/src/java.base/share/classes/java/lang/classfile/MethodBuilder.java +++ b/src/java.base/share/classes/java/lang/classfile/MethodBuilder.java @@ -28,6 +28,8 @@ import java.util.function.Consumer; import java.lang.classfile.constantpool.Utf8Entry; + +import jdk.internal.classfile.impl.AccessFlagsImpl; import jdk.internal.classfile.impl.ChainedMethodBuilder; import jdk.internal.classfile.impl.TerminalMethodBuilder; import java.lang.reflect.AccessFlag; @@ -55,7 +57,7 @@ public sealed interface MethodBuilder * @return this builder */ default MethodBuilder withFlags(int flags) { - return with(AccessFlags.ofMethod(flags)); + return with(new AccessFlagsImpl(AccessFlag.Location.METHOD, flags)); } /** @@ -64,7 +66,7 @@ default MethodBuilder withFlags(int flags) { * @return this builder */ default MethodBuilder withFlags(AccessFlag... flags) { - return with(AccessFlags.ofMethod(flags)); + return with(new AccessFlagsImpl(AccessFlag.Location.METHOD, flags)); } /** diff --git a/src/java.base/share/classes/java/lang/classfile/attribute/CodeAttribute.java b/src/java.base/share/classes/java/lang/classfile/attribute/CodeAttribute.java index 02cbcee810f..7a4f5886580 100644 --- a/src/java.base/share/classes/java/lang/classfile/attribute/CodeAttribute.java +++ b/src/java.base/share/classes/java/lang/classfile/attribute/CodeAttribute.java @@ -47,6 +47,16 @@ public sealed interface CodeAttribute extends Attribute, CodeModel permits BoundAttribute.BoundCodeAttribute { + /** + * {@return the maximum size of the local variable table} + */ + int maxLocals(); + + /** + * {@return the maximum size of the operand stack} + */ + int maxStack(); + /** * {@return The length of the code array in bytes} */ diff --git a/src/java.base/share/classes/java/text/DecimalFormat.java b/src/java.base/share/classes/java/text/DecimalFormat.java index 07a1c6932e7..c04abbd042f 100644 --- a/src/java.base/share/classes/java/text/DecimalFormat.java +++ b/src/java.base/share/classes/java/text/DecimalFormat.java @@ -430,16 +430,15 @@ public class DecimalFormat extends NumberFormat { * for the default {@link java.util.Locale.Category#FORMAT FORMAT} locale. * This is a convenient way to obtain a * DecimalFormat when internationalization is not the main concern. - *

- * To obtain standard formats for a given locale, use the factory methods - * on NumberFormat such as getNumberInstance. These factories will - * return the most appropriate sub-class of NumberFormat for a given - * locale. * - * @see java.text.NumberFormat#getInstance - * @see java.text.NumberFormat#getNumberInstance - * @see java.text.NumberFormat#getCurrencyInstance - * @see java.text.NumberFormat#getPercentInstance + * @apiNote To obtain standard formats for a given locale, use the + * {@code NumberFormat} factory methods such as {@link + * NumberFormat#getNumberInstance(Locale)}. These factories will return the most + * appropriate subclass of NumberFormat for a given locale. + * @see NumberFormat#getInstance(Locale) + * @see NumberFormat#getNumberInstance(Locale) + * @see NumberFormat#getCurrencyInstance(Locale) + * @see NumberFormat#getPercentInstance(Locale) */ @SuppressWarnings("this-escape") public DecimalFormat() { @@ -464,19 +463,18 @@ public DecimalFormat() { * DecimalFormat when internationalization is not the main concern. * The number of maximum integer digits is usually not derived from the pattern. * See the note in the {@link ##patterns Patterns} section for more detail. - *

- * To obtain standard formats for a given locale, use the factory methods - * on NumberFormat such as getNumberInstance. These factories will - * return the most appropriate sub-class of NumberFormat for a given - * locale. * + * @apiNote To obtain standard formats for a given locale, use the + * {@code NumberFormat} factory methods such as {@link + * NumberFormat#getNumberInstance(Locale)}. These factories will return the most + * appropriate subclass of NumberFormat for a given locale. * @param pattern a non-localized pattern string. * @throws NullPointerException if {@code pattern} is null * @throws IllegalArgumentException if the given pattern is invalid. - * @see java.text.NumberFormat#getInstance - * @see java.text.NumberFormat#getNumberInstance - * @see java.text.NumberFormat#getCurrencyInstance - * @see java.text.NumberFormat#getPercentInstance + * @see NumberFormat#getInstance(Locale) + * @see NumberFormat#getNumberInstance(Locale) + * @see NumberFormat#getCurrencyInstance(Locale) + * @see NumberFormat#getPercentInstance(Locale) */ @SuppressWarnings("this-escape") public DecimalFormat(String pattern) { @@ -492,21 +490,20 @@ public DecimalFormat(String pattern) { * behavior of the format. * The number of maximum integer digits is usually not derived from the pattern. * See the note in the {@link ##patterns Patterns} section for more detail. - *

- * To obtain standard formats for a given - * locale, use the factory methods on NumberFormat such as - * getInstance or getCurrencyInstance. If you need only minor adjustments - * to a standard format, you can modify the format returned by - * a NumberFormat factory method. * + * @apiNote To obtain standard formats for a given locale, use the + * {@code NumberFormat} factory methods such as {@link + * NumberFormat#getInstance(Locale)} or {@link NumberFormat#getCurrencyInstance(Locale)}. + * If you need only minor adjustments to a standard format, you can modify + * the format returned by a NumberFormat factory method. * @param pattern a non-localized pattern string * @param symbols the set of symbols to be used * @throws NullPointerException if any of the given arguments is null * @throws IllegalArgumentException if the given pattern is invalid - * @see java.text.NumberFormat#getInstance - * @see java.text.NumberFormat#getNumberInstance - * @see java.text.NumberFormat#getCurrencyInstance - * @see java.text.NumberFormat#getPercentInstance + * @see NumberFormat#getInstance(Locale) + * @see NumberFormat#getNumberInstance(Locale) + * @see NumberFormat#getCurrencyInstance(Locale) + * @see NumberFormat#getPercentInstance(Locale) * @see java.text.DecimalFormatSymbols */ @SuppressWarnings("this-escape") @@ -522,8 +519,8 @@ public DecimalFormat (String pattern, DecimalFormatSymbols symbols) { * Formats a number and appends the resulting text to the given string * buffer. * The number can be of any subclass of {@link java.lang.Number}. - *

- * This implementation uses the maximum precision permitted. + * + * @implSpec This implementation uses the maximum precision permitted. * @param number the number to format * @param toAppendTo the {@code StringBuffer} to which the formatted * text is to be appended diff --git a/src/java.base/share/classes/java/time/LocalDate.java b/src/java.base/share/classes/java/time/LocalDate.java index b0d7ad33c8a..2d71fb1ceca 100644 --- a/src/java.base/share/classes/java/time/LocalDate.java +++ b/src/java.base/share/classes/java/time/LocalDate.java @@ -2147,11 +2147,20 @@ public int hashCode() { */ @Override public String toString() { + var buf = new StringBuilder(10); + formatTo(buf); + return buf.toString(); + } + + /** + * Prints the toString result to the given buf, avoiding extra string allocations. + * Requires extra capacity of 10 to avoid StringBuilder reallocation. + */ + void formatTo(StringBuilder buf) { int yearValue = year; int monthValue = month; int dayValue = day; int absYear = Math.abs(yearValue); - StringBuilder buf = new StringBuilder(10); if (absYear < 1000) { if (yearValue < 0) { buf.append('-'); @@ -2164,11 +2173,10 @@ public String toString() { } buf.append(yearValue); } - return buf.append(monthValue < 10 ? "-0" : "-") - .append(monthValue) - .append(dayValue < 10 ? "-0" : "-") - .append(dayValue) - .toString(); + buf.append(monthValue < 10 ? "-0" : "-") + .append(monthValue) + .append(dayValue < 10 ? "-0" : "-") + .append(dayValue); } //----------------------------------------------------------------------- diff --git a/src/java.base/share/classes/java/time/LocalDateTime.java b/src/java.base/share/classes/java/time/LocalDateTime.java index 024aa1dacbe..d14afb7d78f 100644 --- a/src/java.base/share/classes/java/time/LocalDateTime.java +++ b/src/java.base/share/classes/java/time/LocalDateTime.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, 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 @@ -1965,7 +1965,11 @@ public int hashCode() { */ @Override public String toString() { - return date.toString() + 'T' + time.toString(); + var buf = new StringBuilder(29); + date.formatTo(buf); + buf.append('T'); + time.formatTo(buf); + return buf.toString(); } //----------------------------------------------------------------------- diff --git a/src/java.base/share/classes/java/time/LocalTime.java b/src/java.base/share/classes/java/time/LocalTime.java index fd2c5ce4d45..f28f8d3c599 100644 --- a/src/java.base/share/classes/java/time/LocalTime.java +++ b/src/java.base/share/classes/java/time/LocalTime.java @@ -1631,7 +1631,16 @@ public int hashCode() { */ @Override public String toString() { - StringBuilder buf = new StringBuilder(18); + var buf = new StringBuilder(18); + formatTo(buf); + return buf.toString(); + } + + /** + * Prints the toString result to the given buf, avoiding extra string allocations. + * Requires extra capacity of 18 to avoid StringBuilder reallocation. + */ + void formatTo(StringBuilder buf) { int hourValue = hour; int minuteValue = minute; int secondValue = second; @@ -1657,7 +1666,6 @@ public String toString() { buf.append(digits); } } - return buf.toString(); } //----------------------------------------------------------------------- diff --git a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java index 46a80deee00..33dea914b66 100644 --- a/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java +++ b/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java @@ -121,6 +121,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import jdk.internal.util.DecimalDigits; + import sun.text.spi.JavaTimeDateTimePatternProvider; import sun.util.locale.provider.CalendarDataUtility; import sun.util.locale.provider.LocaleProviderAdapter; @@ -2908,24 +2910,6 @@ NumberPrinterParser withSubsequentWidth(int subsequentWidth) { return new NumberPrinterParser(field, minWidth, maxWidth, signStyle, this.subsequentWidth + subsequentWidth); } - /* - * Copied from Long.stringSize - */ - private static int stringSize(long x) { - int d = 1; - if (x >= 0) { - d = 0; - x = -x; - } - long p = -10; - for (int i = 1; i < 19; i++) { - if (x > p) - return i + d; - p = 10 * p; - } - return 19 + d; - } - @Override public boolean format(DateTimePrintContext context, StringBuilder buf) { Long valueLong = context.getValue(field); @@ -2934,7 +2918,7 @@ public boolean format(DateTimePrintContext context, StringBuilder buf) { } long value = getValue(context, valueLong); DecimalStyle decimalStyle = context.getDecimalStyle(); - int size = stringSize(value); + int size = DecimalDigits.stringSize(value); if (value < 0) { size--; } @@ -3369,17 +3353,6 @@ boolean isFixedWidth(DateTimeParseContext context) { return false; } - // Simplified variant of Integer.stringSize that assumes positive values - private static int stringSize(int x) { - int p = 10; - for (int i = 1; i < 10; i++) { - if (x < p) - return i; - p = 10 * p; - } - return 10; - } - private static final int[] TENS = new int[] { 1, 10, @@ -3400,7 +3373,7 @@ public boolean format(DateTimePrintContext context, StringBuilder buf) { } int val = field.range().checkValidIntValue(value, field); DecimalStyle decimalStyle = context.getDecimalStyle(); - int stringSize = stringSize(val); + int stringSize = DecimalDigits.stringSize(val); char zero = decimalStyle.getZeroDigit(); if (val == 0 || stringSize < 10 - maxWidth) { // 0 or would round down to 0 diff --git a/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java b/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java index 28fe0d4dca3..d6c45c7b04f 100644 --- a/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java +++ b/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java @@ -35,7 +35,9 @@ package java.util.concurrent.locks; -import jdk.internal.misc.VirtualThreads; +import java.util.concurrent.TimeUnit; +import jdk.internal.access.JavaLangAccess; +import jdk.internal.access.SharedSecrets; import jdk.internal.misc.Unsafe; /** @@ -176,7 +178,7 @@ public static void setCurrentBlocker(Object blocker) { public static void unpark(Thread thread) { if (thread != null) { if (thread.isVirtual()) { - VirtualThreads.unpark(thread); + JLA.unparkVirtualThread(thread); } else { U.unpark(thread); } @@ -216,7 +218,7 @@ public static void park(Object blocker) { setBlocker(t, blocker); try { if (t.isVirtual()) { - VirtualThreads.park(); + JLA.parkVirtualThread(); } else { U.park(false, 0L); } @@ -264,7 +266,7 @@ public static void parkNanos(Object blocker, long nanos) { setBlocker(t, blocker); try { if (t.isVirtual()) { - VirtualThreads.park(nanos); + JLA.parkVirtualThread(nanos); } else { U.park(false, nanos); } @@ -311,11 +313,7 @@ public static void parkUntil(Object blocker, long deadline) { Thread t = Thread.currentThread(); setBlocker(t, blocker); try { - if (t.isVirtual()) { - VirtualThreads.parkUntil(deadline); - } else { - U.park(true, deadline); - } + parkUntil(deadline); } finally { setBlocker(t, null); } @@ -366,7 +364,7 @@ public static Object getBlocker(Thread t) { */ public static void park() { if (Thread.currentThread().isVirtual()) { - VirtualThreads.park(); + JLA.parkVirtualThread(); } else { U.park(false, 0L); } @@ -405,7 +403,7 @@ public static void park() { public static void parkNanos(long nanos) { if (nanos > 0) { if (Thread.currentThread().isVirtual()) { - VirtualThreads.park(nanos); + JLA.parkVirtualThread(nanos); } else { U.park(false, nanos); } @@ -444,7 +442,8 @@ public static void parkNanos(long nanos) { */ public static void parkUntil(long deadline) { if (Thread.currentThread().isVirtual()) { - VirtualThreads.parkUntil(deadline); + long millis = deadline - System.currentTimeMillis(); + JLA.parkVirtualThread(TimeUnit.MILLISECONDS.toNanos(millis)); } else { U.park(true, deadline); } @@ -462,4 +461,5 @@ static final long getThreadId(Thread thread) { private static final long PARKBLOCKER = U.objectFieldOffset(Thread.class, "parkBlocker"); + private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); } diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java index 2254c065e2e..24aeabf6d36 100644 --- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java +++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java @@ -42,7 +42,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.RejectedExecutionException; import java.util.stream.Stream; @@ -459,8 +458,6 @@ public interface JavaLangAccess { */ Object classData(Class c); - int stringSize(long i); - int getCharsLatin1(long i, int index, byte[] buf); int getCharsUTF16(long i, int index, byte[] buf); @@ -504,11 +501,6 @@ public interface JavaLangAccess { */ Thread currentCarrierThread(); - /** - * Executes the given value returning task on the current carrier thread. - */ - V executeOnCarrierThread(Callable task) throws Exception; - /** * Returns the value of the current carrier thread's copy of a thread-local. */ diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractUnboundModel.java b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractUnboundModel.java index 788bdffe1cd..9502ff7b246 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractUnboundModel.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/AbstractUnboundModel.java @@ -24,6 +24,7 @@ */ package jdk.internal.classfile.impl; +import java.util.Collections; import java.util.List; import java.util.function.Consumer; import java.util.stream.Stream; @@ -35,13 +36,13 @@ public abstract sealed class AbstractUnboundModel extends AbstractElement - implements CompoundElement, AttributedElement, Util.Writable + implements CompoundElement, AttributedElement permits BufferedCodeBuilder.Model, BufferedFieldBuilder.Model, BufferedMethodBuilder.Model { - private final List elements; + final List elements; private List> attributes; public AbstractUnboundModel(List elements) { - this.elements = elements; + this.elements = Collections.unmodifiableList(elements); } @Override @@ -63,8 +64,11 @@ public List elementList() { public List> attributes() { if (attributes == null) attributes = elements.stream() - .filter(e -> e instanceof Attribute) - .>map(e -> (Attribute) e) + .>mapMulti((e, sink) -> { + if (e instanceof Attribute attr) { + sink.accept(attr); + } + }) .toList(); return attributes; } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BoundAttribute.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BoundAttribute.java index a0428d9d09d..d5ed0c14bce 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BoundAttribute.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BoundAttribute.java @@ -39,6 +39,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.function.Function; @@ -134,7 +135,6 @@ public static List> readAttributes(AttributedElement enclosing, Cla var filled = new ArrayList>(size); int p = pos + 2; int cfLen = reader.classfileLength(); - var apo = ((ClassReaderImpl)reader).context().attributesProcessingOption(); for (int i = 0; i < size; ++i) { Utf8Entry name = reader.readEntry(p, Utf8Entry.class); int len = reader.readInt(p + 2); @@ -148,7 +148,7 @@ public static List> readAttributes(AttributedElement enclosing, Cla mapper = customAttributes.apply(name); } if (mapper != null) { - filled.add((Attribute)mapper.readAttribute(enclosing, reader, p)); + filled.add((Attribute) Objects.requireNonNull(mapper.readAttribute(enclosing, reader, p))); } else { AttributeMapper fakeMapper = new AttributeMapper<>() { @Override diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedCodeBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedCodeBuilder.java index 4743c58607c..b690f8dbfe7 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedCodeBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedCodeBuilder.java @@ -32,9 +32,6 @@ import java.lang.classfile.Label; import java.lang.classfile.MethodModel; import java.lang.classfile.instruction.ExceptionCatch; -import java.lang.classfile.instruction.IncrementInstruction; -import java.lang.classfile.instruction.LoadInstruction; -import java.lang.classfile.instruction.StoreInstruction; import java.util.ArrayList; import java.util.List; @@ -47,7 +44,6 @@ public final class BufferedCodeBuilder private final ClassFileImpl context; private final List elements = new ArrayList<>(); private final LabelImpl startLabel, endLabel; - private final CodeModel original; private final MethodInfo methodInfo; private boolean finished; private int maxLocals; @@ -60,12 +56,8 @@ public BufferedCodeBuilder(MethodInfo methodInfo, this.context = context; this.startLabel = new LabelImpl(this, -1); this.endLabel = new LabelImpl(this, -1); - this.original = original; this.methodInfo = methodInfo; - this.maxLocals = Util.maxLocals(methodInfo.methodFlags(), methodInfo.methodTypeSymbol()); - if (original != null) - this.maxLocals = Math.max(this.maxLocals, original.maxLocals()); - + this.maxLocals = TerminalCodeBuilder.setupTopLocal(methodInfo, original); elements.add(startLabel); } @@ -156,33 +148,22 @@ public final class Model implements CodeModel { private Model() { - super(elements); + super(BufferedCodeBuilder.this.elements); } @Override public List exceptionHandlers() { return elements.stream() - .filter(x -> x instanceof ExceptionCatch) - .map(x -> (ExceptionCatch) x) + .mapMulti((x, sink) -> { + if (x instanceof ExceptionCatch ec) { + sink.accept(ec); + } + }) .toList(); } - @Override - public int maxLocals() { - for (CodeElement element : elements) { - if (element instanceof LoadInstruction i) - maxLocals = Math.max(maxLocals, i.slot() + i.typeKind().slotSize()); - else if (element instanceof StoreInstruction i) - maxLocals = Math.max(maxLocals, i.slot() + i.typeKind().slotSize()); - else if (element instanceof IncrementInstruction i) - maxLocals = Math.max(maxLocals, i.slot() + 1); - } - return maxLocals; - } - - @Override - public int maxStack() { - throw new UnsupportedOperationException("nyi"); + int curTopLocal() { + return BufferedCodeBuilder.this.curTopLocal(); } @Override @@ -200,11 +181,6 @@ public void accept(CodeBuilder cb) { }); } - @Override - public void writeTo(BufWriterImpl buf) { - DirectCodeBuilder.build(methodInfo, cb -> elements.forEach(cb), constantPool, context, null).writeTo(buf); - } - @Override public String toString() { return String.format("CodeModel[id=%s]", Integer.toHexString(System.identityHashCode(this))); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedFieldBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedFieldBuilder.java index 660a999f2a3..d165ddd3928 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedFieldBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedFieldBuilder.java @@ -24,6 +24,7 @@ */ package jdk.internal.classfile.impl; +import java.lang.reflect.AccessFlag; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -50,7 +51,7 @@ public BufferedFieldBuilder(SplitConstantPool constantPool, this.context = context; this.name = name; this.desc = type; - this.flags = AccessFlags.ofField(); + this.flags = new AccessFlagsImpl(AccessFlag.Location.FIELD); } @Override @@ -78,7 +79,7 @@ public final class Model extends AbstractUnboundModel implements FieldModel { public Model() { - super(elements); + super(BufferedFieldBuilder.this.elements); } @Override @@ -103,7 +104,7 @@ public Utf8Entry fieldType() { @Override public void writeTo(DirectClassBuilder builder) { - builder.withField(name, desc, new Consumer() { + builder.withField(name, desc, new Consumer<>() { @Override public void accept(FieldBuilder fieldBuilder) { elements.forEach(fieldBuilder); @@ -111,13 +112,6 @@ public void accept(FieldBuilder fieldBuilder) { }); } - @Override - public void writeTo(BufWriterImpl buf) { - DirectFieldBuilder fb = new DirectFieldBuilder(constantPool, context, name, desc, null); - elements.forEach(fb); - fb.writeTo(buf); - } - @Override public String toString() { return String.format("FieldModel[fieldName=%s, fieldType=%s, flags=%d]", name.stringValue(), desc.stringValue(), flags.flagsMask()); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedMethodBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedMethodBuilder.java index 880c6717db8..4a9e9b111ce 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedMethodBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/BufferedMethodBuilder.java @@ -66,7 +66,7 @@ public BufferedMethodBuilder(SplitConstantPool constantPool, this.context = context; this.name = nameInfo; this.desc = typeInfo; - this.flags = AccessFlags.ofMethod(flags); + this.flags = new AccessFlagsImpl(AccessFlag.Location.METHOD, flags); this.original = original; } @@ -156,7 +156,7 @@ public final class Model extends AbstractUnboundModel implements MethodModel, MethodInfo { public Model() { - super(elements); + super(BufferedMethodBuilder.this.elements); } @Override @@ -196,7 +196,11 @@ public int parameterSlot(int paramNo) { @Override public Optional code() { - throw new UnsupportedOperationException("nyi"); + return elements.stream().mapMulti((e, sink) -> { + if (e instanceof CodeModel cm) { + sink.accept(cm); + } + }).findFirst(); } @Override @@ -209,13 +213,6 @@ public void accept(MethodBuilder mb) { }); } - @Override - public void writeTo(BufWriterImpl buf) { - DirectMethodBuilder mb = new DirectMethodBuilder(constantPool, context, name, desc, methodFlags(), null); - elements.forEach(mb); - mb.writeTo(buf); - } - @Override public String toString() { return String.format("MethodModel[methodName=%s, methodType=%s, flags=%d]", diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassImpl.java index eab283a8c60..c6b9be5c76a 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/ClassImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/ClassImpl.java @@ -101,7 +101,7 @@ public int classfileLength() { @Override public AccessFlags flags() { - return AccessFlags.ofClass(reader.flags()); + return new AccessFlagsImpl(AccessFlag.Location.CLASS, reader.flags()); } @Override diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java index 7669cbfdd30..d4719fad0b1 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/CodeImpl.java @@ -43,7 +43,7 @@ public final class CodeImpl extends BoundAttribute.BoundCodeAttribute - implements CodeModel, LabelContext { + implements LabelContext { static final Instruction[] SINGLETON_INSTRUCTIONS = new Instruction[256]; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java index 536750ebd2f..a7652ac369b 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/DirectCodeBuilder.java @@ -127,12 +127,10 @@ private DirectCodeBuilder(MethodInfo methodInfo, this.transformFwdJumps = transformFwdJumps; this.transformBackJumps = context.shortJumpsOption() == ClassFile.ShortJumpsOption.FIX_SHORT_JUMPS; bytecodesBufWriter = (original instanceof CodeImpl cai) ? new BufWriterImpl(constantPool, context, cai.codeLength()) - : new BufWriterImpl(constantPool, context); + : new BufWriterImpl(constantPool, context); this.startLabel = new LabelImpl(this, 0); this.endLabel = new LabelImpl(this, -1); - this.topLocal = Util.maxLocals(methodInfo.methodFlags(), methodInfo.methodTypeSymbol()); - if (original != null) - this.topLocal = Math.max(this.topLocal, original.maxLocals()); + this.topLocal = TerminalCodeBuilder.setupTopLocal(methodInfo, original); } @Override @@ -312,8 +310,9 @@ public void writeBody(BufWriterImpl b) { private void writeCounters(boolean codeMatch, BufWriterImpl buf) { if (codeMatch) { - buf.writeU2(original.maxStack()); - buf.writeU2(original.maxLocals()); + var originalAttribute = (CodeImpl) original; + buf.writeU2(originalAttribute.maxStack()); + buf.writeU2(originalAttribute.maxLocals()); } else { StackCounter cntr = StackCounter.of(DirectCodeBuilder.this, buf); buf.writeU2(cntr.maxStack()); diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/FieldImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/FieldImpl.java index ab42b96084a..a71593a5712 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/FieldImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/FieldImpl.java @@ -24,6 +24,7 @@ */ package jdk.internal.classfile.impl; +import java.lang.reflect.AccessFlag; import java.util.List; import java.util.Optional; import java.util.function.Consumer; @@ -48,7 +49,7 @@ public FieldImpl(ClassReader reader, int startPos, int endPos, int attributesPos @Override public AccessFlags flags() { - return AccessFlags.ofField(reader.readU2(startPos)); + return new AccessFlagsImpl(AccessFlag.Location.FIELD, reader.readU2(startPos)); } @Override diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/MethodImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/MethodImpl.java index 40223d58d6b..032b18600a8 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/MethodImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/MethodImpl.java @@ -28,6 +28,7 @@ import java.lang.classfile.*; import java.lang.classfile.constantpool.Utf8Entry; +import java.lang.reflect.AccessFlag; import java.util.List; import java.util.Optional; import java.util.function.Consumer; @@ -51,7 +52,7 @@ public MethodImpl(ClassReader reader, int startPos, int endPos, int attrStart) { @Override public AccessFlags flags() { - return AccessFlags.ofMethod(reader.readU2(startPos)); + return new AccessFlagsImpl(AccessFlag.Location.METHOD, reader.readU2(startPos)); } @Override diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java index 2ea8922d326..d0bf91171ac 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/StackMapDecoder.java @@ -285,5 +285,9 @@ public static record StackMapFrameImpl(int frameType, List locals, List stack) implements StackMapFrameInfo { + public StackMapFrameImpl { + locals = List.copyOf(locals); + stack = List.copyOf(stack); + } } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/TerminalCodeBuilder.java b/src/java.base/share/classes/jdk/internal/classfile/impl/TerminalCodeBuilder.java index 6e3ca516bf4..054ea919928 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/TerminalCodeBuilder.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/TerminalCodeBuilder.java @@ -25,8 +25,24 @@ package jdk.internal.classfile.impl; import java.lang.classfile.CodeBuilder; +import java.lang.classfile.CodeModel; +import java.lang.classfile.attribute.CodeAttribute; public sealed interface TerminalCodeBuilder extends CodeBuilder, LabelContext permits DirectCodeBuilder, BufferedCodeBuilder { int curTopLocal(); + + static int setupTopLocal(MethodInfo methodInfo, CodeModel original) { + int paramSlots = Util.maxLocals(methodInfo.methodFlags(), methodInfo.methodTypeSymbol()); + if (original == null) { + return paramSlots; + } + if (original instanceof CodeAttribute attr) { + return Math.max(paramSlots, attr.maxLocals()); + } + if (original instanceof BufferedCodeBuilder.Model buffered) { + return Math.max(paramSlots, buffered.curTopLocal()); + } + throw new InternalError("Unknown code model " + original); + } } diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/UnboundAttribute.java b/src/java.base/share/classes/jdk/internal/classfile/impl/UnboundAttribute.java index 6d71497f693..6b64511fd5a 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/UnboundAttribute.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/UnboundAttribute.java @@ -92,6 +92,8 @@ import java.lang.classfile.constantpool.PackageEntry; import java.lang.classfile.constantpool.Utf8Entry; +import jdk.internal.access.SharedSecrets; + public abstract sealed class UnboundAttribute> extends AbstractElement implements Attribute, Util.Writable { @@ -627,7 +629,13 @@ public static final class UnboundRuntimeInvisibleParameterAnnotationsAttribute public UnboundRuntimeInvisibleParameterAnnotationsAttribute(List> elements) { super(Attributes.runtimeInvisibleParameterAnnotations()); - this.elements = List.copyOf(elements); + // deep copy + var array = elements.toArray().clone(); + for (int i = 0; i < array.length; i++) { + array[i] = List.copyOf((List) array[i]); + } + + this.elements = SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArray(array); } @Override diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java index 33733c5572a..74cdb881a74 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java @@ -26,6 +26,7 @@ import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.function.Consumer; import java.lang.classfile.ClassHierarchyResolver; @@ -133,7 +134,7 @@ public static List verify(ClassModel classModel, ClassHierarchyReso errors.addAll(inference_verify(klass)); } } - return errors; + return Collections.unmodifiableList(errors); } finally { log_info(logger, "End class verification for: %s", clsName); } diff --git a/src/java.base/share/classes/jdk/internal/misc/VirtualThreads.java b/src/java.base/share/classes/jdk/internal/misc/VirtualThreads.java deleted file mode 100644 index 04a93afad94..00000000000 --- a/src/java.base/share/classes/jdk/internal/misc/VirtualThreads.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2018, 2022, 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. - */ - -package jdk.internal.misc; - -import java.util.concurrent.TimeUnit; -import java.util.concurrent.RejectedExecutionException; -import jdk.internal.access.JavaLangAccess; -import jdk.internal.access.SharedSecrets; - -/** - * Defines static methods to support execution in the context of a virtual thread. - */ -public final class VirtualThreads { - private static final JavaLangAccess JLA; - static { - JLA = SharedSecrets.getJavaLangAccess(); - if (JLA == null) { - throw new InternalError("JavaLangAccess not setup"); - } - } - private VirtualThreads() { } - - /** - * Parks the current virtual thread until it is unparked or interrupted. - * If already unparked then the parking permit is consumed and this method - * completes immediately (meaning it doesn't yield). It also completes - * immediately if the interrupt status is set. - * @throws WrongThreadException if the current thread is not a virtual thread - */ - public static void park() { - JLA.parkVirtualThread(); - } - - /** - * Parks the current virtual thread up to the given waiting time or until it - * is unparked or interrupted. If already unparked then the parking permit is - * consumed and this method completes immediately (meaning it doesn't yield). - * It also completes immediately if the interrupt status is set or the waiting - * time is {@code <= 0}. - * @param nanos the maximum number of nanoseconds to wait - * @throws WrongThreadException if the current thread is not a virtual thread - */ - public static void park(long nanos) { - JLA.parkVirtualThread(nanos); - } - - /** - * Parks the current virtual thread until the given deadline or until is is - * unparked or interrupted. If already unparked then the parking permit is - * consumed and this method completes immediately (meaning it doesn't yield). - * It also completes immediately if the interrupt status is set or the - * deadline has past. - * @param deadline absolute time, in milliseconds, from the epoch - * @throws WrongThreadException if the current thread is not a virtual thread - */ - public static void parkUntil(long deadline) { - long millis = deadline - System.currentTimeMillis(); - long nanos = TimeUnit.NANOSECONDS.convert(millis, TimeUnit.MILLISECONDS); - park(nanos); - } - - /** - * Re-enables a virtual thread for scheduling. If the thread was parked then - * it will be unblocked, otherwise its next attempt to park will not block - * @param thread the virtual thread to unpark - * @throws IllegalArgumentException if the thread is not a virtual thread - * @throws RejectedExecutionException if the scheduler cannot accept a task - */ - public static void unpark(Thread thread) { - JLA.unparkVirtualThread(thread); - } -} diff --git a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java index ea1c9fec730..83438e59b82 100644 --- a/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/DecimalDigits.java @@ -110,4 +110,30 @@ public static int stringSize(int x) { } return 10 + d; } + + /** + * Returns the string representation size for a given long value. + * + * @param x long value + * @return string size + * + * @implNote There are other ways to compute this: e.g. binary search, + * but values are biased heavily towards zero, and therefore linear search + * wins. The iteration results are also routinely inlined in the generated + * code after loop unrolling. + */ + public static int stringSize(long x) { + int d = 1; + if (x >= 0) { + d = 0; + x = -x; + } + long p = -10; + for (int i = 1; i < 19; i++) { + if (x > p) + return i + d; + p = 10 * p; + } + return 19 + d; + } } diff --git a/src/java.base/share/classes/sun/security/util/DerInputStream.java b/src/java.base/share/classes/sun/security/util/DerInputStream.java index 8df4e80eb97..e0519f0fa09 100644 --- a/src/java.base/share/classes/sun/security/util/DerInputStream.java +++ b/src/java.base/share/classes/sun/security/util/DerInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2024, 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 @@ -114,7 +114,7 @@ public DerValue getDerValue() throws IOException { // to the end of return value by DerIndefLenConverter::convertBytes // and stay inside result.buffer. int unused = result.buffer.length - result.end; - this.pos = this.data.length - unused; + this.pos = this.end - unused; } else { this.pos = result.end; } diff --git a/src/java.base/windows/classes/sun/nio/fs/WindowsSecurity.java b/src/java.base/windows/classes/sun/nio/fs/WindowsSecurity.java index 19e3e62412b..9bcd800e13c 100644 --- a/src/java.base/windows/classes/sun/nio/fs/WindowsSecurity.java +++ b/src/java.base/windows/classes/sun/nio/fs/WindowsSecurity.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2024, 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 @@ -25,8 +25,8 @@ package sun.nio.fs; -import jdk.internal.misc.PreviewFeatures; import jdk.internal.vm.Continuation; +import jdk.internal.vm.ContinuationSupport; import static sun.nio.fs.WindowsNativeDispatcher.*; import static sun.nio.fs.WindowsConstants.*; @@ -106,7 +106,7 @@ static Privilege enablePrivilege(String priv) { final boolean needToRevert = elevated; // prevent yielding with privileges - if (PreviewFeatures.isEnabled()) + if (ContinuationSupport.isSupported()) Continuation.pin(); return () -> { @@ -126,7 +126,7 @@ else if (needToRevert) } } finally { LocalFree(pLuid); - if (PreviewFeatures.isEnabled()) + if (ContinuationSupport.isSupported()) Continuation.unpin(); } }; diff --git a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKPainter.java b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKPainter.java index e643d3d28ff..5d77800f88e 100644 --- a/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKPainter.java +++ b/src/java.desktop/share/classes/com/sun/java/swing/plaf/gtk/GTKPainter.java @@ -610,10 +610,10 @@ public void paintPopupMenuBackground(SynthContext context, Graphics g, x + insets.left, y + insets.top, w - insets.left - insets.right, h - insets.top - insets.bottom); BufferedImage img = ENGINE.finishPainting(); - if(!isHW) { + if (!isHW && img != null) { int border = img.getRGB(0, h / 2); - if (img != null && border == img.getRGB(w / 2, h / 2)) { - // fix no menu borders in Adwaita theme + if (border == img.getRGB(w / 2, h / 2)) { + // fix no menu borders Graphics g2 = img.getGraphics(); Color c = new Color(border); g2.setColor(new Color(Math.max((int) (c.getRed() * 0.8), 0), diff --git a/src/java.desktop/share/classes/javax/swing/text/html/CSS.java b/src/java.desktop/share/classes/javax/swing/text/html/CSS.java index 595799926a3..8239565a53f 100644 --- a/src/java.desktop/share/classes/javax/swing/text/html/CSS.java +++ b/src/java.desktop/share/classes/javax/swing/text/html/CSS.java @@ -844,6 +844,10 @@ Object getInternalCSSValue(CSS.Attribute key, String value) { } static Object mergeTextDecoration(String value) { + if (value.startsWith("none")) { + return null; + } + boolean underline = value.contains("underline"); boolean strikeThrough = value.contains("line-through"); if (!underline && !strikeThrough) { diff --git a/src/java.desktop/share/classes/javax/swing/text/html/StyleSheet.java b/src/java.desktop/share/classes/javax/swing/text/html/StyleSheet.java index 0790060f5e8..9b8e33b4c6b 100644 --- a/src/java.desktop/share/classes/javax/swing/text/html/StyleSheet.java +++ b/src/java.desktop/share/classes/javax/swing/text/html/StyleSheet.java @@ -2391,7 +2391,7 @@ void drawShape(Graphics g, CSS.Value type, int ax, int ay, int aw, // Position shape to the middle of the html text. int gap = isLeftToRight ? - (bulletgap + size/3) : (aw + bulletgap); int x = ax + gap; - int y = Math.max(ay, ay + (int)Math.ceil(ah/2)); + int y = Math.max(ay, ay + ah/2); if (type == CSS.Value.SQUARE) { g.drawRect(x, y, size/3, size/3); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java index 28768f2517b..8576d8cfb2f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java @@ -62,6 +62,8 @@ import static com.sun.tools.javac.code.Type.*; import static com.sun.tools.javac.code.TypeTag.*; import static com.sun.tools.javac.jvm.ClassFile.externalize; +import static com.sun.tools.javac.main.Option.DOE; + import com.sun.tools.javac.resources.CompilerProperties.Fragments; /** @@ -99,6 +101,7 @@ public class Types { final Name capturedName; public final Warner noWarnings; + public final boolean dumpStacktraceOnError; // public static Types instance(Context context) { @@ -120,6 +123,8 @@ protected Types(Context context) { messages = JavacMessages.instance(context); diags = JCDiagnostic.Factory.instance(context); noWarnings = new Warner(null); + Options options = Options.instance(context); + dumpStacktraceOnError = options.isSet("dev") || options.isSet(DOE); } // @@ -634,12 +639,13 @@ public boolean isConvertible(Type t, Type s) { * wraps a diagnostic that can be used to generate more details error * messages. */ - public static class FunctionDescriptorLookupError extends RuntimeException { + public static class FunctionDescriptorLookupError extends CompilerInternalException { private static final long serialVersionUID = 0; transient JCDiagnostic diagnostic; - FunctionDescriptorLookupError() { + FunctionDescriptorLookupError(boolean dumpStackTraceOnError) { + super(dumpStackTraceOnError); this.diagnostic = null; } @@ -651,12 +657,6 @@ FunctionDescriptorLookupError setMessage(JCDiagnostic diag) { public JCDiagnostic getDiagnostic() { return diagnostic; } - - @Override - public Throwable fillInStackTrace() { - // This is an internal exception; the stack trace is irrelevant. - return this; - } } /** @@ -809,7 +809,7 @@ FunctionDescriptorLookupError failure(String msg, Object... args) { } FunctionDescriptorLookupError failure(JCDiagnostic diag) { - return new FunctionDescriptorLookupError().setMessage(diag); + return new FunctionDescriptorLookupError(Types.this.dumpStacktraceOnError).setMessage(diag); } } @@ -5107,41 +5107,30 @@ public RetentionPolicy getRetention(TypeSymbol sym) { // - public abstract static class SignatureGenerator { + public abstract class SignatureGenerator { - public static class InvalidSignatureException extends RuntimeException { + public class InvalidSignatureException extends CompilerInternalException { private static final long serialVersionUID = 0; private final transient Type type; - InvalidSignatureException(Type type) { + InvalidSignatureException(Type type, boolean dumpStackTraceOnError) { + super(dumpStackTraceOnError); this.type = type; } public Type type() { return type; } - - @Override - public Throwable fillInStackTrace() { - // This is an internal exception; the stack trace is irrelevant. - return this; - } } - private final Types types; - protected abstract void append(char ch); protected abstract void append(byte[] ba); protected abstract void append(Name name); protected void classReference(ClassSymbol c) { /* by default: no-op */ } - protected SignatureGenerator(Types types) { - this.types = types; - } - protected void reportIllegalSignature(Type t) { - throw new InvalidSignatureException(t); + throw new InvalidSignatureException(t, Types.this.dumpStacktraceOnError); } /** @@ -5257,9 +5246,9 @@ public void assembleClassSig(Type type) { if (outer.allparams().nonEmpty()) { boolean rawOuter = c.owner.kind == MTH || // either a local class - c.name == types.names.empty; // or anonymous + c.name == Types.this.names.empty; // or anonymous assembleClassSig(rawOuter - ? types.erasure(outer) + ? Types.this.erasure(outer) : outer); append(rawOuter ? '$' : '.'); Assert.check(c.flatname.startsWith(c.owner.enclClass().flatname)); @@ -5281,7 +5270,7 @@ public void assembleParamsSig(List typarams) { for (List ts = typarams; ts.nonEmpty(); ts = ts.tail) { Type.TypeVar tvar = (Type.TypeVar) ts.head; append(tvar.tsym.name); - List bounds = types.getBounds(tvar); + List bounds = Types.this.getBounds(tvar); if ((bounds.head.tsym.flags() & INTERFACE) != 0) { append(':'); } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java index 48459dd96bb..77a7e3332f0 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Annotate.java @@ -945,7 +945,13 @@ private MethodSymbol validateContainer(Type targetContainerType, boolean fatalError = false; // Validate that there is a (and only 1) value method - Scope scope = targetContainerType.tsym.members(); + Scope scope = null; + try { + scope = targetContainerType.tsym.members(); + } catch (CompletionFailure ex) { + chk.completionError(pos, ex); + return null; + } int nr_value_elems = 0; boolean error = false; for(Symbol elm : scope.getSymbolsByName(names.value)) { diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java index e3e7e1adea5..f5df6adddf3 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Infer.java @@ -47,6 +47,7 @@ import com.sun.tools.javac.comp.Infer.GraphSolver.InferenceGraph.Node; import com.sun.tools.javac.comp.Resolve.InapplicableMethodException; import com.sun.tools.javac.comp.Resolve.VerboseResolutionMode; +import com.sun.tools.javac.util.CompilerInternalException; import java.io.IOException; import java.io.Writer; @@ -68,7 +69,7 @@ import java.util.function.Predicate; import static com.sun.tools.javac.code.TypeTag.*; -import java.util.Comparator; +import static com.sun.tools.javac.main.Option.DOE; /** Helper class for type parameter inference, used by the attribution phase. * @@ -97,6 +98,8 @@ public class Infer { */ private List pendingGraphs; + private final boolean dumpStacktraceOnError; + public static Infer instance(Context context) { Infer instance = context.get(inferKey); if (instance == null) @@ -119,6 +122,7 @@ protected Infer(Context context) { pendingGraphs = List.nil(); emptyContext = new InferenceContext(this, List.nil()); + dumpStacktraceOnError = options.isSet("dev") || options.isSet(DOE); } /** A value for prototypes that admit any type, including polymorphic ones. */ @@ -133,8 +137,8 @@ public static class InferenceException extends InapplicableMethodException { transient List messages = List.nil(); - InferenceException() { - super(null); + InferenceException(boolean dumpStacktrace) { + super(null, dumpStacktrace); } @Override @@ -144,7 +148,7 @@ public JCDiagnostic getDiagnostic() { } InferenceException error(JCDiagnostic diag) { - InferenceException result = new InferenceException(); + InferenceException result = new InferenceException(dumpStacktraceOnError); if (diag != null) { result.messages = result.messages.append(diag); } @@ -1342,20 +1346,15 @@ interface GraphStrategy { * A NodeNotFoundException is thrown whenever an inference strategy fails * to pick the next node to solve in the inference graph. */ - public static class NodeNotFoundException extends RuntimeException { + class NodeNotFoundException extends CompilerInternalException { private static final long serialVersionUID = 0; transient InferenceGraph graph; - public NodeNotFoundException(InferenceGraph graph) { + public NodeNotFoundException(InferenceGraph graph, boolean dumpStacktraceOnError) { + super(dumpStacktraceOnError); this.graph = graph; } - - @Override - public Throwable fillInStackTrace() { - // This is an internal exception; the stack trace is irrelevant. - return this; - } } /** * Pick the next node (leaf) to solve in the graph @@ -1375,7 +1374,7 @@ abstract class LeafSolver implements GraphStrategy { public Node pickNode(InferenceGraph g) { if (g.nodes.isEmpty()) { //should not happen - throw new NodeNotFoundException(g); + throw new NodeNotFoundException(g, Infer.this.dumpStacktraceOnError); } return g.nodes.get(0); } @@ -1450,7 +1449,7 @@ public Node pickNode(final InferenceGraph g) { } if (bestPath == noPath) { //no path leads there - throw new NodeNotFoundException(g); + throw new NodeNotFoundException(g, Infer.this.dumpStacktraceOnError); } return bestPath.fst.head; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java index 7f1ad8211cb..2c3d79c0ab6 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java @@ -1796,7 +1796,7 @@ private class L2MSignatureGenerator extends Types.SignatureGenerator { boolean allowIllegalSignatures; L2MSignatureGenerator(boolean allowIllegalSignatures) { - super(types); + types.super(); this.allowIllegalSignatures = allowIllegalSignatures; } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java index a330f045fe5..c5fd2177d49 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java @@ -62,6 +62,7 @@ import com.sun.tools.javac.tree.JCTree.JCCase; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCExpressionStatement; + import static com.sun.tools.javac.tree.JCTree.JCOperatorExpression.OperandPos.LEFT; import com.sun.tools.javac.tree.JCTree.JCSwitchExpression; @@ -2629,7 +2630,7 @@ private class LowerSignatureGenerator extends Types.SignatureGenerator { StringBuilder sb = new StringBuilder(); LowerSignatureGenerator() { - super(types); + types.super(); } @Override diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java index 643bb22c853..c71b6d341dc 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java @@ -79,6 +79,7 @@ import static com.sun.tools.javac.code.Kinds.Kind.*; import static com.sun.tools.javac.code.TypeTag.*; import static com.sun.tools.javac.comp.Resolve.MethodResolutionPhase.*; +import static com.sun.tools.javac.main.Option.DOE; import static com.sun.tools.javac.tree.JCTree.Tag.*; import static com.sun.tools.javac.util.Iterators.createCompoundIterator; @@ -112,6 +113,7 @@ public class Resolve { private final boolean allowYieldStatement; final EnumSet verboseResolutionMode; final boolean dumpMethodReferenceSearchResults; + final boolean dumpStacktraceOnError; WriteableScope polymorphicSignatureScope; @@ -149,6 +151,7 @@ protected Resolve(Context context) { allowModules = Feature.MODULES.allowedInSource(source); allowRecords = Feature.RECORDS.allowedInSource(source); dumpMethodReferenceSearchResults = options.isSet("debug.dumpMethodReferenceSearchResults"); + dumpStacktraceOnError = options.isSet("dev") || options.isSet(DOE); } /** error symbols, which are returned when resolution fails @@ -584,7 +587,7 @@ Type rawInstantiate(Env env, ForAll pmt = (ForAll) mt; if (typeargtypes.length() != pmt.tvars.length()) // not enough args - throw new InapplicableMethodException(diags.fragment(Fragments.WrongNumberTypeArgs(Integer.toString(pmt.tvars.length())))); + throw new InapplicableMethodException(diags.fragment(Fragments.WrongNumberTypeArgs(Integer.toString(pmt.tvars.length()))), dumpStacktraceOnError); // Check type arguments are within bounds List formals = pmt.tvars; List actuals = typeargtypes; @@ -593,7 +596,7 @@ Type rawInstantiate(Env env, pmt.tvars, typeargtypes); for (; bounds.nonEmpty(); bounds = bounds.tail) { if (!types.isSubtypeUnchecked(actuals.head, bounds.head, warn)) { - throw new InapplicableMethodException(diags.fragment(Fragments.ExplicitParamDoNotConformToBounds(actuals.head, bounds))); + throw new InapplicableMethodException(diags.fragment(Fragments.ExplicitParamDoNotConformToBounds(actuals.head, bounds)), dumpStacktraceOnError); } } formals = formals.tail; @@ -830,7 +833,7 @@ protected void reportMC(DiagnosticPosition pos, MethodCheckDiag diag, InferenceC String key = inferDiag ? diag.inferKey : diag.basicKey; throw inferDiag ? infer.error(diags.create(DiagnosticType.FRAGMENT, log.currentSource(), pos, key, args)) : - methodCheckFailure.setMessage(diags.create(DiagnosticType.FRAGMENT, log.currentSource(), pos, key, args)); + getMethodCheckFailure().setMessage(diags.create(DiagnosticType.FRAGMENT, log.currentSource(), pos, key, args)); } /** @@ -842,7 +845,7 @@ class SharedInapplicableMethodException extends InapplicableMethodException { private static final long serialVersionUID = 0; SharedInapplicableMethodException() { - super(null); + super(null, Resolve.this.dumpStacktraceOnError); } SharedInapplicableMethodException setMessage(JCDiagnostic details) { @@ -851,12 +854,15 @@ SharedInapplicableMethodException setMessage(JCDiagnostic details) { } } - SharedInapplicableMethodException methodCheckFailure = new SharedInapplicableMethodException(); + private SharedInapplicableMethodException methodCheckFailure; public MethodCheck mostSpecificCheck(List actuals) { return nilMethodCheck; } + private SharedInapplicableMethodException getMethodCheckFailure() { + return methodCheckFailure == null ? methodCheckFailure = new SharedInapplicableMethodException() : methodCheckFailure; + } } /** @@ -1036,7 +1042,7 @@ public boolean compatible(Type found, Type req, Warner warn) { } public void report(DiagnosticPosition pos, JCDiagnostic details) { - throw new InapplicableMethodException(details); + throw new InapplicableMethodException(details, Resolve.this.dumpStacktraceOnError); } public Warner checkWarner(DiagnosticPosition pos, Type found, Type req) { @@ -1392,24 +1398,19 @@ public MethodCheck mostSpecificCheck(List actuals) { } } - public static class InapplicableMethodException extends RuntimeException { + public static class InapplicableMethodException extends CompilerInternalException { private static final long serialVersionUID = 0; transient JCDiagnostic diagnostic; - InapplicableMethodException(JCDiagnostic diag) { + InapplicableMethodException(JCDiagnostic diag, boolean dumpStackTraceOnError) { + super(dumpStackTraceOnError); this.diagnostic = diag; } public JCDiagnostic getDiagnostic() { return diagnostic; } - - @Override - public Throwable fillInStackTrace() { - // This is an internal exception; the stack trace is irrelevant. - return this; - } } /* *************************************************************************** diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java index 2d522d01641..f1f4e73b5a6 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java @@ -79,6 +79,7 @@ import com.sun.tools.javac.code.Type; import static com.sun.tools.javac.code.TypeTag.BOT; import static com.sun.tools.javac.code.TypeTag.VOID; + import com.sun.tools.javac.jvm.PoolConstant.LoadableConstant; import com.sun.tools.javac.jvm.Target; import com.sun.tools.javac.tree.JCTree; @@ -786,7 +787,7 @@ private class PrimitiveGenerator extends Types.SignatureGenerator { StringBuilder sb = new StringBuilder(); PrimitiveGenerator() { - super(types); + types.super(); } @Override diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/PoolWriter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/PoolWriter.java index af6f4b67fae..bfdb9775e11 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/PoolWriter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/PoolWriter.java @@ -100,7 +100,7 @@ public class PoolWriter { public PoolWriter(Types types, Names names) { this.types = types; this.names = names; - this.signatureGen = new SharedSignatureGenerator(types); + this.signatureGen = new SharedSignatureGenerator(); this.pool = new WriteablePoolHelper(); } @@ -278,8 +278,8 @@ class SharedSignatureGenerator extends Types.SignatureGenerator { */ ByteBuffer sigbuf = new ByteBuffer(); - SharedSignatureGenerator(Types types) { - super(types); + SharedSignatureGenerator() { + types.super(); } /** diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/CompilerInternalException.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/CompilerInternalException.java new file mode 100644 index 00000000000..3d3c45368c2 --- /dev/null +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/CompilerInternalException.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024, 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. + */ + +package com.sun.tools.javac.util; + +/** The super class of all compiler internal exceptions + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + */ +public class CompilerInternalException extends RuntimeException { + private static final long serialVersionUID = 0; + + @SuppressWarnings("this-escape") + public CompilerInternalException(boolean dumpStackTraceOnError) { + /* by default the stacktrace wont be filled, meaning that method CompilerInternalException::fillInStackTrace + * will always be invoked, if we do want to dump the stacktrace then we will invoke super::fillInStackTrace + * there is a bit of a dance here that could be fixed once flexible constructor bodies exits the preview + * state + */ + if (dumpStackTraceOnError) { + super.fillInStackTrace(); + } + } + + @Override + public Throwable fillInStackTrace() { + return this; + } +} diff --git a/src/jdk.internal.jvmstat/share/classes/sun/jvmstat/perfdata/resources/aliasmap b/src/jdk.internal.jvmstat/share/classes/sun/jvmstat/perfdata/resources/aliasmap index 5993acf701f..001ce5b5cac 100644 --- a/src/jdk.internal.jvmstat/share/classes/sun/jvmstat/perfdata/resources/aliasmap +++ b/src/jdk.internal.jvmstat/share/classes/sun/jvmstat/perfdata/resources/aliasmap @@ -404,8 +404,6 @@ alias sun.gc.lastCause // 1.5.0 b39 hotspot.gc.last_cause // 1.4.2_02 // sun.gc.policy -alias sun.gc.policy.avgBaseFootprint // 1.5.0 b39 - hotspot.gc.policy.avg_base_footprint // 1.5.0 b21 alias sun.gc.policy.avgMajorIntervalTime // 1.5.0 b39 hotspot.gc.policy.avg_major_interval // 1.5.0 b21 alias sun.gc.policy.avgMajorPauseTime // 1.5.0 b39 diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/CompilerToVM.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/CompilerToVM.java index 14646fd0275..59369754526 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/CompilerToVM.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/CompilerToVM.java @@ -289,6 +289,12 @@ HotSpotResolvedJavaType lookupType(ClassLoader classLoader, String name) throws native HotSpotResolvedJavaType lookupJClass(long jclass); + /** + * Gets the {@code jobject} value wrapped by {@code peerObject}. + * Must not be called if {@link Services#IS_IN_NATIVE_IMAGE} is {@code false}. + */ + native long getJObjectValue(HotSpotObjectConstantImpl peerObject); + /** * Resolves the entry at index {@code cpi} in {@code constantPool} to an interned String object. * diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java index 3dcf855ff23..3fd92f8dee5 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java @@ -925,23 +925,23 @@ JavaType lookupTypeInternal(String name, HotSpotResolvedObjectType accessingType } /** - * Gets the {@code jobject} value wrapped by {@code peerObject}. The returned "naked" value is - * only valid as long as {@code peerObject} is valid. Note that the latter may be shorter than - * the lifetime of {@code peerObject}. As such, this method should only be used to pass an - * object parameter across a JNI call from the JVMCI shared library to HotSpot. This method must - * only be called from within the JVMCI shared library. + * Gets the {@code jobject} value wrapped by {@code peerObject}. The returned value is + * a JNI local reference whose lifetime is scoped by the nearest Java caller (from + * HotSpot's perspective). You can use {@code PushLocalFrame} and {@code PopLocalFrame} to + * shorten the lifetime of the reference. The current thread's state must be + * {@code _thread_in_native}. A call from the JVMCI shared library (e.g. libgraal) is in such + * a state. * - * @param peerObject a reference to an object in the peer runtime - * @return the {@code jobject} value wrapped by {@code peerObject} + * @param peerObject a reference to an object in the HotSpot heap + * @return the {@code jobject} value unpacked from {@code peerObject} * @throws IllegalArgumentException if the current runtime is not the JVMCI shared library or - * {@code peerObject} is not a peer object reference + * {@code peerObject} is not a HotSpot heap object reference + * @throws IllegalStateException if not called from within the JVMCI shared library + * or if there is no Java caller frame on the stack + * (i.e., JavaThread::has_last_Java_frame returns false) */ public long getJObjectValue(HotSpotObjectConstant peerObject) { - if (peerObject instanceof IndirectHotSpotObjectConstantImpl) { - IndirectHotSpotObjectConstantImpl remote = (IndirectHotSpotObjectConstantImpl) peerObject; - return remote.getHandle(); - } - throw new IllegalArgumentException("Cannot get jobject value for " + peerObject + " (" + peerObject.getClass().getName() + ")"); + return compilerToVm.getJObjectValue((HotSpotObjectConstantImpl)peerObject); } @Override diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractExecutableMemberWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractExecutableMemberWriter.java index 4e929130b66..f6ad4cf05df 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractExecutableMemberWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractExecutableMemberWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, 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 @@ -31,6 +31,7 @@ import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; +import javax.lang.model.element.TypeParameterElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.ExecutableType; @@ -130,6 +131,24 @@ protected void addInheritedSummaryLink(TypeElement te, Element member, Content t target.add(writer.getDocLink(PLAIN, te, member, name(member))); } + /** + * Adds the generic type parameters. + * + * @param member the member to add the generic type parameters for + * @param target the content to which the generic type parameters will be added + */ + protected void addTypeParameters(ExecutableElement member, Content target) { + Content typeParameters = getTypeParameters(member); + target.add(typeParameters); + // Add explicit line break between method type parameters and + // return type in member summary table to avoid random wrapping. + if (typeParameters.charCount() > 10) { + target.add(new HtmlTree(TagName.BR)); + } else { + target.add(Entity.NO_BREAK_SPACE); + } + } + /** * Add the parameter for the executable member. * diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractMemberWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractMemberWriter.java index caeb6c35e6d..af1f7ef1fe6 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractMemberWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractMemberWriter.java @@ -466,16 +466,8 @@ protected void addModifiersAndType(Element member, TypeMirror type, ? ((ExecutableElement)member).getTypeParameters() : null; if (list != null && !list.isEmpty()) { - Content typeParameters = ((AbstractExecutableMemberWriter) this) - .getTypeParameters((ExecutableElement)member); - code.add(typeParameters); - // Add explicit line break between method type parameters and - // return type in member summary table to avoid random wrapping. - if (typeParameters.charCount() > 10) { - code.add(new HtmlTree(TagName.BR)); - } else { - code.add(Entity.NO_BREAK_SPACE); - } + ((AbstractExecutableMemberWriter) this) + .addTypeParameters((ExecutableElement)member, code); } code.add( writer.getLink(new HtmlLinkInfo(configuration, diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassUseWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassUseWriter.java index e949b4ddc68..94e66f1eed6 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassUseWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassUseWriter.java @@ -142,7 +142,7 @@ public ClassUseWriter(HtmlConfiguration configuration, methodSubWriter = new MethodWriter(this); constrSubWriter = new ConstructorWriter(this); - constrSubWriter.setFoundNonPubConstructor(true); + constrSubWriter.setShowConstructorModifiers(true); fieldSubWriter = new FieldWriter(this); classSubWriter = new NestedClassWriter(this); } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ConstructorWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ConstructorWriter.java index 1c4af2faf97..c5fb332a008 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ConstructorWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ConstructorWriter.java @@ -31,6 +31,7 @@ import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; +import javax.lang.model.element.TypeParameterElement; import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder; import jdk.javadoc.internal.doclets.formats.html.markup.Entity; @@ -53,7 +54,17 @@ public class ConstructorWriter extends AbstractExecutableMemberWriter { */ private ExecutableElement currentConstructor; - private boolean foundNonPubConstructor = false; + /** + * If any constructors are non-public, then we want the modifiers shown in the summary. + * This implies we need a three-column summary. + */ + private boolean showConstructorModifiers = false; + + /** + * Whether any constructors have type parameters. + * This implies we need a three column summary. + */ + private boolean hasTypeParamsConstructor = false; /** * Construct a new member writer for constructors. @@ -65,11 +76,7 @@ public ConstructorWriter(ClassWriter writer) { // the following must be done before the summary table is generated var constructors = getVisibleMembers(VisibleMemberTable.Kind.CONSTRUCTORS); - for (Element constructor : constructors) { - if (utils.isProtected(constructor) || utils.isPrivate(constructor)) { - setFoundNonPubConstructor(true); - } - } + analyzeConstructors(constructors); } /** @@ -94,11 +101,7 @@ public void buildDetails(Content target) { protected void buildConstructorDoc(Content target) { var constructors = getVisibleMembers(VisibleMemberTable.Kind.CONSTRUCTORS); if (!constructors.isEmpty()) { - for (Element constructor : constructors) { - if (utils.isProtected(constructor) || utils.isPrivate(constructor)) { - setFoundNonPubConstructor(true); - } - } + analyzeConstructors(constructors); Content constructorDetailsHeader = getConstructorDetailsHeader(target); Content memberList = getMemberList(); @@ -126,6 +129,24 @@ protected void buildConstructorDoc(Content target) { } } + // Calculate "showConstructorModifiers" and "hasTypeParamsConstructor" + private void analyzeConstructors(List constructors) { + for (Element constructor : constructors) { + if (utils.isProtected(constructor) || utils.isPrivate(constructor)) { + setShowConstructorModifiers(true); + } + List list = ((ExecutableElement)constructor).getTypeParameters(); + if (list != null && !list.isEmpty()) { + hasTypeParamsConstructor = true; + } + } + } + + // Does the constructor summary need three columnns or just two? + protected boolean threeColumnSummary() { + return showConstructorModifiers || hasTypeParamsConstructor; + } + @Override protected void buildSignature(Content target) { target.add(getSignature(currentConstructor)); @@ -201,6 +222,7 @@ protected Content getConstructorHeaderContent(ExecutableElement constructor) { protected Content getSignature(ExecutableElement constructor) { return new Signatures.MemberSignature(constructor, this) + .setTypeParameters(getTypeParameters(constructor)) .setParameters(getParameters(constructor, true)) .setExceptions(getExceptions(constructor)) .setAnnotations(writer.getAnnotationInfo(constructor, true)) @@ -231,8 +253,8 @@ protected Content getConstructorDetails(Content memberDetailsHeader, Content mem .add(memberDetails)); } - protected void setFoundNonPubConstructor(boolean foundNonPubConstructor) { - this.foundNonPubConstructor = foundNonPubConstructor; + protected void setShowConstructorModifiers(boolean showConstructorModifiers) { + this.showConstructorModifiers = showConstructorModifiers; } @Override @@ -244,7 +266,7 @@ public void addSummaryLabel(Content content) { @Override public TableHeader getSummaryTableHeader(Element member) { - if (foundNonPubConstructor) { + if (threeColumnSummary()) { return new TableHeader(contents.modifierLabel, contents.constructorLabel, contents.descriptionLabel); } else { @@ -256,7 +278,7 @@ public TableHeader getSummaryTableHeader(Element member) { protected Table createSummaryTable() { List bodyRowStyles; - if (foundNonPubConstructor) { + if (threeColumnSummary()) { bodyRowStyles = Arrays.asList(HtmlStyle.colFirst, HtmlStyle.colConstructorName, HtmlStyle.colLast); } else { @@ -276,7 +298,7 @@ public void addInheritedSummaryLabel(TypeElement typeElement, Content content) { @Override protected void addSummaryType(Element member, Content content) { - if (foundNonPubConstructor) { + if (threeColumnSummary()) { var code = new HtmlTree(TagName.CODE); if (utils.isProtected(member)) { code.add("protected "); @@ -288,6 +310,11 @@ protected void addSummaryType(Element member, Content content) { code.add( resources.getText("doclet.Package_private")); } + ExecutableElement constructor = (ExecutableElement)member; + List list = constructor.getTypeParameters(); + if (list != null && !list.isEmpty()) { + addTypeParameters(constructor, code); + } content.add(code); } } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Signatures.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Signatures.java index f4f472de9f9..afc0648fa79 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Signatures.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/Signatures.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, 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 @@ -529,6 +529,7 @@ private void appendModifiers(Content target) { private int appendTypeParameters(Content target, int lastLineSeparator) { // Apply different wrapping strategies for type parameters // depending on the combined length of type parameters and return type. + // Note return type will be null if this is a constructor. int typeParamLength = typeParameters.charCount(); if (typeParamLength >= TYPE_PARAMS_MAX_INLINE_LENGTH) { @@ -539,9 +540,10 @@ private int appendTypeParameters(Content target, int lastLineSeparator) { int lineLength = target.charCount() - lastLineSeparator; int newLastLineSeparator = lastLineSeparator; + int returnTypeLength = returnType != null ? returnType.charCount() : 0; // sum below includes length of modifiers plus type params added above - if (lineLength + returnType.charCount() > RETURN_TYPE_MAX_LINE_LENGTH) { + if (lineLength + returnTypeLength > RETURN_TYPE_MAX_LINE_LENGTH) { target.add(Text.NL); newLastLineSeparator = target.charCount(); } else { diff --git a/src/jdk.jdeps/share/classes/com/sun/tools/javap/ClassWriter.java b/src/jdk.jdeps/share/classes/com/sun/tools/javap/ClassWriter.java index 2483e99e49a..764f93bd54a 100644 --- a/src/jdk.jdeps/share/classes/com/sun/tools/javap/ClassWriter.java +++ b/src/jdk.jdeps/share/classes/com/sun/tools/javap/ClassWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2024, 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 @@ -25,10 +25,12 @@ package com.sun.tools.javap; +import java.lang.reflect.AccessFlag; import java.net.URI; import java.text.DateFormat; import java.util.Collection; import java.util.Date; +import java.util.EnumSet; import java.util.List; import java.util.Set; @@ -149,7 +151,7 @@ public boolean write(ClassModel cm) { indent(-1); } - writeModifiers(getClassModifiers(cm.flags().flagsMask())); + writeModifiers(getClassModifiers(cm.flags())); if ((classModel.flags().flagsMask() & ACC_MODULE) != 0) { var attr = classModel.findAttribute(Attributes.module()); @@ -210,7 +212,7 @@ public boolean write(ClassModel cm) { println("minor version: " + classModel.minorVersion()); println("major version: " + classModel.majorVersion()); writeList(String.format("flags: (0x%04x) ", cm.flags().flagsMask()), - getClassFlags(cm.flags().flagsMask()), "\n"); + getClassFlags(cm.flags()), "\n"); print("this_class: #");print(() -> classModel.thisClass().index()); tab(); print(() -> "// " + classModel.thisClass().asInternalName()); @@ -416,7 +418,7 @@ protected void writeField(FieldModel f) { if (!options.checkAccess(f.flags().flagsMask())) return; - var flags = AccessFlags.ofField(f.flags().flagsMask()); + var flags = f.flags(); writeModifiers(flagsReportUnknown(flags).stream().filter(fl -> fl.sourceModifier()) .map(fl -> Modifier.toString(fl.mask())).toList()); print(() -> sigPrinter.print( @@ -794,9 +796,16 @@ else switch (c) { } } - private Set getClassModifiers(int mask) { - return getModifiers(flagsReportUnknown(AccessFlags.ofClass((mask & ACC_INTERFACE) != 0 - ? mask & ~ACC_ABSTRACT : mask))); + private Set getClassModifiers(AccessFlags flags) { + var flagSet = flagsReportUnknown(flags); + Set set; + if (flagSet.contains(AccessFlag.INTERFACE)) { + set = EnumSet.copyOf(flagSet); + set.remove(AccessFlag.ABSTRACT); + } else { + set = flagSet; + } + return getModifiers(set); } private static Set getModifiers(Set flags) { @@ -806,16 +815,8 @@ private static Set getModifiers(Set flags) return s; } - private Set getClassFlags(int mask) { - return getFlags(mask, flagsReportUnknown(AccessFlags.ofClass(mask))); - } - - private Set getMethodFlags(int mask) { - return getFlags(mask, flagsReportUnknown(AccessFlags.ofMethod(mask))); - } - - private Set getFieldFlags(int mask) { - return getFlags(mask, flagsReportUnknown(AccessFlags.ofField(mask))); + private Set getClassFlags(AccessFlags flags) { + return getFlags(flags.flagsMask(), flagsReportUnknown(flags)); } private static Set getFlags(int mask, Set flags) { diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.c b/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.c index f91f16104ca..cf2de806119 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024, 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 @@ -93,8 +93,8 @@ static jrawMonitorID callbackBlock; * not blocking might mean that a return would continue execution of * some java thread in the middle of VM_DEATH, this seems troubled. * - * WARNING: No not 'return' or 'goto' out of the BEGIN_CALLBACK/END_CALLBACK - * block, this will mess up the count. + * WARNING: Do not 'return' or 'goto' out of the BEGIN_CALLBACK/END_CALLBACK + * block. This will mess up the active_callbacks count. */ #define BEGIN_CALLBACK() \ @@ -1709,6 +1709,18 @@ eventHandler_unlock(void) debugMonitorExit(handlerLock); } +void +callback_lock(void) +{ + debugMonitorEnter(callbackLock); +} + +void +callback_unlock(void) +{ + debugMonitorExit(callbackLock); +} + /***** handler creation *****/ HandlerNode * diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.h b/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.h index c4a7aa0c441..096b1ed795a 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.h +++ b/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024, 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 @@ -78,6 +78,9 @@ void eventHandler_waitForActiveCallbacks(); void eventHandler_lock(void); void eventHandler_unlock(void); +void callback_lock(void); +void callback_unlock(void); + jboolean eventHandler_synthesizeUnloadEvent(char *signature, JNIEnv *env); jclass getMethodClass(jvmtiEnv *jvmti_env, jmethodID method); diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/invoker.c b/src/jdk.jdwp.agent/share/native/libjdwp/invoker.c index e1b4d2f3ab6..4b32430ad7b 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/invoker.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/invoker.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024, 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 @@ -719,7 +719,9 @@ invoker_completeInvokeRequest(jthread thread) exc = NULL; id = 0; - eventHandler_lock(); /* for proper lock order */ + callback_lock(); /* for proper lock order in threadControl getLocks() */ + eventHandler_lock(); /* for proper lock order in threadControl getLocks() */ + stepControl_lock(); /* for proper lock order in threadControl getLocks() */ debugMonitorEnter(invokerLock); request = threadControl_getInvokeRequest(thread); @@ -772,7 +774,7 @@ invoker_completeInvokeRequest(jthread thread) * We cannot delete saved exception or return value references * since otherwise a deleted handle would escape when writing * the response to the stream. Instead, we clean those refs up - * after writing the respone. + * after writing the response. */ deleteGlobalArgumentRefs(env, request); @@ -790,7 +792,9 @@ invoker_completeInvokeRequest(jthread thread) * Give up the lock before I/O operation */ debugMonitorExit(invokerLock); + stepControl_unlock(); eventHandler_unlock(); + callback_unlock(); if (!detached) { outStream_initReply(&out, id); diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/stepControl.c b/src/jdk.jdwp.agent/share/native/libjdwp/stepControl.c index 0611e8fc213..aeec8175b17 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/stepControl.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/stepControl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024, 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 @@ -805,7 +805,8 @@ stepControl_beginStep(JNIEnv *env, jthread thread, jint size, jint depth, LOG_STEP(("stepControl_beginStep: thread=%p,size=%d,depth=%d", thread, size, depth)); - eventHandler_lock(); /* for proper lock order */ + callback_lock(); /* for proper lock order in threadControl getLocks() */ + eventHandler_lock(); /* for proper lock order in threadControl getLocks() */ stepControl_lock(); step = threadControl_getStepRequest(thread); @@ -852,6 +853,7 @@ stepControl_beginStep(JNIEnv *env, jthread thread, jint size, jint depth, stepControl_unlock(); eventHandler_unlock(); + callback_unlock(); return error; } diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/threadControl.c b/src/jdk.jdwp.agent/share/native/libjdwp/threadControl.c index 00c34844c98..220e0040979 100644 --- a/src/jdk.jdwp.agent/share/native/libjdwp/threadControl.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/threadControl.c @@ -642,26 +642,31 @@ getLocks(void) * thread) needs to be grabbed here. This allows thread control * code to safely suspend and resume the application threads * while ensuring they don't hold a critical lock. + * + * stepControl_beginStep() grabs the eventHandler lock and stepControl lock + * before eventually ending up here, so we need to maintain that order here. + * Similarly, invoker_completeInvokeRequest() grabs the eventHandler lock + * and invoker lock. */ - + callback_lock(); eventHandler_lock(); + stepControl_lock(); invoker_lock(); eventHelper_lock(); - stepControl_lock(); - commonRef_lock(); debugMonitorEnter(threadLock); - + commonRef_lock(); } static void releaseLocks(void) { - debugMonitorExit(threadLock); commonRef_unlock(); - stepControl_unlock(); + debugMonitorExit(threadLock); eventHelper_unlock(); invoker_unlock(); + stepControl_unlock(); eventHandler_unlock(); + callback_unlock(); } void diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java index 71569c30273..f086bbf8847 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/EventControl.java @@ -299,7 +299,7 @@ private static Control defineEnabled(PlatformEventType type) { } private static Control defineThreshold(PlatformEventType type) { - String def = type.getAnnotationValue(Threshold.class, "0 ns"); + String def = type.getAnnotationValue(Threshold.class, ThresholdSetting.DEFAULT_VALUE); type.add(PrivateAccess.getInstance().newSettingDescriptor(TYPE_THRESHOLD, Threshold.NAME, def, Collections.emptyList())); return new Control(new ThresholdSetting(type), def); } @@ -311,13 +311,13 @@ private static Control defineStackTrace(PlatformEventType type) { } private static Control defineCutoff(PlatformEventType type) { - String def = type.getAnnotationValue(Cutoff.class, Cutoff.INFINITY); + String def = type.getAnnotationValue(Cutoff.class, CutoffSetting.DEFAULT_VALUE); type.add(PrivateAccess.getInstance().newSettingDescriptor(TYPE_CUTOFF, Cutoff.NAME, def, Collections.emptyList())); return new Control(new CutoffSetting(type), def); } private static Control defineThrottle(PlatformEventType type) { - String def = type.getAnnotationValue(Throttle.class, Throttle.DEFAULT); + String def = type.getAnnotationValue(Throttle.class, ThrottleSetting.DEFAULT_VALUE); type.add(PrivateAccess.getInstance().newSettingDescriptor(TYPE_THROTTLE, Throttle.NAME, def, Collections.emptyList())); return new Control(new ThrottleSetting(type), def); } @@ -330,7 +330,7 @@ private static Control defineLevel(PlatformEventType type) { } private static Control definePeriod(PlatformEventType type) { - String def = type.getAnnotationValue(Period.class, "everyChunk"); + String def = type.getAnnotationValue(Period.class, PeriodSetting.DEFAULT_VALUE); type.add(PrivateAccess.getInstance().newSettingDescriptor(TYPE_PERIOD, PeriodSetting.NAME, def, Collections.emptyList())); return new Control(new PeriodSetting(type), def); } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/BooleanSetting.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/BooleanSetting.java new file mode 100644 index 00000000000..72ffcac72c9 --- /dev/null +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/BooleanSetting.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2024, 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. + */ + +package jdk.jfr.internal.settings; + +import java.util.Objects; +import java.util.Set; + +import jdk.jfr.internal.PlatformEventType; + +abstract class BooleanSetting extends JDKSettingControl { + private final PlatformEventType eventType; + private final String defaultValue; + private String value; + + public BooleanSetting(PlatformEventType eventType, String defaultValue) { + this.eventType = Objects.requireNonNull(eventType); + this.defaultValue = defaultValue; + this.value = defaultValue; + if (parse(defaultValue) == null) { + throw new InternalError("Only 'true' or 'false' is allowed with class BooleanSetting"); + } + } + + protected abstract void apply(PlatformEventType eventType, boolean value); + + @Override + public String combine(Set values) { + if (values.contains("true")) { + return "true"; + } + if (values.contains("false")) { + return "false"; + } + return defaultValue; + } + + @Override + public void setValue(String value) { + Boolean b = parse(value); + if (b != null) { + apply(eventType, b.booleanValue()); + this.value = value; + } + } + + @Override + public String getValue() { + return value; + } + + private static Boolean parse(String value) { + if ("true".equals(value)) { + return Boolean.TRUE; + } + if ("false".equals(value)) { + return Boolean.FALSE; + } + return null; + } +} diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/CutoffSetting.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/CutoffSetting.java index 553336b59e2..68950d71b63 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/CutoffSetting.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/CutoffSetting.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, 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 @@ -25,6 +25,8 @@ package jdk.jfr.internal.settings; +import static jdk.jfr.internal.util.ValueParser.MISSING; + import java.util.Objects; import java.util.Set; @@ -43,8 +45,8 @@ @Name(Type.SETTINGS_PREFIX + "Cutoff") @Timespan public final class CutoffSetting extends JDKSettingControl { - - private String value = "0 ns"; + public static final String DEFAULT_VALUE = ValueParser.INFINITY; + private String value = DEFAULT_VALUE; private final PlatformEventType eventType; public CutoffSetting(PlatformEventType eventType) { @@ -54,22 +56,24 @@ public CutoffSetting(PlatformEventType eventType) { @Override public String combine(Set values) { long max = 0; - String text = "0 ns"; + String text = null; for (String value : values) { - long l = ValueParser.parseTimespanWithInfinity(value); - if (l > max) { + long nanos = ValueParser.parseTimespanWithInfinity(value, MISSING); + if (nanos != MISSING && nanos > max) { text = value; - max = l; + max = nanos; } } - return text; + return Objects.requireNonNullElse(text, DEFAULT_VALUE); } @Override public void setValue(String value) { - long l = ValueParser.parseTimespanWithInfinity(value); - this.value = value; - eventType.setCutoff(l); + long nanos = ValueParser.parseTimespanWithInfinity(value, MISSING); + if (nanos != MISSING) { + eventType.setCutoff(nanos); + this.value = value; + } } @Override @@ -81,10 +85,6 @@ public static long parseValueSafe(String value) { if (value == null) { return 0L; } - try { - return ValueParser.parseTimespanWithInfinity(value); - } catch (NumberFormatException nfe) { - return 0L; - } + return ValueParser.parseTimespanWithInfinity(value, 0L); } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/EnabledSetting.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/EnabledSetting.java index 77a57a9b75b..e81bce484f3 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/EnabledSetting.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/EnabledSetting.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024, 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 @@ -25,9 +25,6 @@ package jdk.jfr.internal.settings; -import java.util.Objects; -import java.util.Set; - import jdk.jfr.Description; import jdk.jfr.BooleanFlag; import jdk.jfr.Label; @@ -41,33 +38,18 @@ @Description("Record event") @Name(Type.SETTINGS_PREFIX + "Enabled") @BooleanFlag -public final class EnabledSetting extends JDKSettingControl { - private final BooleanValue booleanValue; - private final PlatformEventType eventType; - +public final class EnabledSetting extends BooleanSetting { public EnabledSetting(PlatformEventType eventType, String defaultValue) { - this.booleanValue = BooleanValue.valueOf(defaultValue); - this.eventType = Objects.requireNonNull(eventType); - } - - @Override - public String combine(Set values) { - return booleanValue.union(values); + super(eventType, defaultValue); } @Override - public void setValue(String value) { - booleanValue.setValue(value); - eventType.setEnabled(booleanValue.getBoolean()); + protected void apply(PlatformEventType eventType, boolean value) { + eventType.setEnabled(value); if (eventType.isEnabled() && !eventType.isJVM()) { if (!eventType.isInstrumented()) { eventType.markForInstrumentation(true); } } } - - @Override - public String getValue() { - return booleanValue.getValue(); - } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/LevelSetting.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/LevelSetting.java index b6aa7500d4b..9531f75c925 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/LevelSetting.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/LevelSetting.java @@ -47,30 +47,29 @@ public final class LevelSetting extends JDKSettingControl { public LevelSetting(PlatformEventType eventType, String[] levels) { this.eventType = Objects.requireNonNull(eventType); this.levels = Arrays.asList(Objects.requireNonNull(levels)); + this.value = levels[0]; } @Override public String combine(Set values) { - int maxIndex = 0; + int maxIndex = 0; // index 0 contains the default value for (String value : values) { - maxIndex = Math.max(maxIndex, indexOf(value)); + maxIndex = Math.max(maxIndex, levels.indexOf(value)); } return levels.get(maxIndex); } @Override public void setValue(String value) { - this.value = value; - this.eventType.setLevel(indexOf(value)); + int index = levels.indexOf(value); + if (index != -1) { + this.eventType.setLevel(index); + this.value = value; + } } @Override public String getValue() { return value; } - - private int indexOf(String value) { - int index = levels.indexOf(value); - return index < 0 ? 0 : index; - } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/PeriodSetting.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/PeriodSetting.java index 05b5a08a3bb..22fef2691aa 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/PeriodSetting.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/PeriodSetting.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024, 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 @@ -35,6 +35,7 @@ import jdk.jfr.internal.PlatformEventType; import jdk.jfr.internal.Type; import jdk.jfr.internal.util.ValueParser; +import static jdk.jfr.internal.util.ValueParser.MISSING; @MetadataDefinition @Label("Period") @@ -46,6 +47,7 @@ public final class PeriodSetting extends JDKSettingControl { public static final String EVERY_CHUNK = "everyChunk"; public static final String BEGIN_CHUNK = "beginChunk"; public static final String END_CHUNK = "endChunk"; + public static final String DEFAULT_VALUE = EVERY_CHUNK; public static final String NAME = "period"; private final PlatformEventType eventType; private String value = EVERY_CHUNK; @@ -56,7 +58,6 @@ public PeriodSetting(PlatformEventType eventType) { @Override public String combine(Set values) { - boolean beginChunk = false; boolean endChunk = false; Long min = null; @@ -74,15 +75,11 @@ public String combine(Set values) { endChunk = true; break; default: - long l = ValueParser.parseTimespanWithInfinity(value); - // Always accept first specified value - if (min == null) { - text = value; - min = l; - } else { - if (l < min) { + long nanos = ValueParser.parseTimespanWithInfinity(value, MISSING); + if (nanos != MISSING) { + if (min == null || nanos < min) { text = value; - min = l; + min = nanos; } } } @@ -97,7 +94,7 @@ public String combine(Set values) { if (!beginChunk && endChunk) { return END_CHUNK; } - return EVERY_CHUNK; // also default + return DEFAULT_VALUE; // "everyChunk" is default } @Override @@ -113,7 +110,10 @@ public void setValue(String value) { eventType.setPeriod(0, false, true); break; default: - long nanos = ValueParser.parseTimespanWithInfinity(value); + long nanos = ValueParser.parseTimespanWithInfinity(value, MISSING); + if (nanos == MISSING) { + return; + } if (nanos == 0 || nanos == Long.MAX_VALUE) { eventType.setPeriod(nanos, false, false); } else { diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/StackTraceSetting.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/StackTraceSetting.java index cfd85a0f240..45489ebc691 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/StackTraceSetting.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/StackTraceSetting.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024, 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 @@ -25,11 +25,8 @@ package jdk.jfr.internal.settings; -import java.util.Objects; -import java.util.Set; - -import jdk.jfr.Description; import jdk.jfr.BooleanFlag; +import jdk.jfr.Description; import jdk.jfr.Label; import jdk.jfr.MetadataDefinition; import jdk.jfr.Name; @@ -41,30 +38,16 @@ @Name(Type.SETTINGS_PREFIX + "StackTrace") @Description("Record stack traces") @BooleanFlag -public final class StackTraceSetting extends JDKSettingControl { - private static final long typeId = Type.getTypeId(StackTraceSetting.class); - private final BooleanValue booleanValue; - private final PlatformEventType eventType; +public final class StackTraceSetting extends BooleanSetting { + private static final long typeId = Type.getTypeId(StackTraceSetting.class); public StackTraceSetting(PlatformEventType eventType, String defaultValue) { - this.booleanValue = BooleanValue.valueOf(defaultValue); - this.eventType = Objects.requireNonNull(eventType); - } - - @Override - public String combine(Set values) { - return booleanValue.union(values); - } - - @Override - public void setValue(String value) { - booleanValue.setValue(value); - eventType.setStackTraceEnabled(booleanValue.getBoolean()); + super(eventType, defaultValue); } @Override - public String getValue() { - return booleanValue.getValue(); + protected void apply(PlatformEventType eventType, boolean value) { + eventType.setStackTraceEnabled(value); } public static boolean isType(long typeId) { diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/ThresholdSetting.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/ThresholdSetting.java index 1a7138b11cd..8870417f9dd 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/ThresholdSetting.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/ThresholdSetting.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024, 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 @@ -25,6 +25,8 @@ package jdk.jfr.internal.settings; +import static jdk.jfr.internal.util.ValueParser.MISSING; + import java.util.Objects; import java.util.Set; @@ -36,14 +38,16 @@ import jdk.jfr.internal.PlatformEventType; import jdk.jfr.internal.Type; import jdk.jfr.internal.util.ValueParser; + @MetadataDefinition @Label("Threshold") @Name(Type.SETTINGS_PREFIX + "Threshold") @Description("Record event with duration above or equal to threshold") @Timespan public final class ThresholdSetting extends JDKSettingControl { + public static final String DEFAULT_VALUE = "0 ns"; private static final long typeId = Type.getTypeId(ThresholdSetting.class); - private String value = "0 ns"; + private String value = DEFAULT_VALUE; private final PlatformEventType eventType; public ThresholdSetting(PlatformEventType eventType) { @@ -55,26 +59,24 @@ public String combine(Set values) { Long min = null; String text = null; for (String value : values) { - long l = ValueParser.parseTimespanWithInfinity(value); - // always accept first value - if (min == null) { - min = l; - text = value; - } else { - if (l < min) { + long nanos = ValueParser.parseTimespanWithInfinity(value, MISSING); + if (nanos != MISSING) { + if (min == null || nanos < min) { text = value; - min = l; + min = nanos; } } } - return text == null ? "0 ns" : text; + return Objects.requireNonNullElse(text, DEFAULT_VALUE); } @Override public void setValue(String value) { - long l = ValueParser.parseTimespanWithInfinity(value); - this.value = value; - eventType.setThreshold(l); + long nanos = ValueParser.parseTimespanWithInfinity(value, MISSING); + if (nanos != MISSING) { + eventType.setThreshold(nanos); + this.value = value; + } } @Override diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/ThrottleSetting.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/ThrottleSetting.java index 45750296389..55757050b80 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/ThrottleSetting.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/ThrottleSetting.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, Datadog, Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -26,6 +26,9 @@ package jdk.jfr.internal.settings; +import static jdk.jfr.internal.util.TimespanUnit.SECONDS; +import static jdk.jfr.internal.util.TimespanUnit.MILLISECONDS; + import java.util.Objects; import java.util.Set; @@ -34,17 +37,20 @@ import jdk.jfr.MetadataDefinition; import jdk.jfr.Name; import jdk.jfr.internal.PlatformEventType; +import jdk.jfr.internal.Throttle; import jdk.jfr.internal.Type; +import jdk.jfr.internal.util.Rate; +import jdk.jfr.internal.util.TimespanUnit; +import jdk.jfr.internal.util.Utils; @MetadataDefinition @Label("Event Emission Throttle") @Description("Throttles the emission rate for an event") @Name(Type.SETTINGS_PREFIX + "Throttle") public final class ThrottleSetting extends JDKSettingControl { - static final String OFF_TEXT = "off"; - private static final long OFF = -2; - private String value = "0/s"; + public static final String DEFAULT_VALUE = Throttle.DEFAULT; private final PlatformEventType eventType; + private String value = DEFAULT_VALUE; public ThrottleSetting(PlatformEventType eventType) { this.eventType = Objects.requireNonNull(eventType); @@ -52,75 +58,51 @@ public ThrottleSetting(PlatformEventType eventType) { @Override public String combine(Set values) { - long max = OFF; - String text = "off"; + Rate max = null; + String text = null; for (String value : values) { - long l = parseValueSafe(value); - if (l > max) { - text = value; - max = l; + Rate rate = Rate.of(value); + if (rate != null) { + if (max == null || rate.isHigher(max)) { + text = value; + max = rate; + } } } - return text; + // "off" is default + return Objects.requireNonNullElse(text, DEFAULT_VALUE); } - private static long parseValueSafe(String s) { - long value = 0L; - try { - value = parseThrottleValue(s); - } catch (NumberFormatException nfe) { + @Override + public void setValue(String value) { + if ("off".equals(value)) { + eventType.setThrottle(-2, 1000); + this.value = value; + return; } - return value; - } - @Override - public void setValue(String s) { - long size = 0; - long millis = 1000; - try { - size = parseThrottleValue(s); - millis = parseThrottleTimeUnit(s); - this.value = s; - } catch (NumberFormatException nfe) { + Rate rate = Rate.of(value); + if (rate != null) { + long millis = 1000; + long samples = rate.amount(); + TimespanUnit unit = rate.unit(); + // if unit is more than 1 s, set millis + if (unit.nanos > SECONDS.nanos) { + millis = unit.nanos / MILLISECONDS.nanos; + } + // if unit is less than 1 s, scale samples + if (unit.nanos < SECONDS.nanos) { + long perSecond = SECONDS.nanos / unit.nanos; + samples *= Utils.multiplyOverflow(samples, perSecond, Long.MAX_VALUE); + } + eventType.setThrottle(samples, millis); + this.value = value; } - eventType.setThrottle(size, millis); } @Override public String getValue() { return value; } - - private static long parseThrottleValue(String s) { - if (s.equals(OFF_TEXT)) { - return OFF; - } - String parsedValue = parseThrottleString(s, true); - long normalizedValue = 0; - try { - normalizedValue = ThrottleUnit.normalizeValueAsMillis(Long.parseLong(parsedValue), s); - } catch (NumberFormatException nfe) { - throwThrottleNumberFormatException(s); - } - return normalizedValue; - } - - private static long parseThrottleTimeUnit(String s) { - return ThrottleUnit.asMillis(s); - } - - // Expected input format is "x/y" where x is a non-negative long - // and y is a time unit. Split the string at the delimiter. - static String parseThrottleString(String s, boolean value) { - String[] split = s.split("/"); - if (split.length != 2) { - throwThrottleNumberFormatException(s); - } - return value ? split[0].trim() : split[1].trim(); - } - - private static void throwThrottleNumberFormatException(String s) { - throw new NumberFormatException("'" + s + "' is not valid. Should be a non-negative numeric value followed by a delimiter. i.e. '/', and then followed by a unit e.g. 100/s."); - } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/ThrottleUnit.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/ThrottleUnit.java deleted file mode 100644 index ec2fafc7656..00000000000 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/ThrottleUnit.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2020, Datadog, Inc. 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. - */ -package jdk.jfr.internal.settings; - -import java.util.concurrent.TimeUnit; - -enum ThrottleUnit { - NANOSECONDS("ns", TimeUnit.SECONDS.toNanos(1), TimeUnit.SECONDS.toMillis(1)), - MICROSECONDS("us", TimeUnit.SECONDS.toNanos(1) / 1000, TimeUnit.SECONDS.toMillis(1)), - MILLISECONDS("ms", TimeUnit.SECONDS.toMillis(1), TimeUnit.SECONDS.toMillis(1)), - SECONDS("s", 1, TimeUnit.SECONDS.toMillis(1)), - MINUTES("m", 1, TimeUnit.MINUTES.toMillis(1)), - HOUR("h", 1, TimeUnit.HOURS.toMillis(1)), - DAY("d", 1, TimeUnit.DAYS.toMillis(1)); - - private final String text; - private final long factor; - private final long millis; - - ThrottleUnit(String t, long factor, long millis) { - this.text = t; - this.factor = factor; - this.millis = millis; - } - - private static ThrottleUnit parse(String s) { - if (s.equals(ThrottleSetting.OFF_TEXT)) { - return MILLISECONDS; - } - return unit(ThrottleSetting.parseThrottleString(s, false)); - } - - private static ThrottleUnit unit(String s) { - if (s.endsWith("ns") || s.endsWith("us") || s.endsWith("ms")) { - return value(s.substring(s.length() - 2)); - } - if (s.endsWith("s") || s.endsWith("m") || s.endsWith("h") || s.endsWith("d")) { - return value(s.substring(s.length() - 1)); - } - throw new NumberFormatException("'" + s + "' is not a valid time unit."); - } - - private static ThrottleUnit value(String s) { - for (ThrottleUnit t : values()) { - if (t.text.equals(s)) { - return t; - } - } - throw new NumberFormatException("'" + s + "' is not a valid time unit."); - } - - static long asMillis(String s) { - return parse(s).millis; - } - - static long normalizeValueAsMillis(long value, String s) { - return value * parse(s).factor; - } - } \ No newline at end of file diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/BooleanValue.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Rate.java similarity index 50% rename from src/jdk.jfr/share/classes/jdk/jfr/internal/settings/BooleanValue.java rename to src/jdk.jfr/share/classes/jdk/jfr/internal/util/Rate.java index d8f46d76787..f32436a5e0f 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/settings/BooleanValue.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Rate.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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 @@ -22,53 +22,37 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ +package jdk.jfr.internal.util; -package jdk.jfr.internal.settings; +public record Rate(long amount, TimespanUnit unit) { -import java.util.Set; - -/** - * Helper class for settings that use boolean numbers - * - */ -final class BooleanValue { - private String value = "false"; - private boolean booleanValue; - - private BooleanValue(boolean b) { - booleanValue = b; - value = b ? "true" : "false"; - } - - public String union(Set values) { - for (String v : values) { - if ("true".equals(v)) { - return "true"; + public static Rate of(String text) { + String[] splitted = text.split("/"); + if (splitted.length != 2) { + return null; + } + String value = splitted[0].strip(); + String unit = splitted[1].strip(); + TimespanUnit tu = TimespanUnit.fromText(unit); + if (unit == null) { + return null; + } + try { + long v = Long.parseLong(value); + if (v >= 0) { + return new Rate(v, tu); } + } catch (NumberFormatException nfe) { + // Ignore } - return "false"; - } - - public void setValue(String value) { - this.value = value; - this.booleanValue = Boolean.valueOf(value); + return null; } - public final String getValue() { - return this.value; + public boolean isHigher(Rate that) { + return this.inNanos() > that.inNanos(); } - public boolean getBoolean() { - return booleanValue; - } - - public static BooleanValue valueOf(String defaultValue) { - if ("true".equals(defaultValue)) { - return new BooleanValue(true); - } - if ("false".equals(defaultValue)) { - return new BooleanValue(false); - } - throw new InternalError("Unknown default value for settings '" + defaultValue + "'"); + private double inNanos() { + return (double) amount / unit.nanos; } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/TimespanUnit.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/TimespanUnit.java new file mode 100644 index 00000000000..9026f59b68b --- /dev/null +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/TimespanUnit.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024, 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. + */ +package jdk.jfr.internal.util; + +public enum TimespanUnit { + NANOSECONDS ("ns", 1L, 1000), + MICROSECONDS("us", 1000L, 1000), + MILLISECONDS("ms", 1_000_000L, 1000), + SECONDS ("s", 1_000_000_000L, 60), + MINUTES ("m", 60 * 1_000_000_000L, 60), + HOURS ("h", 60 * 60 * 1_000_000_000L, 24), + DAYS ("d", 24 * 60 * 60 * 1_000_000_000L, 7); + public final String text; + public final long nanos; + public final int size; + TimespanUnit(String text, long nanos, int size) { + this.text = text; + this.nanos = nanos; + this.size = size; + } + + public static TimespanUnit fromText(String text) { + for (TimespanUnit tu : values()) { + // Case-sensitive by design + if (tu.text.equals(text)) { + return tu; + } + } + return null; + } +} diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Utils.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Utils.java index ae83727096a..b5ae906c71c 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Utils.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/Utils.java @@ -430,4 +430,12 @@ public static boolean isJDKClass(Class type) { // but only if it is safe and there is a mechanism to register event // classes in other modules besides jdk.jfr and java.base. } + + public static long multiplyOverflow(long a, long b, long defaultValue) { + try { + return Math.multiplyExact(a, b); + } catch (ArithmeticException ae) { + return defaultValue; + } + } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/ValueFormatter.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/ValueFormatter.java index 8fd47dbf5b3..53a11101944 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/ValueFormatter.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/ValueFormatter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024, 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 @@ -40,23 +40,6 @@ import jdk.jfr.consumer.RecordedMethod; public final class ValueFormatter { - private static enum TimespanUnit { - NANOSECONDS("ns", 1000), - MICROSECONDS("us", 1000), - MILLISECONDS("ms", 1000), - SECONDS("s", 60), MINUTES("m", 60), - HOURS("h", 24), - DAYS("d", 7); - - private final String text; - private final long amount; - - TimespanUnit(String unit, long amount) { - this.text = unit; - this.amount = amount; - } - } - private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("HH:mm:ss"); private static final Duration MICRO_SECOND = Duration.ofNanos(1_000); private static final Duration SECOND = Duration.ofSeconds(1); @@ -82,7 +65,7 @@ public static String formatTimespan(Duration dValue, String separation) { TimespanUnit result = TimespanUnit.NANOSECONDS; for (TimespanUnit unit : TimespanUnit.values()) { result = unit; - long amount = unit.amount; + long amount = unit.size; if (result == TimespanUnit.DAYS || value < amount || value % amount != 0) { break; } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/ValueParser.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/ValueParser.java index 730c83daaae..e4482c92368 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/util/ValueParser.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/util/ValueParser.java @@ -32,7 +32,16 @@ import static java.util.concurrent.TimeUnit.SECONDS; public final class ValueParser { - private static final String INFINITY = "infinity"; + public static final String INFINITY = "infinity"; + public static final long MISSING = Long.MIN_VALUE; + + public static long parseTimespanWithInfinity(String s, long defaultValue) { + try { + return parseTimespanWithInfinity(s); + } catch (NumberFormatException nfe) { + return defaultValue; + } + } public static long parseTimespanWithInfinity(String s) { if (INFINITY.equals(s)) { diff --git a/src/jdk.jshell/share/classes/jdk/jshell/TreeDissector.java b/src/jdk.jshell/share/classes/jdk/jshell/TreeDissector.java index 518a61d6969..fc259b8bc7c 100644 --- a/src/jdk.jshell/share/classes/jdk/jshell/TreeDissector.java +++ b/src/jdk.jshell/share/classes/jdk/jshell/TreeDissector.java @@ -246,7 +246,7 @@ private static class TDSignatureGenerator extends Types.SignatureGenerator { StringBuilder sb = new StringBuilder(); TDSignatureGenerator(Types types) { - super(types); + types.super(); } @Override diff --git a/test/hotspot/gtest/utilities/test_utf8.cpp b/test/hotspot/gtest/utilities/test_utf8.cpp index ffd8121075b..80f6671207b 100644 --- a/test/hotspot/gtest/utilities/test_utf8.cpp +++ b/test/hotspot/gtest/utilities/test_utf8.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024, 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 @@ -22,6 +22,8 @@ */ #include "precompiled.hpp" +#include "nmt/memflags.hpp" +#include "runtime/os.hpp" #include "utilities/utf8.hpp" #include "unittest.hpp" @@ -101,5 +103,105 @@ TEST_VM(utf8, jbyte_length) { UNICODE::as_utf8(str, 19, res, i); EXPECT_TRUE(test_stamp(res + i, sizeof(res) - i)); } +} + +TEST_VM(utf8, truncation) { + + // Test that truncation removes partial encodings as expected. + + const char orig_bytes[] = { 'A', 'B', 'C', 'D', 'E', '\0' }; + const int orig_length = sizeof(orig_bytes)/sizeof(char); + ASSERT_TRUE(UTF8::is_legal_utf8((const unsigned char*)orig_bytes, orig_length - 1, false)); + const char* orig_str = &orig_bytes[0]; + ASSERT_EQ((int)strlen(orig_str), orig_length - 1); + + unsigned char* temp_bytes; + const char* temp_str; + char* utf8; + int n_utf8; // Number of bytes in the encoding + + // Test 1: a valid UTF8 "ascii" ending string should be returned as-is + + temp_bytes = (unsigned char*) os::malloc(sizeof(unsigned char) * orig_length, mtTest); + strcpy((char*)temp_bytes, orig_str); + temp_str = (const char*) temp_bytes; + UTF8::truncate_to_legal_utf8(temp_bytes, orig_length); + ASSERT_EQ((int)strlen(temp_str), orig_length - 1) << "bytes should be unchanged"; + ASSERT_EQ(strcmp(orig_str, temp_str), 0) << "bytes should be unchanged"; + os::free(temp_bytes); + + // Test 2: a UTF8 sequence that "ends" with a 2-byte encoding + // drops the 2-byte encoding + + jchar two_byte_char[] = { 0x00D1 }; // N with tilde + n_utf8 = 2; + utf8 = (char*) os::malloc(sizeof(char) * (n_utf8 + 1), mtTest); // plus NUL + UNICODE::convert_to_utf8(two_byte_char, 1, utf8); + int utf8_len = (int)strlen(utf8); + ASSERT_EQ(utf8_len, n_utf8) << "setup error"; + + // Now drop zero or one byte from the end and check it truncates as expected + for (int drop = 0; drop < n_utf8; drop++) { + int temp_len = orig_length + utf8_len - drop; + temp_bytes = (unsigned char*) os::malloc(sizeof(unsigned char) * temp_len, mtTest); + temp_str = (const char*) temp_bytes; + strcpy((char*)temp_bytes, orig_str); + strncat((char*)temp_bytes, utf8, utf8_len - drop); + ASSERT_EQ((int)strlen(temp_str), temp_len - 1) << "setup error"; + UTF8::truncate_to_legal_utf8(temp_bytes, temp_len); + ASSERT_EQ((int)strlen(temp_str), orig_length - 1) << "bytes should be truncated to original length"; + ASSERT_EQ(strcmp(orig_str, temp_str), 0) << "bytes should be truncated to original"; + os::free(temp_bytes); + } + os::free(utf8); + + // Test 3: a UTF8 sequence that "ends" with a 3-byte encoding + // drops the 3-byte encoding + n_utf8 = 3; + jchar three_byte_char[] = { 0x0800 }; + utf8 = (char*) os::malloc(sizeof(char) * (n_utf8 + 1), mtTest); // plus NUL + UNICODE::convert_to_utf8(three_byte_char, 1, utf8); + utf8_len = (int)strlen(utf8); + ASSERT_EQ(utf8_len, n_utf8) << "setup error"; + + // Now drop zero, to two bytes from the end and check it truncates as expected + for (int drop = 0; drop < n_utf8; drop++) { + int temp_len = orig_length + utf8_len - drop; + temp_bytes = (unsigned char*) os::malloc(sizeof(unsigned char) * temp_len, mtTest); + temp_str = (const char*) temp_bytes; + strcpy((char*)temp_bytes, orig_str); + strncat((char*)temp_bytes, utf8, utf8_len - drop); + ASSERT_EQ((int)strlen(temp_str), temp_len - 1) << "setup error"; + UTF8::truncate_to_legal_utf8(temp_bytes, temp_len); + ASSERT_EQ((int)strlen(temp_str), orig_length - 1) << "bytes should be truncated to original length"; + ASSERT_EQ(strcmp(orig_str, temp_str), 0) << "bytes should be truncated to original"; + os::free(temp_bytes); + } + os::free(utf8); + + // Test 4: a UTF8 sequence that "ends" with a 6-byte encoding + // drops the 6-byte encoding + n_utf8 = 6; + jchar six_byte_char[] = { 0xD801, 0xDC37 }; // U+10437 as its UTF-16 surrogate pairs + utf8 = (char*) os::malloc(sizeof(char) * (n_utf8 + 1), mtTest); // plus NUL + UNICODE::convert_to_utf8(six_byte_char, 2, utf8); + utf8_len = (int)strlen(utf8); + ASSERT_EQ(utf8_len, n_utf8) << "setup error"; + + // Now drop zero to five bytes from the end and check it truncates as expected + for (int drop = 0; drop < n_utf8; drop++) { + int temp_len = orig_length + utf8_len - drop; + temp_bytes = (unsigned char*) os::malloc(sizeof(unsigned char) * temp_len, mtTest); + temp_str = (const char*) temp_bytes; + strcpy((char*)temp_bytes, orig_str); + strncat((char*)temp_bytes, utf8, utf8_len - drop); + ASSERT_EQ((int)strlen(temp_str), temp_len - 1) << "setup error"; + UTF8::truncate_to_legal_utf8(temp_bytes, temp_len); + ASSERT_EQ((int)strlen(temp_str), orig_length - 1) << "bytes should be truncated to original length"; + ASSERT_EQ(strcmp(orig_str, temp_str), 0) << "bytes should be truncated to original"; + os::free(temp_bytes); + } + os::free(utf8); + } diff --git a/test/hotspot/jtreg/compiler/c2/TestCastX2P.java b/test/hotspot/jtreg/compiler/c2/TestCastX2P.java new file mode 100644 index 00000000000..6591692f0cc --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/TestCastX2P.java @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2024, Arm Limited. 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. + */ + +package compiler.c2; + +import jdk.internal.misc.Unsafe; +import jdk.test.lib.Asserts; + +/** + * @test TestCastX2P + * @summary AArch64: remove extra register copy when converting from long to pointer. + * @bug 8336245 + * @library /test/lib + * @modules java.base/jdk.internal.misc + * @run main/othervm -XX:-TieredCompilation compiler.c2.TestCastX2P + */ + +public class TestCastX2P { + + public static final int LEN = 2040; + + static final Unsafe UNSAFE = Unsafe.getUnsafe(); + + public static long lseed = 0xbeef; + public static int iseed = 0xbeef; + public static short sseed = (short) (0xef); + public static byte bseed = (byte) (0xe); + + public static long off1 = 16; + public static long off2 = 32; + public static long off3 = 64; + + public static class TestLong { + + private static long address = UNSAFE.allocateMemory(LEN); + + static { + for (int k = 0; k < 10_000; k++) { + for (int i = 0; i < LEN/2; i++) { + UNSAFE.putLong(address+i, lseed); + } + } + + UNSAFE.putLong(address + off1 + 1030, lseed); + UNSAFE.putLong(address + 1023, lseed); + UNSAFE.putLong(address + off2 + 1001, lseed); + } + } + + public static class TestLongIndirect { + + private static long address = UNSAFE.allocateMemory(LEN); + + static { + for (int k = 0; k < 1000; k++) { + for (int i = 0; i < LEN/2; i++) { + UNSAFE.putLong(address+i, lseed); + } + } + + UNSAFE.putLong(address + off1, lseed); + UNSAFE.putLong(address + off1 + off2, lseed); + UNSAFE.putLong(address + off3, lseed); + } + } + + public static class TestInt { + + private static long address = UNSAFE.allocateMemory(LEN); + + static { + for (int k = 0; k < 10_000; k++) { + for (int i = 0; i < LEN/2; i++) { + UNSAFE.putInt(address+i, iseed); + } + } + + UNSAFE.putInt(address + off1 + 274, iseed); + UNSAFE.putInt(address + 278, iseed); + UNSAFE.putInt(address + off2 + 282, iseed); + } + } + + public static class TestIntIndirect { + + private static long address = UNSAFE.allocateMemory(LEN); + + static { + for (int k = 0; k < 1000; k++) { + for (int i = 0; i < LEN/2; i++) { + UNSAFE.putInt(address+i, iseed); + } + } + + UNSAFE.putInt(address + off1, iseed); + UNSAFE.putInt(address + off1 + off2, iseed); + UNSAFE.putInt(address + off3, iseed); + } + } + + public static class TestShort { + + private static long address = UNSAFE.allocateMemory(LEN); + + static { + for (int k = 0; k < 10_000; k++) { + for (int i = 0; i < LEN/2; i++) { + UNSAFE.putShort(address+i, sseed); + } + } + + UNSAFE.putShort(address + off1 + 257, sseed); + UNSAFE.putShort(address + 277, sseed); + UNSAFE.putShort(address + off2 + 283, sseed); + } + } + + public static class TestShortIndirect { + + private static long address = UNSAFE.allocateMemory(LEN); + + static { + for (int k = 0; k < 1000; k++) { + for (int i = 0; i < LEN/2; i++) { + UNSAFE.putShort(address+i, sseed); + } + } + + UNSAFE.putShort(address + off1, sseed); + UNSAFE.putShort(address + off1 + off2, sseed); + UNSAFE.putShort(address + off3, sseed); + } + } + + public static class TestByte { + + private static long address = UNSAFE.allocateMemory(LEN); + + static { + for (int k = 0; k < 10_000; k++) { + for (int i = 0; i < LEN/2; i++) { + UNSAFE.putByte(address+i, bseed); + } + } + + UNSAFE.putByte(address + off1 + 257, bseed); + UNSAFE.putByte(address + 277, bseed); + UNSAFE.putByte(address + off2 + 283, bseed); + } + } + + public static class TestByteIndirect { + + private static long address = UNSAFE.allocateMemory(LEN); + + static { + for (int k = 0; k < 1000; k++) { + for (int i = 0; i < LEN/2; i++) { + UNSAFE.putByte(address+i, bseed); + } + } + + UNSAFE.putByte(address + off1, bseed); + UNSAFE.putByte(address + off1 + off2, bseed); + UNSAFE.putByte(address + off3, bseed); + } + } + + static void test() { + TestLong t1 = new TestLong(); + Asserts.assertEquals(UNSAFE.getLong(t1.address + off1 + 1030), lseed, "put long failed!"); + Asserts.assertEquals(UNSAFE.getLong(t1.address + 1023), lseed, "put long failed!"); + Asserts.assertEquals(UNSAFE.getLong(t1.address + off2 + 1001), lseed, "put long failed!"); + + TestLongIndirect t2 = new TestLongIndirect(); + Asserts.assertEquals(UNSAFE.getLong(t2.address + off1), lseed, "put long failed!"); + Asserts.assertEquals(UNSAFE.getLong(t2.address + off1 + off2), lseed, "put long failed!"); + Asserts.assertEquals(UNSAFE.getLong(t2.address + off3), lseed, "put long failed!"); + + TestInt t3 = new TestInt(); + Asserts.assertEquals(UNSAFE.getInt(t3.address + off1 + 274), iseed, "put int failed!"); + Asserts.assertEquals(UNSAFE.getInt(t3.address + 278), iseed, "put int failed!"); + Asserts.assertEquals(UNSAFE.getInt(t3.address + off2 + 282), iseed, "put int failed!"); + + TestIntIndirect t4 = new TestIntIndirect(); + Asserts.assertEquals(UNSAFE.getInt(t4.address + off1), iseed, "put int failed!"); + Asserts.assertEquals(UNSAFE.getInt(t4.address + off1 + off2), iseed, "put int failed!"); + Asserts.assertEquals(UNSAFE.getInt(t4.address + off3), iseed, "put int failed!"); + + TestShort t5 = new TestShort(); + Asserts.assertEquals(UNSAFE.getShort(t5.address + off1 + 257), sseed, "put short failed!"); + Asserts.assertEquals(UNSAFE.getShort(t5.address + 277), sseed, "put short failed!"); + Asserts.assertEquals(UNSAFE.getShort(t5.address + off2 + 283), sseed, "put short failed!"); + + TestShortIndirect t6 = new TestShortIndirect(); + Asserts.assertEquals(UNSAFE.getShort(t6.address + off1), sseed, "put short failed!"); + Asserts.assertEquals(UNSAFE.getShort(t6.address + off1 + off2), sseed, "put short failed!"); + Asserts.assertEquals(UNSAFE.getShort(t6.address + off3), sseed, "put short failed!"); + + TestByte t7 = new TestByte(); + Asserts.assertEquals(UNSAFE.getByte(t7.address + off1 + 257), bseed, "put byte failed!"); + Asserts.assertEquals(UNSAFE.getByte(t7.address + 277), bseed, "put byte failed!"); + Asserts.assertEquals(UNSAFE.getByte(t7.address + off2 + 283), bseed, "put byte failed!"); + + TestByteIndirect t8 = new TestByteIndirect(); + Asserts.assertEquals(UNSAFE.getByte(t8.address + off1), bseed, "put byte failed!"); + Asserts.assertEquals(UNSAFE.getByte(t8.address + off1 + off2), bseed, "put byte failed!"); + Asserts.assertEquals(UNSAFE.getByte(t8.address + off3), bseed, "put byte failed!"); + } + + public static void main(String[] strArr) { + test(); + } +} diff --git a/test/hotspot/jtreg/compiler/c2/aarch64/TestVolatiles.java b/test/hotspot/jtreg/compiler/c2/aarch64/TestVolatiles.java index 10bc237f2be..23b9321fc35 100644 --- a/test/hotspot/jtreg/compiler/c2/aarch64/TestVolatiles.java +++ b/test/hotspot/jtreg/compiler/c2/aarch64/TestVolatiles.java @@ -39,8 +39,7 @@ * and in {G1, * Serial, * Parallel, - * Shenandoah, - * ShenandoahIU} + * Shenandoah} */ @@ -95,13 +94,6 @@ public void runtest(String classname, String testType) throws Throwable { procArgs[argcount - 3] = "-XX:+UnlockExperimentalVMOptions"; procArgs[argcount - 2] = "-XX:+UseShenandoahGC"; break; - case "ShenandoahIU": - argcount = 11; - procArgs = new String[argcount]; - procArgs[argcount - 4] = "-XX:+UnlockExperimentalVMOptions"; - procArgs[argcount - 3] = "-XX:+UseShenandoahGC"; - procArgs[argcount - 2] = "-XX:ShenandoahGCMode=iu"; - break; default: throw new RuntimeException("unexpected test type " + testType); } @@ -286,7 +278,6 @@ private void checkstore(OutputAnalyzer output, String testType, boolean useCompr }; break; case "Shenandoah": - case "ShenandoahIU": // Shenandoah generates normal object graphs for // volatile stores matches = new String[] { @@ -358,7 +349,6 @@ private void checkcas(OutputAnalyzer output, String testType, boolean useCompres }; break; case "Shenandoah": - case "ShenandoahIU": // For volatile CAS, Shenanodoah generates normal // graphs with a shenandoah-specific cmpxchg matches = new String[] { @@ -445,7 +435,6 @@ private void checkcae(OutputAnalyzer output, String testType, boolean useCompres }; break; case "Shenandoah": - case "ShenandoahIU": // For volatile CAS, Shenanodoah generates normal // graphs with a shenandoah-specific cmpxchg matches = new String[] { @@ -512,7 +501,6 @@ private void checkgas(OutputAnalyzer output, String testType, boolean useCompres }; break; case "Shenandoah": - case "ShenandoahIU": matches = new String[] { "membar_release \\(elided\\)", useCompressedOops ? "atomic_xchgw?_acq" : "atomic_xchg_acq", diff --git a/test/hotspot/jtreg/compiler/c2/aarch64/TestVolatilesShenandoah.java b/test/hotspot/jtreg/compiler/c2/aarch64/TestVolatilesShenandoah.java index 9678c37a384..60c9cc3713e 100644 --- a/test/hotspot/jtreg/compiler/c2/aarch64/TestVolatilesShenandoah.java +++ b/test/hotspot/jtreg/compiler/c2/aarch64/TestVolatilesShenandoah.java @@ -71,33 +71,6 @@ * @run driver compiler.c2.aarch64.TestVolatilesShenandoah * TestUnsafeVolatileGAA Shenandoah * - * @run driver compiler.c2.aarch64.TestVolatilesShenandoah - * TestVolatileLoad ShenandoahIU - * - * @run driver compiler.c2.aarch64.TestVolatilesShenandoah - * TestVolatileStore ShenandoahIU - * - * @run driver compiler.c2.aarch64.TestVolatilesShenandoah - * TestUnsafeVolatileLoad ShenandoahIU - * - * @run driver compiler.c2.aarch64.TestVolatilesShenandoah - * TestUnsafeVolatileStore ShenandoahIU - * - * @run driver compiler.c2.aarch64.TestVolatilesShenandoah - * TestUnsafeVolatileCAS ShenandoahIU - * - * @run driver compiler.c2.aarch64.TestVolatilesShenandoah - * TestUnsafeVolatileWeakCAS ShenandoahIU - * - * @run driver compiler.c2.aarch64.TestVolatilesShenandoah - * TestUnsafeVolatileCAE ShenandoahIU - * - * @run driver compiler.c2.aarch64.TestVolatilesShenandoah - * TestUnsafeVolatileGAS ShenandoahIU - * - * @run driver compiler.c2.aarch64.TestVolatilesShenandoah - * TestUnsafeVolatileGAA ShenandoahIU - * */ package compiler.c2.aarch64; diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestIfMinMax.java b/test/hotspot/jtreg/compiler/c2/irTests/TestIfMinMax.java index 41402036aea..e232895257a 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/TestIfMinMax.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/TestIfMinMax.java @@ -30,7 +30,7 @@ /* * @test - * @bug 8324655 8329797 + * @bug 8324655 8329797 8331090 * @key randomness * @summary Test that if expressions are properly folded into min/max nodes * @requires os.arch != "riscv64" @@ -505,7 +505,27 @@ public void checkTestMinLongVector(Object[] vals) { } } - @Run(test = { "testMinI1", "testMinI2", "testMaxI1", "testMaxI2", "testMinI1E", "testMinI2E", "testMaxI1E", "testMaxI2E" }) + @Test + @IR(failOn = { IRNode.IF }, counts = { IRNode.MIN_I, "1" }) + public int testMinIConst(int a) { + if (a > 65535) { + a = 65535; + } + + return a; + } + + @Test + @IR(phase = { CompilePhase.BEFORE_MACRO_EXPANSION }, failOn = { IRNode.IF }, counts = { IRNode.MIN_L, "1" }) + public long testMinLConst(long a) { + if (a > 65535) { + a = 65535; + } + + return a; + } + + @Run(test = { "testMinI1", "testMinI2", "testMaxI1", "testMaxI2", "testMinI1E", "testMinI2E", "testMaxI1E", "testMaxI2E", "testMinIConst" }) public void runTestIntegers() { testIntegers(10, 20); testIntegers(20, 10); @@ -526,9 +546,12 @@ public void testIntegers(int a, int b) { Asserts.assertEQ(a >= b ? b : a, testMinI2E(a, b)); Asserts.assertEQ(a >= b ? a : b, testMaxI1E(a, b)); Asserts.assertEQ(a <= b ? b : a, testMaxI2E(a, b)); + + Asserts.assertEQ(a > 65535 ? 65535 : a, testMinIConst(a)); + Asserts.assertEQ(b > 65535 ? 65535 : b, testMinIConst(b)); } - @Run(test = { "testMinL1", "testMinL2", "testMaxL1", "testMaxL2", "testMinL1E", "testMinL2E", "testMaxL1E", "testMaxL2E" }) + @Run(test = { "testMinL1", "testMinL2", "testMaxL1", "testMaxL2", "testMinL1E", "testMinL2E", "testMaxL1E", "testMaxL2E", "testMinLConst" }) public void runTestLongs() { testLongs(10, 20); testLongs(20, 10); @@ -551,5 +574,8 @@ public void testLongs(long a, long b) { Asserts.assertEQ(a >= b ? b : a, testMinL2E(a, b)); Asserts.assertEQ(a >= b ? a : b, testMaxL1E(a, b)); Asserts.assertEQ(a <= b ? b : a, testMaxL2E(a, b)); + + Asserts.assertEQ(a > 65535L ? 65535L : a, testMinLConst(a)); + Asserts.assertEQ(b > 65535L ? 65535L : b, testMinLConst(b)); } } diff --git a/test/hotspot/jtreg/compiler/c2/irTests/gc/ReferenceRefersToTests.java b/test/hotspot/jtreg/compiler/c2/irTests/gc/ReferenceRefersToTests.java new file mode 100644 index 00000000000..7cf7c72c8fd --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/irTests/gc/ReferenceRefersToTests.java @@ -0,0 +1,156 @@ +/* + * Copyright Amazon.com Inc. 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. + */ +package compiler.c2.irTests.gc; + +import jdk.test.lib.Asserts; +import compiler.lib.ir_framework.*; +import jdk.test.whitebox.gc.GC; + +import java.lang.ref.*; +import java.util.*; + +/* + * @test + * @bug 8256999 + * @summary Test that Reference.refersTo intrinsics are properly handled + * @library /test/lib / + * @build jdk.test.whitebox.WhiteBox + * @requires vm.compiler2.enabled + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI compiler.c2.irTests.gc.ReferenceRefersToTests + */ +public class ReferenceRefersToTests { + + private static String[] args(String... add) { + List args = new ArrayList<>(); + + // Use PerMethodTrapLimit=0 to compile all branches in the intrinsics. + args.add("-XX:PerMethodTrapLimit=0"); + + // Forcefully inline all methods to reach the intrinsic code. + args.add("-XX:CompileCommand=inline,compiler.c2.irTests.gc.ReferenceRefersToTests::*"); + args.add("-XX:CompileCommand=inline,java.lang.ref.Reference::*"); + args.add("-XX:CompileCommand=inline,java.lang.ref.PhantomReference::*"); + + // Mix in test config code. + args.addAll(Arrays.asList(add)); + + return args.toArray(new String[0]); + } + + public static void main(String[] args) { + TestFramework framework = new TestFramework(); + + int idx = 0; + if (GC.isSelectedErgonomically() && GC.Serial.isSupported()) { + // Serial does not have any barriers in refersTo. + framework.addScenarios(new Scenario(idx++, args( + "-XX:+UseSerialGC" + ))); + } + if (GC.isSelectedErgonomically() && GC.Parallel.isSupported()) { + // Parallel does not have any barriers in refersTo. + framework.addScenarios(new Scenario(idx++, args( + "-XX:+UseParallelGC" + ))); + } + if (GC.isSelectedErgonomically() && GC.G1.isSupported()) { + // G1 nominally needs keep-alive barriers for Reference loads, + // but should not have them for refersTo. + framework.addScenarios(new Scenario(idx++, args( + "-XX:+UseG1GC" + ))); + } + if (GC.isSelectedErgonomically() && GC.Shenandoah.isSupported()) { + // Shenandoah nominally needs keep-alive barriers for Reference loads, + // but should not have them for refersTo. We only care to check that + // SATB barrier is not emitted. Shenandoah would also emit LRB barrier, + // which would false-negative the test. + framework.addScenarios(new Scenario(idx++, args( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:ShenandoahGCMode=passive", + "-XX:+ShenandoahSATBBarrier", + "-XX:+UseShenandoahGC" + ))); + } + if (GC.isSelectedErgonomically() && GC.Z.isSupported()) { + // ZGC does not emit barriers in IR. + framework.addScenarios(new Scenario(idx++, args( + "-XX:+UseZGC" + ))); + } + framework.start(); + } + + static final Object REF = new Object(); + + static final SoftReference SR = new SoftReference<>(REF); + static final WeakReference WR = new WeakReference<>(REF); + static final PhantomReference PR = new PhantomReference<>(REF, null); + + // Verify that we are left with a single load of Reference.referent and no stores. + // This serves as a signal that no GC barriers are emitted in IR. + + @Test + @IR(counts = { IRNode.LOAD, "1" }) + @IR(failOn = { IRNode.STORE }) + public boolean soft_null() { + return SR.refersTo(null); + } + + @Test + @IR(counts = { IRNode.LOAD, "1" }) + @IR(failOn = { IRNode.STORE }) + public boolean soft_ref() { + return SR.refersTo(REF); + } + + @Test + @IR(counts = { IRNode.LOAD, "1" }) + @IR(failOn = { IRNode.STORE }) + public boolean weak_null() { + return WR.refersTo(null); + } + + @Test + @IR(counts = { IRNode.LOAD, "1" }) + @IR(failOn = { IRNode.STORE }) + public boolean weak_ref() { + return WR.refersTo(REF); + } + + @Test + @IR(counts = { IRNode.LOAD, "1" }) + @IR(failOn = { IRNode.STORE }) + public boolean phantom_null() { + return PR.refersTo(null); + } + + @Test + @IR(counts = { IRNode.LOAD, "1" }) + @IR(failOn = { IRNode.STORE }) + public boolean phantom_ref() { + return PR.refersTo(REF); + } + +} diff --git a/test/hotspot/jtreg/compiler/escapeAnalysis/TestFindInstMemRecursion.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestFindInstMemRecursion.java new file mode 100644 index 00000000000..90a5ff92cd3 --- /dev/null +++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestFindInstMemRecursion.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024, 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 8324345 + * @summary Ensure that ConnectionGraph::find_inst_mem does not cause a stack + * overflow. + * + * @run main/othervm -Xcomp -XX:CompileThreshold=10 -XX:-TieredCompilation + * -XX:CompileCommand=CompileOnly,javax.swing.plaf.basic.BasicLookAndFeel::initComponentDefaults + * -XX:CompileCommand=MemLimit,*.*,0 + * compiler.escapeAnalysis.TestFindInstMemRecursion + * + */ + +package compiler.escapeAnalysis; + +import javax.swing.*; +import javax.swing.plaf.metal.*; + +public class TestFindInstMemRecursion { + public static void main(String[] args) throws Exception { + LookAndFeel lookAndFeel = new MetalLookAndFeel(); + for (int i = 0; i < 20; ++i) { + UIManager.setLookAndFeel(lookAndFeel); + } + } +} diff --git a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/SimpleDebugInfoTest.java b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/SimpleDebugInfoTest.java index c7d8d2cf830..e77ac8dc4f9 100644 --- a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/SimpleDebugInfoTest.java +++ b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.code.test/src/jdk/vm/ci/code/test/SimpleDebugInfoTest.java @@ -200,84 +200,4 @@ public void testStackLong() { testLongOnStack(compiler); testLongInLocal(compiler); } - - public static Class objectOnStack() { - return SimpleDebugInfoTest.class; - } - - private void testObjectOnStack(DebugInfoCompiler compiler) { - test(compiler, getMethod("objectOnStack"), 2, JavaKind.Object); - } - - public static Class objectInLocal() { - Class local = SimpleDebugInfoTest.class; - return local; - } - - private void testObjectInLocal(DebugInfoCompiler compiler) { - test(compiler, getMethod("objectInLocal"), 3, JavaKind.Object); - } - - @Test - public void testConstObject() { - ResolvedJavaType type = metaAccess.lookupJavaType(objectOnStack()); - DebugInfoCompiler compiler = (asm, values) -> { - values[0] = constantReflection.asJavaClass(type); - return null; - }; - testObjectOnStack(compiler); - testObjectInLocal(compiler); - } - - @Test - public void testRegObject() { - ResolvedJavaType type = metaAccess.lookupJavaType(objectOnStack()); - DebugInfoCompiler compiler = (asm, values) -> { - Register reg = asm.emitLoadPointer((HotSpotConstant) constantReflection.asJavaClass(type)); - values[0] = reg.asValue(asm.getValueKind(JavaKind.Object)); - return null; - }; - testObjectOnStack(compiler); - testObjectInLocal(compiler); - } - - @Test - public void testStackObject() { - ResolvedJavaType type = metaAccess.lookupJavaType(objectOnStack()); - DebugInfoCompiler compiler = (asm, values) -> { - Register reg = asm.emitLoadPointer((HotSpotConstant) constantReflection.asJavaClass(type)); - values[0] = asm.emitPointerToStack(reg); - return null; - }; - testObjectOnStack(compiler); - testObjectInLocal(compiler); - } - - @Test - public void testRegNarrowObject() { - Assume.assumeTrue(config.useCompressedOops); - ResolvedJavaType type = metaAccess.lookupJavaType(objectOnStack()); - DebugInfoCompiler compiler = (asm, values) -> { - HotSpotConstant wide = (HotSpotConstant) constantReflection.asJavaClass(type); - Register reg = asm.emitLoadPointer((HotSpotConstant) wide.compress()); - values[0] = reg.asValue(asm.narrowOopKind); - return null; - }; - testObjectOnStack(compiler); - testObjectInLocal(compiler); - } - - @Test - public void testStackNarrowObject() { - Assume.assumeTrue(config.useCompressedOops); - ResolvedJavaType type = metaAccess.lookupJavaType(objectOnStack()); - DebugInfoCompiler compiler = (asm, values) -> { - HotSpotConstant wide = (HotSpotConstant) constantReflection.asJavaClass(type); - Register reg = asm.emitLoadPointer((HotSpotConstant) wide.compress()); - values[0] = asm.emitNarrowPointerToStack(reg); - return null; - }; - testObjectOnStack(compiler); - testObjectInLocal(compiler); - } } diff --git a/test/hotspot/jtreg/compiler/print/CompileCommandPrintMemStat.java b/test/hotspot/jtreg/compiler/print/CompileCommandPrintMemStat.java index c2689d88372..2dbf46fd040 100644 --- a/test/hotspot/jtreg/compiler/print/CompileCommandPrintMemStat.java +++ b/test/hotspot/jtreg/compiler/print/CompileCommandPrintMemStat.java @@ -77,16 +77,17 @@ private static void test(String include, String exclude) throws Exception { // Should see final report // Looks like this: - // total NA RA result #nodes limit time type #rc thread method - // 2149912 0 1986272 ok - - 0.101 c1 1 0x000000015180a600 jdk/internal/org/objectweb/asm/Frame::execute((IILjdk/internal/org/objectweb/asm/Symbol;Ljdk/internal/org/objectweb/asm/SymbolTable;)V) oa.shouldMatch("total.*method"); + // total Others RA HA NA result #nodes limit time type #rc thread method + // 523648 32728 490920 0 0 ok - - 0.250 c1 1 0x00007f4ec00d4ac0 java/lang/Class::descriptorString(()Ljava/lang/String;) // or - // 537784 98184 208536 ok 267 - 0.096 c2 1 0x0000000153019c00 jdk/internal/classfile/impl/BufWriterImpl::writeU1((I)V) 4521912 0 1986272 ok - - 0.101 c1 1 0x000000015180a600 jdk/internal/org/objectweb/asm/Frame::execute((IILjdk/internal/org/objectweb/asm/Symbol;Ljdk/internal/org/objectweb/asm/SymbolTable;)V) oa.shouldMatch("total.*method"); - oa.shouldMatch("\\d+ +\\d+ +\\d+ +ok +(\\d+|-) +.*" + expectedNameIncl + ".*"); + // 1898600 853176 750872 0 294552 ok 934 - 1.501 c2 1 0x00007f4ec00d3330 java/lang/String::replace((CC)Ljava/lang/String;) + oa.shouldMatch("total.*method"); + oa.shouldMatch("\\d+ +(\\d+ +){4}ok +(\\d+|-) +.*" + expectedNameIncl + ".*"); // In debug builds, we have a default memory limit enabled. That implies MemStat. Therefore we // expect to see all methods, not just the one we specified on the command line. if (Platform.isDebugBuild()) { - oa.shouldMatch("\\d+ +\\d+ +\\d+ +ok +(\\d+|-) +.*" + expectedNameExcl + ".*"); + oa.shouldMatch("\\d+ +(\\d+ +){4}ok +(\\d+|-) +.*" + expectedNameExcl + ".*"); } else { oa.shouldNotMatch(".*" + expectedNameExcl + ".*"); } diff --git a/test/hotspot/jtreg/compiler/rangechecks/TestEmptyLoopDeadCast.java b/test/hotspot/jtreg/compiler/rangechecks/TestEmptyLoopDeadCast.java new file mode 100644 index 00000000000..105f297c8cb --- /dev/null +++ b/test/hotspot/jtreg/compiler/rangechecks/TestEmptyLoopDeadCast.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2024, Red Hat 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 8335393 + * @summary C2: assert(!had_error) failed: bad dominance + * @requires vm.compiler2.enabled + * @run main/othervm -XX:-TieredCompilation -XX:-UseOnStackReplacement -XX:-BackgroundCompilation -XX:-UseLoopPredicate + * -XX:LoopMaxUnroll=0 TestEmptyLoopDeadCast + */ + +public class TestEmptyLoopDeadCast { + public static void main(String[] args) { + int[] array = new int[100]; + for (int i = 0; i < 20_000; i++) { + test1Helper(1, 101, array); + test1(0, array); + test2Helper(0, -101, array); + test2(0, array); + } + } + + private static int test1(int start, int[] array) { + return test1Helper(start, 0, array); + } + + private static int test1Helper(int start, int stop, int[] array) { + if (array == null) { + } + int v = 0; + for (int i = start; i < stop; i++) { + v = array[i - 1]; + } + return v; + } + + private static int test2(int start, int[] array) { + return test2Helper(start, -1, array); + } + + private static int test2Helper(int start, int stop, int[] array) { + if (array == null) { + } + int v = 0; + for (int i = start-1; i > stop; i--) { + v = array[-1 - i]; + } + return v; + } + +} diff --git a/test/hotspot/jtreg/compiler/vectorapi/VectorReplicateLongSpecialImmTest.java b/test/hotspot/jtreg/compiler/vectorapi/VectorReplicateLongSpecialImmTest.java index e2ad716d1fa..b885abb632a 100644 --- a/test/hotspot/jtreg/compiler/vectorapi/VectorReplicateLongSpecialImmTest.java +++ b/test/hotspot/jtreg/compiler/vectorapi/VectorReplicateLongSpecialImmTest.java @@ -36,7 +36,7 @@ * @library /test/lib * @requires os.arch == "aarch64" * @modules jdk.incubator.vector - * @run testng/othervm -XX:UseSVE=0 -XX:-TieredCompilation -XX:CompileThreshold=100 compiler.vectorapi.VectorReplicateLongSpecialImmTest + * @run testng/othervm -XX:UseSVE=0 -XX:-TieredCompilation -XX:CompileThreshold=100 -XX:+IgnoreUnrecognizedVMOptions -XX:CompileCommand=MemLimit,*.*,0 compiler.vectorapi.VectorReplicateLongSpecialImmTest */ public class VectorReplicateLongSpecialImmTest { diff --git a/test/hotspot/jtreg/gc/TestAllocHumongousFragment.java b/test/hotspot/jtreg/gc/TestAllocHumongousFragment.java index 88b838b4bde..6498a394911 100644 --- a/test/hotspot/jtreg/gc/TestAllocHumongousFragment.java +++ b/test/hotspot/jtreg/gc/TestAllocHumongousFragment.java @@ -120,51 +120,6 @@ * TestAllocHumongousFragment */ -/* - * @test id=iu-aggressive - * @summary Make sure Shenandoah can recover from humongous allocation fragmentation - * @key randomness - * @requires vm.gc.Shenandoah - * @library /test/lib - * - * @run main/othervm -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g -XX:ShenandoahTargetNumRegions=2048 - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahOOMDuringEvacALot -XX:+ShenandoahVerify - * TestAllocHumongousFragment - * - * @run main/othervm -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g -XX:ShenandoahTargetNumRegions=2048 - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahAllocFailureALot -XX:+ShenandoahVerify - * TestAllocHumongousFragment - * - * @run main/othervm -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g -XX:ShenandoahTargetNumRegions=2048 - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahOOMDuringEvacALot - * TestAllocHumongousFragment - * - * @run main/othervm -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g -XX:ShenandoahTargetNumRegions=2048 - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahAllocFailureALot - * TestAllocHumongousFragment - */ - -/* - * @test id=iu - * @summary Make sure Shenandoah can recover from humongous allocation fragmentation - * @key randomness - * @requires vm.gc.Shenandoah - * @library /test/lib - * - * @run main/othervm -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g -XX:ShenandoahTargetNumRegions=2048 - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify - * TestAllocHumongousFragment - * - * @run main/othervm -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g -XX:ShenandoahTargetNumRegions=2048 - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * TestAllocHumongousFragment - */ - /* * @test id=g1 * @summary Make sure G1 can recover from humongous allocation fragmentation diff --git a/test/hotspot/jtreg/gc/TestDisableExplicitGC.java b/test/hotspot/jtreg/gc/TestDisableExplicitGC.java index 9a0b61900dd..08fda78baaa 100644 --- a/test/hotspot/jtreg/gc/TestDisableExplicitGC.java +++ b/test/hotspot/jtreg/gc/TestDisableExplicitGC.java @@ -26,6 +26,7 @@ /* * @test TestDisableExplicitGC * @requires vm.opt.DisableExplicitGC == null + * @requires vm.compMode != "Xcomp" * @summary Verify GC behavior with DisableExplicitGC flag. * @library /test/lib * @modules java.base/jdk.internal.misc diff --git a/test/hotspot/jtreg/gc/arguments/TestG1PercentageOptions.java b/test/hotspot/jtreg/gc/arguments/TestG1PercentageOptions.java index e8ee2598c9a..48345da250a 100644 --- a/test/hotspot/jtreg/gc/arguments/TestG1PercentageOptions.java +++ b/test/hotspot/jtreg/gc/arguments/TestG1PercentageOptions.java @@ -51,14 +51,14 @@ private static final class OptionDescription { } } - private static final String[] defaultValid = new String[] { - "0", "1", "50", "95", "100" }; - private static final String[] defaultInvalid = new String[] { - "-10", "110", "bad" }; + private static final String[] rangeOneToHundredValid = new String[] { + "1", "50", "95", "100" }; + private static final String[] rangeOneToHundredInvalid = new String[] { + "0", "-10", "110", "bad" }; // All of the G1 product arguments that are percentages. private static final OptionDescription[] percentOptions = new OptionDescription[] { - new OptionDescription("G1ConfidencePercent", defaultValid, defaultInvalid) + new OptionDescription("G1ConfidencePercent", rangeOneToHundredValid, rangeOneToHundredInvalid) // Other percentage options are not yet validated by argument processing. }; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestAllocIntArrays.java b/test/hotspot/jtreg/gc/shenandoah/TestAllocIntArrays.java index 35b0cdfa49e..b10e90b4055 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestAllocIntArrays.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestAllocIntArrays.java @@ -135,56 +135,6 @@ * -XX:-UseTLAB -XX:+ShenandoahVerify * TestAllocIntArrays */ - -/* - * @test id=iu-aggressive - * @summary Acceptance tests: collector can withstand allocation - * @key randomness - * @requires vm.gc.Shenandoah - * @library /test/lib - * - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahOOMDuringEvacALot -XX:+ShenandoahVerify - * TestAllocIntArrays - * - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahAllocFailureALot -XX:+ShenandoahVerify - * TestAllocIntArrays - * - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahOOMDuringEvacALot - * TestAllocIntArrays - * - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahAllocFailureALot - * TestAllocIntArrays - * - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * TestAllocIntArrays - */ - -/* - * @test id=iu - * @summary Acceptance tests: collector can withstand allocation - * @key randomness - * @requires vm.gc.Shenandoah - * @library /test/lib - * - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify - * TestAllocIntArrays - * - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * TestAllocIntArrays - */ - import java.util.Random; import jdk.test.lib.Utils; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestAllocObjectArrays.java b/test/hotspot/jtreg/gc/shenandoah/TestAllocObjectArrays.java index 8eebba4a308..fa9bb7f0177 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestAllocObjectArrays.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestAllocObjectArrays.java @@ -135,56 +135,6 @@ * -XX:-UseTLAB -XX:+ShenandoahVerify * TestAllocObjectArrays */ - -/* - * @test id=iu-aggressive - * @summary Acceptance tests: collector can withstand allocation - * @key randomness - * @requires vm.gc.Shenandoah - * @library /test/lib - * - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahOOMDuringEvacALot -XX:+ShenandoahVerify - * TestAllocObjectArrays - * - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahAllocFailureALot -XX:+ShenandoahVerify - * TestAllocObjectArrays - * - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahOOMDuringEvacALot - * TestAllocObjectArrays - * - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahAllocFailureALot - * TestAllocObjectArrays - * - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * TestAllocObjectArrays - */ - -/* - * @test id=iu - * @summary Acceptance tests: collector can withstand allocation - * @key randomness - * @requires vm.gc.Shenandoah - * @library /test/lib - * - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify - * TestAllocObjectArrays - * - * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xmx1g -Xms1g - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * TestAllocObjectArrays - */ - import java.util.Random; import jdk.test.lib.Utils; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestAllocObjects.java b/test/hotspot/jtreg/gc/shenandoah/TestAllocObjects.java index 32178555c9f..26dd98ed403 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestAllocObjects.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestAllocObjects.java @@ -112,52 +112,6 @@ * -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=compact * TestAllocObjects */ - -/* - * @test id=iu-aggressive - * @summary Acceptance tests: collector can withstand allocation - * @requires vm.gc.Shenandoah - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahOOMDuringEvacALot -XX:+ShenandoahVerify - * TestAllocObjects - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahAllocFailureALot -XX:+ShenandoahVerify - * TestAllocObjects - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahOOMDuringEvacALot - * TestAllocObjects - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahAllocFailureALot - * TestAllocObjects - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * TestAllocObjects - */ - -/* - * @test id=iu - * @summary Acceptance tests: collector can withstand allocation - * @requires vm.gc.Shenandoah - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify - * TestAllocObjects - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * TestAllocObjects - */ - public class TestAllocObjects { static final long TARGET_MB = Long.getLong("target", 10_000); // 10 Gb allocation diff --git a/test/hotspot/jtreg/gc/shenandoah/TestDynamicSoftMaxHeapSize.java b/test/hotspot/jtreg/gc/shenandoah/TestDynamicSoftMaxHeapSize.java index f34fcd8024d..b47a818694f 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestDynamicSoftMaxHeapSize.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestDynamicSoftMaxHeapSize.java @@ -84,28 +84,6 @@ * TestDynamicSoftMaxHeapSize */ -/* - * @test id=iu-aggressive - * @requires vm.gc.Shenandoah - * @library /test/lib - * - * @run main/othervm -Xms16m -Xmx512m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -Dtarget=1000 - * TestDynamicSoftMaxHeapSize - */ - -/* - * @test id=iu - * @requires vm.gc.Shenandoah - * @library /test/lib - * - * @run main/othervm -Xms16m -Xmx512m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -Dtarget=10000 - * TestDynamicSoftMaxHeapSize - */ - import java.util.Random; import jdk.test.lib.Utils; import jdk.test.lib.process.OutputAnalyzer; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestGCThreadGroups.java b/test/hotspot/jtreg/gc/shenandoah/TestGCThreadGroups.java index ff1596d6833..1e8aaa32dd6 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestGCThreadGroups.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestGCThreadGroups.java @@ -76,24 +76,6 @@ * TestGCThreadGroups */ -/** - * @test id=iu - * @summary Test Shenandoah GC uses concurrent/parallel threads correctly - * @requires vm.gc.Shenandoah - * - * @run main/othervm -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:ConcGCThreads=2 -XX:ParallelGCThreads=4 - * -Dtarget=1000 - * TestGCThreadGroups - * - * @run main/othervm -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:ConcGCThreads=2 -XX:ParallelGCThreads=4 - * -Dtarget=1000 - * TestGCThreadGroups -*/ - public class TestGCThreadGroups { static final long TARGET_MB = Long.getLong("target", 10_000); // 10 Gb allocation, around 1K cycles to handle diff --git a/test/hotspot/jtreg/gc/shenandoah/TestHeapUncommit.java b/test/hotspot/jtreg/gc/shenandoah/TestHeapUncommit.java index 6c54a34dae5..a57d9c7c49a 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestHeapUncommit.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestHeapUncommit.java @@ -84,27 +84,6 @@ * TestHeapUncommit */ -/* - * @test id=iu - * @summary Acceptance tests: collector can withstand allocation - * @key randomness - * @requires vm.gc.Shenandoah - * @library /test/lib - * - * @run main/othervm -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+ShenandoahUncommit -XX:ShenandoahUncommitDelay=0 - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify - * TestHeapUncommit - * - * @run main/othervm -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+ShenandoahUncommit -XX:ShenandoahUncommitDelay=0 - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * TestHeapUncommit - * - * @run main/othervm -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+ShenandoahUncommit -XX:ShenandoahUncommitDelay=0 - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * TestHeapUncommit - */ - /* * @test id=default-lp * @key randomness diff --git a/test/hotspot/jtreg/gc/shenandoah/TestJcmdHeapDump.java b/test/hotspot/jtreg/gc/shenandoah/TestJcmdHeapDump.java index 40a36d0a5ef..6d8b1cb3875 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestJcmdHeapDump.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestJcmdHeapDump.java @@ -94,38 +94,6 @@ * TestJcmdHeapDump */ -/* - * @test id=iu-aggressive - * @library /test/lib - * @modules jdk.attach/com.sun.tools.attach - * @requires vm.gc.Shenandoah - * - * @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahOOMDuringEvacALot - * TestJcmdHeapDump - * - * @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahAllocFailureALot - * TestJcmdHeapDump - * - * @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * TestJcmdHeapDump - */ - -/* - * @test id=iu - * @requires vm.gc.Shenandoah - * @library /test/lib - * @modules jdk.attach/com.sun.tools.attach - * - * @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * TestJcmdHeapDump - */ - import jdk.test.lib.JDKToolLauncher; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestLotsOfCycles.java b/test/hotspot/jtreg/gc/shenandoah/TestLotsOfCycles.java index 63d9fa08767..9d7738c25ce 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestLotsOfCycles.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestLotsOfCycles.java @@ -91,38 +91,6 @@ * TestLotsOfCycles */ -/* - * @test id=iu-aggressive - * @requires vm.gc.Shenandoah - * - * @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahOOMDuringEvacALot - * -Dtarget=1000 - * TestLotsOfCycles - * - * @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahAllocFailureALot - * -Dtarget=1000 - * TestLotsOfCycles - * - * @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -Dtarget=1000 - * TestLotsOfCycles - */ - -/* - * @test id=iu - * @requires vm.gc.Shenandoah - * - * @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -Dtarget=10000 - * TestLotsOfCycles - */ - public class TestLotsOfCycles { static final long TARGET_MB = Long.getLong("target", 10_000); // 10 Gb allocation, around 1K cycles to handle diff --git a/test/hotspot/jtreg/gc/shenandoah/TestObjItrWithHeapDump.java b/test/hotspot/jtreg/gc/shenandoah/TestObjItrWithHeapDump.java index a0612a60a9f..b30f7fd9ac4 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestObjItrWithHeapDump.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestObjItrWithHeapDump.java @@ -56,7 +56,6 @@ public static void main(String[] args) throws Exception { String[][][] modeHeuristics = new String[][][] { {{"satb"}, {"adaptive", "compact", "static", "aggressive"}}, - {{"iu"}, {"adaptive", "aggressive"}}, {{"passive"}, {"passive"}} }; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestPeriodicGC.java b/test/hotspot/jtreg/gc/shenandoah/TestPeriodicGC.java index 3a5780069ef..2849b58aa94 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestPeriodicGC.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestPeriodicGC.java @@ -97,36 +97,6 @@ public static void main(String[] args) throws Exception { ); } - testWith("Zero interval with iu mode", - false, - "-Xlog:gc", - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+UnlockExperimentalVMOptions", - "-XX:+UseShenandoahGC", - "-XX:ShenandoahGCMode=iu", - "-XX:ShenandoahGuaranteedGCInterval=0" - ); - - testWith("Short interval with iu mode", - true, - "-Xlog:gc", - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+UnlockExperimentalVMOptions", - "-XX:+UseShenandoahGC", - "-XX:ShenandoahGCMode=iu", - "-XX:ShenandoahGuaranteedGCInterval=1000" - ); - - testWith("Long interval with iu mode", - false, - "-Xlog:gc", - "-XX:+UnlockDiagnosticVMOptions", - "-XX:+UnlockExperimentalVMOptions", - "-XX:+UseShenandoahGC", - "-XX:ShenandoahGCMode=iu", - "-XX:ShenandoahGuaranteedGCInterval=100000" // deliberately too long - ); - testWith("Short interval with aggressive", false, "-Xlog:gc", diff --git a/test/hotspot/jtreg/gc/shenandoah/TestReferenceRefersToShenandoah.java b/test/hotspot/jtreg/gc/shenandoah/TestReferenceRefersToShenandoah.java index 8bad2fdbfa4..3763a7eea87 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestReferenceRefersToShenandoah.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestReferenceRefersToShenandoah.java @@ -35,18 +35,6 @@ * gc.shenandoah.TestReferenceRefersToShenandoah */ -/* @test id=iu - * @requires vm.gc.Shenandoah - * @library /test/lib - * @build jdk.test.whitebox.WhiteBox - * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm - * -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * gc.shenandoah.TestReferenceRefersToShenandoah - */ - /* @test id=satb-100 * @requires vm.gc.Shenandoah * @library /test/lib @@ -60,19 +48,6 @@ * gc.shenandoah.TestReferenceRefersToShenandoah */ -/* @test id=iu-100 - * @requires vm.gc.Shenandoah - * @library /test/lib - * @build jdk.test.whitebox.WhiteBox - * @modules java.base - * @run main jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm - * -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGarbageThreshold=100 -Xmx100m - * gc.shenandoah.TestReferenceRefersToShenandoah - */ - import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; @@ -200,10 +175,6 @@ private static void discardStrongReferences() { testObject4 = null; } - private static boolean isShenandoahIUMode() { - return "iu".equals(WB.getStringVMFlag("ShenandoahGCMode")); - } - private static void testConcurrentCollection() throws Exception { progress("setup concurrent collection test"); setup(); @@ -239,14 +210,7 @@ private static void testConcurrentCollection() throws Exception { expectCleared(testPhantom1, "testPhantom1"); expectCleared(testWeak2, "testWeak2"); expectValue(testWeak3, testObject3, "testWeak3"); - // This is true for all currently supported concurrent collectors, - // except Shenandoah+IU, which allows clearing refs even when - // accessed during concurrent marking. - if (isShenandoahIUMode()) { - expectCleared(testWeak4, "testWeak4"); - } else { - expectNotCleared(testWeak4, "testWeak4"); - } + expectNotCleared(testWeak4, "testWeak4"); progress("verify get returns expected values"); if (testWeak2.get() != null) { @@ -261,12 +225,10 @@ private static void testConcurrentCollection() throws Exception { } TestObject obj4 = testWeak4.get(); - if (!isShenandoahIUMode()) { - if (obj4 == null) { - fail("testWeak4.get() returned null"); - } else if (obj4.value != 4) { - fail("testWeak4.get().value is " + obj4.value); - } + if (obj4 == null) { + fail("testWeak4.get() returned null"); + } else if (obj4.value != 4) { + fail("testWeak4.get().value is " + obj4.value); } progress("verify queue entries"); diff --git a/test/hotspot/jtreg/gc/shenandoah/TestReferenceShortcutCycle.java b/test/hotspot/jtreg/gc/shenandoah/TestReferenceShortcutCycle.java index 1c2c2ed5ca7..b7ae0c1c31a 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestReferenceShortcutCycle.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestReferenceShortcutCycle.java @@ -36,19 +36,6 @@ * gc.shenandoah.TestReferenceShortcutCycle */ -/* @test id=iu-100 - * @requires vm.gc.Shenandoah - * @library /test/lib - * @build jdk.test.whitebox.WhiteBox - * @modules java.base - * @run main jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm - * -Xbootclasspath/a:. - * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI - * -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGarbageThreshold=100 -Xmx100m - * gc.shenandoah.TestReferenceShortcutCycle - */ - import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestRefprocSanity.java b/test/hotspot/jtreg/gc/shenandoah/TestRefprocSanity.java index 03f008d10c3..0bf7672e7d5 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestRefprocSanity.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestRefprocSanity.java @@ -41,25 +41,6 @@ * TestRefprocSanity */ -/* - * @test id=iu - * @summary Test that null references/referents work fine - * @requires vm.gc.Shenandoah - * - * @run main/othervm -Xmx128m -Xms128m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify - * TestRefprocSanity - * - * @run main/othervm -Xmx128m -Xms128m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * TestRefprocSanity - * - * @run main/othervm -Xmx128m -Xms128m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * TestRefprocSanity - */ - import java.lang.ref.*; public class TestRefprocSanity { diff --git a/test/hotspot/jtreg/gc/shenandoah/TestRegionSampling.java b/test/hotspot/jtreg/gc/shenandoah/TestRegionSampling.java index dd98585181c..dfa09a2f5ab 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestRegionSampling.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestRegionSampling.java @@ -73,25 +73,6 @@ * TestRegionSampling */ -/* - * @test id=iu-aggressive - * @requires vm.gc.Shenandoah - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+ShenandoahRegionSampling - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * TestRegionSampling - */ - -/* - * @test id=iu - * @requires vm.gc.Shenandoah - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+ShenandoahRegionSampling - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * TestRegionSampling - * - */ - public class TestRegionSampling { static final long TARGET_MB = Long.getLong("target", 2_000); // 2 Gb allocation diff --git a/test/hotspot/jtreg/gc/shenandoah/TestResizeTLAB.java b/test/hotspot/jtreg/gc/shenandoah/TestResizeTLAB.java index 095f9939569..4e3089d0862 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestResizeTLAB.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestResizeTLAB.java @@ -138,46 +138,6 @@ * TestResizeTLAB */ -/* - * @test id=iu-aggressive - * @key randomness - * @summary Test that Shenandoah is able to work with(out) resizeable TLABs - * @requires vm.gc.Shenandoah - * @library /test/lib - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahVerify - * -XX:+ResizeTLAB - * TestResizeTLAB - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahVerify - * -XX:-ResizeTLAB - * TestResizeTLAB - */ - -/* - * @test id=iu - * @key randomness - * @summary Test that Shenandoah is able to work with(out) resizeable TLABs - * @requires vm.gc.Shenandoah - * @library /test/lib - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify - * -XX:+ResizeTLAB - * TestResizeTLAB - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify - * -XX:-ResizeTLAB - * TestResizeTLAB - */ - import java.util.Random; import jdk.test.lib.Utils; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestRetainObjects.java b/test/hotspot/jtreg/gc/shenandoah/TestRetainObjects.java index 7a28548f610..7be388b7945 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestRetainObjects.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestRetainObjects.java @@ -114,41 +114,6 @@ * TestRetainObjects */ -/* - * @test id=iu-aggressive - * @summary Acceptance tests: collector can deal with retained objects - * @requires vm.gc.Shenandoah - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahOOMDuringEvacALot - * TestRetainObjects - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahAllocFailureALot - * TestRetainObjects - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * TestRetainObjects - */ - -/* - * @test id=iu - * @summary Acceptance tests: collector can deal with retained objects - * @requires vm.gc.Shenandoah - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify - * TestRetainObjects - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * TestRetainObjects - */ - public class TestRetainObjects { static final int COUNT = 10_000_000; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestSieveObjects.java b/test/hotspot/jtreg/gc/shenandoah/TestSieveObjects.java index 1b16ba4b8d2..2eaead55a6a 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestSieveObjects.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestSieveObjects.java @@ -127,45 +127,6 @@ * TestSieveObjects */ -/* - * @test id=iu-aggressive - * @summary Acceptance tests: collector can deal with retained objects - * @key randomness - * @requires vm.gc.Shenandoah - * @library /test/lib - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahOOMDuringEvacALot - * TestSieveObjects - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahAllocFailureALot - * TestSieveObjects - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * TestSieveObjects - */ - -/* - * @test id=iu - * @summary Acceptance tests: collector can deal with retained objects - * @key randomness - * @requires vm.gc.Shenandoah - * @library /test/lib - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify - * TestSieveObjects - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * TestSieveObjects - */ - import java.util.Random; import jdk.test.lib.Utils; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestStringDedup.java b/test/hotspot/jtreg/gc/shenandoah/TestStringDedup.java index 94691432f38..9fdb822d48c 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestStringDedup.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestStringDedup.java @@ -65,24 +65,6 @@ * TestStringDedup */ -/* - * @test id=iu - * @summary Test Shenandoah string deduplication implementation - * @key randomness - * @requires vm.gc.Shenandoah - * @library /test/lib - * @modules java.base/java.lang:open - * java.management - * - * @run main/othervm -Xmx256m -Xlog:gc+stats -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:StringDeduplicationAgeThreshold=3 - * TestStringDedup - * - * @run main/othervm -Xmx256m -Xlog:gc+stats -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive -XX:StringDeduplicationAgeThreshold=3 - * TestStringDedup - */ - import java.lang.reflect.*; import java.util.*; import jdk.test.lib.Utils; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestStringDedupStress.java b/test/hotspot/jtreg/gc/shenandoah/TestStringDedupStress.java index 0382d0e00dd..2d928e848ce 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestStringDedupStress.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestStringDedupStress.java @@ -72,37 +72,6 @@ * TestStringDedupStress */ - /* - * @test id=iu - * @summary Test Shenandoah string deduplication implementation - * @key randomness - * @requires vm.gc.Shenandoah - * @library /test/lib - * @modules java.base/java.lang:open - * java.management - * - * @run main/othervm -Xmx1g -Xlog:gc+stats -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * TestStringDedupStress - * - * @run main/othervm -Xmx1g -Xlog:gc+stats -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -DtargetStrings=2000000 - * TestStringDedupStress - * - * @run main/othervm -Xmx1g -Xlog:gc+stats -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahOOMDuringEvacALot - * -DtargetStrings=2000000 - * TestStringDedupStress - * - * @run main/othervm -Xmx1g -Xlog:gc+stats -Xlog:gc -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseStringDeduplication - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahOOMDuringEvacALot - * -DtargetStrings=2000000 - * TestStringDedupStress - */ - import java.lang.management.*; import java.lang.reflect.*; import java.util.*; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestStringInternCleanup.java b/test/hotspot/jtreg/gc/shenandoah/TestStringInternCleanup.java index cf663f2329c..62c9e16f777 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestStringInternCleanup.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestStringInternCleanup.java @@ -75,26 +75,6 @@ * TestStringInternCleanup */ -/* - * @test id=iu - * @summary Check that Shenandoah cleans up interned strings - * @requires vm.gc.Shenandoah - * - * @run main/othervm -Xmx64m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+ClassUnloadingWithConcurrentMark - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify - * TestStringInternCleanup - * - * @run main/othervm -Xmx64m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+ClassUnloadingWithConcurrentMark - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahVerify - * TestStringInternCleanup - * - * @run main/othervm -Xmx64m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+ClassUnloadingWithConcurrentMark - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * TestStringInternCleanup - */ - public class TestStringInternCleanup { static final int COUNT = 1_000_000; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestVerifyJCStress.java b/test/hotspot/jtreg/gc/shenandoah/TestVerifyJCStress.java index 312ab964f5e..82bc74daddc 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestVerifyJCStress.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestVerifyJCStress.java @@ -58,33 +58,6 @@ * TestVerifyJCStress */ -/* - * @test id=iu - * @summary Tests that we pass at least one jcstress-like test with all verification turned on - * @requires vm.gc.Shenandoah - * @modules java.base/jdk.internal.misc - * java.management - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify - * TestVerifyJCStress - */ - -/* - * @test id=iu-c1 - * @summary Tests that we pass at least one jcstress-like test with all verification turned on - * @requires vm.gc.Shenandoah - * @modules java.base/jdk.internal.misc - * java.management - * - * @run main/othervm -Xmx1g -Xms1g -XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify -XX:TieredStopAtLevel=1 - * TestVerifyJCStress - */ - - import java.util.*; import java.util.concurrent.*; import java.util.concurrent.locks.*; diff --git a/test/hotspot/jtreg/gc/shenandoah/TestWrongArrayMember.java b/test/hotspot/jtreg/gc/shenandoah/TestWrongArrayMember.java index 6712cb49561..6dcdf259c6c 100644 --- a/test/hotspot/jtreg/gc/shenandoah/TestWrongArrayMember.java +++ b/test/hotspot/jtreg/gc/shenandoah/TestWrongArrayMember.java @@ -26,8 +26,7 @@ * @test * @requires vm.gc.Shenandoah * - * @run main/othervm -Xmx128m -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC TestWrongArrayMember - * @run main/othervm -Xmx128m -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu TestWrongArrayMember + * @run main/othervm -Xmx128m -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC TestWrongArrayMember */ public class TestWrongArrayMember { diff --git a/test/hotspot/jtreg/gc/shenandoah/compiler/BarrierInInfiniteLoop.java b/test/hotspot/jtreg/gc/shenandoah/compiler/BarrierInInfiniteLoop.java index 8673005f96f..9054c1f470e 100644 --- a/test/hotspot/jtreg/gc/shenandoah/compiler/BarrierInInfiniteLoop.java +++ b/test/hotspot/jtreg/gc/shenandoah/compiler/BarrierInInfiniteLoop.java @@ -30,8 +30,6 @@ * * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -Xcomp -XX:CompileOnly=BarrierInInfiniteLoop::test1 * -XX:CompileOnly=BarrierInInfiniteLoop::test2 -XX:CompileOnly=BarrierInInfiniteLoop::test3 -XX:CompileCommand=quiet BarrierInInfiniteLoop - * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -Xcomp -XX:CompileOnly=BarrierInInfiniteLoop::test1 - * -XX:CompileOnly=BarrierInInfiniteLoop::test2 -XX:CompileOnly=BarrierInInfiniteLoop::test3 -XX:CompileCommand=quiet BarrierInInfiniteLoop * */ diff --git a/test/hotspot/jtreg/gc/shenandoah/compiler/TestUnexpectedIUBarrierEA.java b/test/hotspot/jtreg/gc/shenandoah/compiler/TestUnexpectedIUBarrierEA.java deleted file mode 100644 index 8662130c378..00000000000 --- a/test/hotspot/jtreg/gc/shenandoah/compiler/TestUnexpectedIUBarrierEA.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2022, Red Hat, Inc. 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 8280885 - * @summary Shenandoah: Some tests failed with "EA: missing allocation reference path" - * @requires vm.gc.Shenandoah - * - * @run main/othervm -XX:-BackgroundCompilation -XX:+UseShenandoahGC -XX:+UnlockExperimentalVMOptions -XX:ShenandoahGCMode=iu - * -XX:CompileCommand=dontinline,TestUnexpectedIUBarrierEA::notInlined TestUnexpectedIUBarrierEA - */ - -public class TestUnexpectedIUBarrierEA { - - private static Object field; - - public static void main(String[] args) { - for (int i = 0; i < 20_000; i++) { - test(false); - } - } - - private static void test(boolean flag) { - A a = new A(); - B b = new B(); - b.field = a; - notInlined(); - Object o = b.field; - if (!(o instanceof A)) { - - } - C c = new C(); - c.field = o; - if (flag) { - field = c.field; - } - } - - private static void notInlined() { - - } - - private static class A { - } - - private static class B { - public Object field; - } - - private static class C { - public Object field; - } -} diff --git a/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestChurnNotifications.java b/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestChurnNotifications.java index 96586b27f65..73743aadedc 100644 --- a/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestChurnNotifications.java +++ b/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestChurnNotifications.java @@ -87,23 +87,6 @@ * TestChurnNotifications */ -/* - * @test id=iu - * @summary Check that MX notifications are reported for all cycles - * @library /test/lib / - * @requires vm.gc.Shenandoah - * - * @run main/othervm -Xmx128m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -Dprecise=false - * TestChurnNotifications - * - * @run main/othervm -Xmx128m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -Dprecise=false - * TestChurnNotifications - */ - import java.util.*; import java.util.concurrent.atomic.*; import javax.management.*; diff --git a/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestPauseNotifications.java b/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestPauseNotifications.java index 0919a21d370..2dd9aba4c62 100644 --- a/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestPauseNotifications.java +++ b/test/hotspot/jtreg/gc/shenandoah/mxbeans/TestPauseNotifications.java @@ -83,21 +83,6 @@ * TestPauseNotifications */ -/* - * @test id=iu - * @summary Check that MX notifications are reported for all cycles - * @library /test/lib / - * @requires vm.gc.Shenandoah - * - * @run main/othervm -Xmx128m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * TestPauseNotifications - * - * @run main/othervm -Xmx128m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * TestPauseNotifications - */ - import java.util.*; import java.util.concurrent.atomic.*; import javax.management.*; diff --git a/test/hotspot/jtreg/gc/shenandoah/oom/TestClassLoaderLeak.java b/test/hotspot/jtreg/gc/shenandoah/oom/TestClassLoaderLeak.java index 9d17e916089..1a3d07bf80d 100644 --- a/test/hotspot/jtreg/gc/shenandoah/oom/TestClassLoaderLeak.java +++ b/test/hotspot/jtreg/gc/shenandoah/oom/TestClassLoaderLeak.java @@ -124,7 +124,6 @@ public static void main(String[] args) throws Exception { String[][][] modeHeuristics = new String[][][] { {{"satb"}, {"adaptive", "compact", "static", "aggressive"}}, - {{"iu"}, {"adaptive", "aggressive"}}, {{"passive"}, {"passive"}} }; diff --git a/test/hotspot/jtreg/gc/shenandoah/options/TestExplicitGC.java b/test/hotspot/jtreg/gc/shenandoah/options/TestExplicitGC.java index 7fe5d56f7de..4effff7ebd5 100644 --- a/test/hotspot/jtreg/gc/shenandoah/options/TestExplicitGC.java +++ b/test/hotspot/jtreg/gc/shenandoah/options/TestExplicitGC.java @@ -125,23 +125,5 @@ public static void main(String[] args) throws Exception { output.shouldNotContain(p); } } - - { - OutputAnalyzer output = ProcessTools.executeLimitedTestJava( - "-Xmx128m", - "-XX:+UnlockExperimentalVMOptions", - "-XX:+UseShenandoahGC", - "-Xlog:gc", - "-XX:+ExplicitGCInvokesConcurrent", - "-XX:ShenandoahGCMode=iu", - TestExplicitGC.class.getName(), - "test"); - for (String p : full) { - output.shouldNotContain(p); - } - for (String p : concNormal) { - output.shouldContain(p); - } - } } } diff --git a/test/hotspot/jtreg/gc/shenandoah/options/TestModeUnlock.java b/test/hotspot/jtreg/gc/shenandoah/options/TestModeUnlock.java index 802038363b5..5422a86a496 100644 --- a/test/hotspot/jtreg/gc/shenandoah/options/TestModeUnlock.java +++ b/test/hotspot/jtreg/gc/shenandoah/options/TestModeUnlock.java @@ -45,7 +45,6 @@ enum Mode { public static void main(String[] args) throws Exception { testWith("-XX:ShenandoahGCMode=satb", Mode.PRODUCT); - testWith("-XX:ShenandoahGCMode=iu", Mode.EXPERIMENTAL); testWith("-XX:ShenandoahGCMode=passive", Mode.DIAGNOSTIC); } diff --git a/test/hotspot/jtreg/gc/shenandoah/options/TestSelectiveBarrierFlags.java b/test/hotspot/jtreg/gc/shenandoah/options/TestSelectiveBarrierFlags.java index 094e62f53f3..fe29a38c422 100644 --- a/test/hotspot/jtreg/gc/shenandoah/options/TestSelectiveBarrierFlags.java +++ b/test/hotspot/jtreg/gc/shenandoah/options/TestSelectiveBarrierFlags.java @@ -50,7 +50,7 @@ public class TestSelectiveBarrierFlags { public static void main(String[] args) throws Exception { String[][] opts = { new String[] { "ShenandoahLoadRefBarrier" }, - new String[] { "ShenandoahSATBBarrier", "ShenandoahIUBarrier" }, + new String[] { "ShenandoahSATBBarrier" }, new String[] { "ShenandoahCASBarrier" }, new String[] { "ShenandoahCloneBarrier" }, new String[] { "ShenandoahStackWatermarkBarrier" } diff --git a/test/hotspot/jtreg/gc/shenandoah/options/TestWrongBarrierDisable.java b/test/hotspot/jtreg/gc/shenandoah/options/TestWrongBarrierDisable.java index aa6b7935649..9bf1e745856 100644 --- a/test/hotspot/jtreg/gc/shenandoah/options/TestWrongBarrierDisable.java +++ b/test/hotspot/jtreg/gc/shenandoah/options/TestWrongBarrierDisable.java @@ -44,21 +44,12 @@ public static void main(String[] args) throws Exception { "ShenandoahCloneBarrier", "ShenandoahStackWatermarkBarrier", }; - String[] iu = { - "ShenandoahLoadRefBarrier", - "ShenandoahIUBarrier", - "ShenandoahCASBarrier", - "ShenandoahCloneBarrier", - "ShenandoahStackWatermarkBarrier", - }; shouldFailAll("-XX:ShenandoahGCHeuristics=adaptive", concurrent); shouldFailAll("-XX:ShenandoahGCHeuristics=static", concurrent); shouldFailAll("-XX:ShenandoahGCHeuristics=compact", concurrent); shouldFailAll("-XX:ShenandoahGCHeuristics=aggressive", concurrent); - shouldFailAll("-XX:ShenandoahGCMode=iu", iu); shouldPassAll("-XX:ShenandoahGCMode=passive", concurrent); - shouldPassAll("-XX:ShenandoahGCMode=passive", iu); } private static void shouldFailAll(String h, String[] barriers) throws Exception { diff --git a/test/hotspot/jtreg/gc/shenandoah/options/TestWrongBarrierEnable.java b/test/hotspot/jtreg/gc/shenandoah/options/TestWrongBarrierEnable.java index 486860728ab..ea64d3ee712 100644 --- a/test/hotspot/jtreg/gc/shenandoah/options/TestWrongBarrierEnable.java +++ b/test/hotspot/jtreg/gc/shenandoah/options/TestWrongBarrierEnable.java @@ -23,7 +23,7 @@ */ /* @test - * @summary Test that disabling wrong barriers fails early + * @summary Test that SATB barrier may be enabled for all modes * @requires vm.gc.Shenandoah * @library /test/lib * @run driver TestWrongBarrierEnable @@ -38,19 +38,13 @@ public class TestWrongBarrierEnable { public static void main(String[] args) throws Exception { String[] concurrent = { - "ShenandoahIUBarrier", - }; - String[] iu = { "ShenandoahSATBBarrier", }; - - shouldFailAll("-XX:ShenandoahGCHeuristics=adaptive", concurrent); - shouldFailAll("-XX:ShenandoahGCHeuristics=static", concurrent); - shouldFailAll("-XX:ShenandoahGCHeuristics=compact", concurrent); - shouldFailAll("-XX:ShenandoahGCHeuristics=aggressive", concurrent); - shouldFailAll("-XX:ShenandoahGCMode=iu", iu); + shouldPassAll("-XX:ShenandoahGCHeuristics=adaptive", concurrent); + shouldPassAll("-XX:ShenandoahGCHeuristics=static", concurrent); + shouldPassAll("-XX:ShenandoahGCHeuristics=compact", concurrent); + shouldPassAll("-XX:ShenandoahGCHeuristics=aggressive", concurrent); shouldPassAll("-XX:ShenandoahGCMode=passive", concurrent); - shouldPassAll("-XX:ShenandoahGCMode=passive", iu); } private static void shouldFailAll(String h, String[] barriers) throws Exception { diff --git a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithShenandoah.java b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithShenandoah.java index 6e98f99b548..501605cd0f5 100644 --- a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithShenandoah.java +++ b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithShenandoah.java @@ -99,47 +99,6 @@ * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 */ -/* - * @test id=iu-aggressive - * @key stress - * @library / - * @requires vm.gc.Shenandoah - * @requires vm.flavor == "server" & !vm.emulatedClient - * @summary Stress the Shenandoah GC by trying to make old objects more likely to be garbage than young objects. - * - * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahOOMDuringEvacALot - * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 - * - * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahAllocFailureALot - * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 - * - * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 - */ - -/* - * @test id=iu - * @key stress - * @library / - * @requires vm.gc.Shenandoah - * @requires vm.flavor == "server" & !vm.emulatedClient - * @summary Stress the Shenandoah GC by trying to make old objects more likely to be garbage than young objects. - * - * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify - * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 - * - * @run main/othervm/timeout=200 -Xlog:gc*=info -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 - */ - /* * @test id=passive-deopt-nmethod * @key stress @@ -221,53 +180,6 @@ * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 */ -/* - * @test id=iu-aggressive-deopt-nmethod - * @key stress - * @library / - * @requires vm.gc.Shenandoah - * @requires vm.flavor == "server" & !vm.emulatedClient & vm.opt.ClassUnloading != false - * @summary Stress Shenandoah GC with nmethod barrier forced deoptimization enabled. - * - * @run main/othervm/timeout=200 -Xlog:gc*=info,nmethod+barrier=trace -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+DeoptimizeNMethodBarriersALot -XX:-Inline - * -XX:+ShenandoahOOMDuringEvacALot - * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 - * - * @run main/othervm/timeout=200 -Xlog:gc*=info,nmethod+barrier=trace -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+DeoptimizeNMethodBarriersALot -XX:-Inline - * -XX:+ShenandoahAllocFailureALot - * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 - * - * @run main/othervm/timeout=200 -Xlog:gc*=info,nmethod+barrier=trace -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+DeoptimizeNMethodBarriersALot -XX:-Inline - * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 - */ - -/* - * @test id=iu-deopt-nmethod - * @key stress - * @library / - * @requires vm.gc.Shenandoah - * @requires vm.flavor == "server" & !vm.emulatedClient & vm.opt.ClassUnloading != false - * @summary Stress Shenandoah GC with nmethod barrier forced deoptimization enabled. - * - * @run main/othervm/timeout=200 -Xlog:gc*=info,nmethod+barrier=trace -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+DeoptimizeNMethodBarriersALot -XX:-Inline - * -XX:+ShenandoahVerify - * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 - * - * @run main/othervm/timeout=200 -Xlog:gc*=info,nmethod+barrier=trace -Xmx1g -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+DeoptimizeNMethodBarriersALot -XX:-Inline - * gc.stress.gcbasher.TestGCBasherWithShenandoah 120000 - */ - - public class TestGCBasherWithShenandoah { public static void main(String[] args) throws IOException { TestGCBasher.main(args); diff --git a/test/hotspot/jtreg/gc/stress/gcold/TestGCOldWithShenandoah.java b/test/hotspot/jtreg/gc/stress/gcold/TestGCOldWithShenandoah.java index f1e34bb1b0b..7b9794ec4db 100644 --- a/test/hotspot/jtreg/gc/stress/gcold/TestGCOldWithShenandoah.java +++ b/test/hotspot/jtreg/gc/stress/gcold/TestGCOldWithShenandoah.java @@ -102,45 +102,6 @@ * gc.stress.gcold.TestGCOld 50 1 20 10 10000 */ -/* - * @test id=iu-aggressive - * @key stress randomness - * @library / /test/lib - * @requires vm.gc.Shenandoah - * @summary Stress the GC by trying to make old objects more likely to be garbage than young objects. - * - * @run main/othervm -Xmx384M -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahOOMDuringEvacALot - * gc.stress.gcold.TestGCOld 50 1 20 10 10000 - * - * @run main/othervm -Xmx384M -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * -XX:+ShenandoahAllocFailureALot - * gc.stress.gcold.TestGCOld 50 1 20 10 10000 - * - * @run main/othervm -Xmx384M -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive - * gc.stress.gcold.TestGCOld 50 1 20 10 10000 - */ - -/* - * @test id=iu - * @key stress randomness - * @library / /test/lib - * @requires vm.gc.Shenandoah - * @summary Stress the GC by trying to make old objects more likely to be garbage than young objects. - * - * @run main/othervm/timeout=600 -Xmx384M -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify - * gc.stress.gcold.TestGCOld 50 1 20 10 10000 - * - * @run main/othervm -Xmx384M -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * gc.stress.gcold.TestGCOld 50 1 20 10 10000 - */ - public class TestGCOldWithShenandoah { public static void main(String[] args) { diff --git a/test/hotspot/jtreg/gc/stress/systemgc/TestSystemGCWithShenandoah.java b/test/hotspot/jtreg/gc/stress/systemgc/TestSystemGCWithShenandoah.java index 1b12e22b62f..f1b743bfc15 100644 --- a/test/hotspot/jtreg/gc/stress/systemgc/TestSystemGCWithShenandoah.java +++ b/test/hotspot/jtreg/gc/stress/systemgc/TestSystemGCWithShenandoah.java @@ -40,20 +40,6 @@ * -XX:+UseShenandoahGC * gc.stress.systemgc.TestSystemGCWithShenandoah 270 */ - -/* - * @test id=iu - * @key stress - * @library / - * @requires vm.gc.Shenandoah - * @summary Stress the Shenandoah GC full GC by allocating objects of different lifetimes concurrently with System.gc(). - * - * @run main/othervm/timeout=300 -Xlog:gc*=info -Xmx512m -XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions - * -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu - * -XX:+ShenandoahVerify - * gc.stress.systemgc.TestSystemGCWithShenandoah 270 - * - */ public class TestSystemGCWithShenandoah { public static void main(String[] args) throws Exception { TestSystemGC.main(args); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/jcmd/JCmdTestDumpBase.java b/test/hotspot/jtreg/runtime/cds/appcds/jcmd/JCmdTestDumpBase.java index ad7b8e97b81..eedefaa1ba1 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/jcmd/JCmdTestDumpBase.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/jcmd/JCmdTestDumpBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, 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 @@ -190,6 +190,10 @@ protected static OutputAnalyzer test(String fileName, long pid, PidJcmdExecutor cmdExecutor = new PidJcmdExecutor(String.valueOf(pid)); OutputAnalyzer output = cmdExecutor.execute(jcmd, true/*silent*/); + if (archiveFileName.contains("%p")) { + archiveFileName = archiveFileName.replace("%p", "%d").formatted(pid); + } + if (expectOK) { output.shouldHaveExitValue(0); checkFileExistence(archiveFileName, true); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/jcmd/JCmdTestStaticDump.java b/test/hotspot/jtreg/runtime/cds/appcds/jcmd/JCmdTestStaticDump.java index a95eaf672c1..08ccf9b7f2a 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/jcmd/JCmdTestStaticDump.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/jcmd/JCmdTestStaticDump.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, 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 @@ -87,6 +87,13 @@ static void test() throws Exception { } app.stopApp(); + // Test static dump with file name containing %p + print2ln(test_count++ + " Test static dump with given file name containing %p."); + app = createLingeredApp("-cp", allJars); + pid = app.getPid(); + test("%p.jsa", pid, noBoot, EXPECT_PASS, STATIC_MESSAGES); + app.stopApp(); + // Test static dump with flags with which dumping should fail // This test will result classes.jsa in default server dir if -XX:SharedArchiveFile= not set. print2ln(test_count++ + " Test static dump with flags with which dumping should fail."); diff --git a/test/hotspot/jtreg/serviceability/ParserTest.java b/test/hotspot/jtreg/serviceability/ParserTest.java index 70d666d2ed0..7c304af243c 100644 --- a/test/hotspot/jtreg/serviceability/ParserTest.java +++ b/test/hotspot/jtreg/serviceability/ParserTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, 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 @@ -33,6 +33,7 @@ import java.math.BigInteger; +import jdk.test.lib.process.ProcessTools; import jdk.test.whitebox.parser.DiagnosticCommand; import jdk.test.whitebox.parser.DiagnosticCommand.DiagnosticArgumentType; import jdk.test.whitebox.WhiteBox; @@ -49,6 +50,7 @@ public ParserTest() throws Exception { testQuotes(); testMemorySize(); testSingleLetterArg(); + testFileName(); } public static void main(String... args) throws Exception { @@ -159,6 +161,33 @@ public void testSingleLetterArg() throws Exception { parse("value", "v", "flag v", ' ', args); } + public void testFileName() throws Exception { + // --- Testing options + long pid = ProcessTools.getProcessId(); + + // Test pid gets injected into %p + String name = "name"; + DiagnosticCommand arg = new DiagnosticCommand(name, + "desc", DiagnosticArgumentType.FILE, + false, null); + DiagnosticCommand[] args = {arg}; + parse(name, "file%d.txt".formatted(pid), name + "=file%p.txt", args); + + // Test custom file name with no %p + parse(name, "myFile.txt", name + "=myFile.txt", args); + + // --- Testing arguments + + // Test pid gets injected into %p + arg = new DiagnosticCommand(name, "desc", DiagnosticArgumentType.FILE, true, + false, null); + args = new DiagnosticCommand[]{arg}; + parse(name, "file%d.txt".formatted(pid), "file%p.txt", args); + + // Test custom file name with no %p + parse(name, "myFile.txt", "myFile.txt", args); + } + public void testMemorySize() throws Exception { String name = "name"; String defaultValue = "1024"; diff --git a/test/hotspot/jtreg/serviceability/dcmd/compiler/CompilerMemoryStatisticTest.java b/test/hotspot/jtreg/serviceability/dcmd/compiler/CompilerMemoryStatisticTest.java index 7d4a38db47b..02bc77263d8 100644 --- a/test/hotspot/jtreg/serviceability/dcmd/compiler/CompilerMemoryStatisticTest.java +++ b/test/hotspot/jtreg/serviceability/dcmd/compiler/CompilerMemoryStatisticTest.java @@ -47,9 +47,9 @@ public static void main(String args[]) throws Exception { out.shouldHaveExitValue(0); // Looks like this: - // total NA RA result #nodes time type #rc thread method - // 211488 66440 77624 ok 13 0.057 c2 2 0x00007fb49428db70 compiler/print/CompileCommandPrintMemStat$TestMain::method1(()V) + // total Others RA HA NA result #nodes limit time type #rc thread method + // 1898600 853176 750872 0 294552 ok 934 - 1.501 c2 1 0x00007f4ec00d3330 java/lang/String::replace((CC)Ljava/lang/String;) out.shouldMatch("total.*method"); - out.shouldMatch("\\d+ +\\d+ +\\d+ +\\S+ +\\d+.*java.*\\(.*\\)"); + out.shouldMatch("\\d+ +(\\d+ +){4}\\S+ +\\d+.*java.*\\(.*\\)"); } } diff --git a/test/hotspot/jtreg/serviceability/jvmti/GetOwnedMonitorInfo/GetOwnedMonitorInfoTest.java b/test/hotspot/jtreg/serviceability/jvmti/GetOwnedMonitorInfo/GetOwnedMonitorInfoTest.java index 80ca790a11d..420f1cedaca 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/GetOwnedMonitorInfo/GetOwnedMonitorInfoTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/GetOwnedMonitorInfo/GetOwnedMonitorInfoTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2024, 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 @@ -24,7 +24,7 @@ /** * @test - * @bug 8185164 8320515 + * @bug 8185164 8320515 8334085 * @summary Checks that a contended monitor does not show up in the list of owned monitors. * 8320515 piggy-backs on this test and injects an owned monitor with a dead object, and checks that that monitor isn't exposed to GetOwnedMonitorInfo. @@ -86,6 +86,9 @@ public static void runTest(boolean isVirtual, boolean jni) throws Exception { System.out.println("Thread doing JNI call: " + Thread.currentThread().getName()); + // Extra unmount helps to reproduce 8334085. + // Two sub-sequential thaws are needed in that scenario. + Thread.yield(); jniMonitorEnterAndLetObjectDie(); } diff --git a/test/hotspot/jtreg/serviceability/jvmti/thread/GetFrameCount/framecnt01/framecnt01.java b/test/hotspot/jtreg/serviceability/jvmti/thread/GetFrameCount/framecnt01/framecnt01.java index 8d757283a30..8a7177ec25d 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/thread/GetFrameCount/framecnt01/framecnt01.java +++ b/test/hotspot/jtreg/serviceability/jvmti/thread/GetFrameCount/framecnt01/framecnt01.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2024, 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 @@ -79,7 +79,7 @@ public static void main(String args[]) throws Exception { } // this is too fragile, implementation can change at any time. - checkFrames(vThread1, false, 14); + checkFrames(vThread1, false, 13); LockSupport.unpark(vThread1); vThread1.join(); diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/GetThreadState/GetThreadStateTest.java b/test/hotspot/jtreg/serviceability/jvmti/vthread/GetThreadState/GetThreadStateTest.java index 4ee1d1a0bfc..cc8f5638c76 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/GetThreadState/GetThreadStateTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/GetThreadState/GetThreadStateTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2024, 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 @@ -25,24 +25,29 @@ * @test id=default * @bug 8312498 * @summary Basic test for JVMTI GetThreadState with virtual threads + * @modules java.base/java.lang:+open * @library /test/lib - * @run junit/othervm/native GetThreadStateTest + * @run junit/othervm/native --enable-native-access=ALL-UNNAMED GetThreadStateTest */ /* * @test id=no-vmcontinuations * @requires vm.continuations + * @modules java.base/java.lang:+open * @library /test/lib - * @run junit/othervm/native -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations GetThreadStateTest + * @run junit/othervm/native -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations --enable-native-access=ALL-UNNAMED GetThreadStateTest */ import java.util.StringJoiner; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.LockSupport; +import jdk.test.lib.thread.VThreadRunner; import jdk.test.lib.thread.VThreadPinner; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import static org.junit.jupiter.api.Assertions.*; class GetThreadStateTest { @@ -51,6 +56,11 @@ class GetThreadStateTest { static void setup() { System.loadLibrary("GetThreadStateTest"); init(); + + // need >=2 carriers for testing pinning when main thread is a virtual thread + if (Thread.currentThread().isVirtual()) { + VThreadRunner.ensureParallelism(2); + } } /** @@ -105,21 +115,29 @@ void testRunnable() throws Exception { } /** - * Test state of thread waiting to enter a monitor. + * Test state of thread waiting to enter a monitor when pinned and not pinned. */ - @Test - void testMonitorEnter() throws Exception { - var started = new AtomicBoolean(); + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testMonitorEnter(boolean pinned) throws Exception { + var ready = new AtomicBoolean(); Object lock = new Object(); var thread = Thread.ofVirtual().unstarted(() -> { - started.set(true); - synchronized (lock) { } + if (pinned) { + VThreadPinner.runPinned(() -> { + ready.set(true); + synchronized (lock) { } + }); + } else { + ready.set(true); + synchronized (lock) { } + } }); try { synchronized (lock) { // start thread and wait for it to start execution thread.start(); - awaitTrue(started); + awaitTrue(ready); // thread should block on monitor enter int expected = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER; @@ -135,23 +153,31 @@ void testMonitorEnter() throws Exception { } /** - * Test state of thread waiting in Object.wait(). + * Test state of thread waiting in Object.wait() when pinned and not pinned. */ - @Test - void testObjectWait() throws Exception { - var started = new AtomicBoolean(); + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testObjectWait(boolean pinned) throws Exception { + var ready = new AtomicBoolean(); Object lock = new Object(); var thread = Thread.ofVirtual().start(() -> { synchronized (lock) { - started.set(true); try { - lock.wait(); + if (pinned) { + VThreadPinner.runPinned(() -> { + ready.set(true); + lock.wait(); + }); + } else { + ready.set(true); + lock.wait(); + } } catch (InterruptedException e) { } } }); try { // wait for thread to start execution - awaitTrue(started); + awaitTrue(ready); // thread should wait int expected = JVMTI_THREAD_STATE_ALIVE | @@ -177,23 +203,33 @@ void testObjectWait() throws Exception { } /** - * Test state of thread waiting in Object.wait(millis). + * Test state of thread waiting in Object.wait(millis) when pinned and not pinned. */ - @Test - void testObjectWaitMillis() throws Exception { - var started = new AtomicBoolean(); + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testObjectWaitMillis(boolean pinned) throws Exception { + var ready = new AtomicBoolean(); Object lock = new Object(); var thread = Thread.ofVirtual().start(() -> { synchronized (lock) { - started.set(true); - try { - lock.wait(Long.MAX_VALUE); - } catch (InterruptedException e) { } + synchronized (lock) { + try { + if (pinned) { + VThreadPinner.runPinned(() -> { + ready.set(true); + lock.wait(Long.MAX_VALUE); + }); + } else { + ready.set(true); + lock.wait(Long.MAX_VALUE); + } + } catch (InterruptedException e) { } + } } }); try { // wait for thread to start execution - awaitTrue(started); + awaitTrue(ready); // thread should wait int expected = JVMTI_THREAD_STATE_ALIVE | @@ -219,83 +255,31 @@ void testObjectWaitMillis() throws Exception { } /** - * Test state of thread parked with LockSupport.park. + * Test state of thread parked with LockSupport.park when pinned and not pinned. */ - @Test - void testPark() throws Exception { - var started = new AtomicBoolean(); + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testPark(boolean pinned) throws Exception { + var ready = new AtomicBoolean(); var done = new AtomicBoolean(); var thread = Thread.ofVirtual().start(() -> { - started.set(true); - while (!done.get()) { - LockSupport.park(); - } - }); - try { - // wait for thread to start execution - awaitTrue(started); - - // thread should park - int expected = JVMTI_THREAD_STATE_ALIVE | - JVMTI_THREAD_STATE_WAITING | - JVMTI_THREAD_STATE_WAITING_INDEFINITELY | - JVMTI_THREAD_STATE_PARKED; - await(thread, expected); - } finally { - done.set(true); - LockSupport.unpark(thread); - thread.join(); - } - } - - /** - * Test state of thread parked with LockSupport.parkNanos. - */ - @Test - void testParkNanos() throws Exception { - var started = new AtomicBoolean(); - var done = new AtomicBoolean(); - var thread = Thread.ofVirtual().start(() -> { - started.set(true); - while (!done.get()) { - LockSupport.parkNanos(Long.MAX_VALUE); - } - }); - try { - // wait for thread to start execution - awaitTrue(started); - - // thread should park - int expected = JVMTI_THREAD_STATE_ALIVE | - JVMTI_THREAD_STATE_WAITING | - JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT | - JVMTI_THREAD_STATE_PARKED; - await(thread, expected); - } finally { - done.set(true); - LockSupport.unpark(thread); - thread.join(); - } - } - - /** - * Test state of thread parked with LockSupport.park while holding a monitor. - */ - @Test - void testParkWhenPinned() throws Exception { - var started = new AtomicBoolean(); - var done = new AtomicBoolean(); - var thread = Thread.ofVirtual().start(() -> { - VThreadPinner.runPinned(() -> { - started.set(true); + if (pinned) { + VThreadPinner.runPinned(() -> { + ready.set(true); + while (!done.get()) { + LockSupport.park(); + } + }); + } else { + ready.set(true); while (!done.get()) { LockSupport.park(); } - }); + } }); try { // wait for thread to start execution - awaitTrue(started); + awaitTrue(ready); // thread should park int expected = JVMTI_THREAD_STATE_ALIVE | @@ -311,23 +295,31 @@ void testParkWhenPinned() throws Exception { } /** - * Test state of thread parked with LockSupport.parkNanos while holding a monitor. + * Test state of thread parked with LockSupport.parkNanos when pinned and not pinned. */ - @Test - void testParkNanosWhenPinned() throws Exception { - var started = new AtomicBoolean(); + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testParkNanos(boolean pinned) throws Exception { + var ready = new AtomicBoolean(); var done = new AtomicBoolean(); var thread = Thread.ofVirtual().start(() -> { - VThreadPinner.runPinned(() -> { - started.set(true); + if (pinned) { + VThreadPinner.runPinned(() -> { + ready.set(true); + while (!done.get()) { + LockSupport.parkNanos(Long.MAX_VALUE); + } + }); + } else { + ready.set(true); while (!done.get()) { LockSupport.parkNanos(Long.MAX_VALUE); } - }); + } }); try { // wait for thread to start execution - awaitTrue(started); + awaitTrue(ready); // thread should park int expected = JVMTI_THREAD_STATE_ALIVE | diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/VThreadEventTest.java b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/VThreadEventTest.java index 61b94a4484b..845e9adba01 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/VThreadEventTest.java +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/VThreadEventTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2024, 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 @@ -28,47 +28,22 @@ * @requires vm.continuations * @requires vm.jvmti * @requires vm.compMode != "Xcomp" + * @modules java.base/java.lang:+open + * @library /test/lib * @run main/othervm/native - * -Djdk.virtualThreadScheduler.parallelism=9 * -Djdk.attach.allowAttachSelf=true -XX:+EnableDynamicAgentLoading VThreadEventTest attach */ import com.sun.tools.attach.VirtualMachine; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.LockSupport; import java.util.List; import java.util.ArrayList; - -/* - * The test uses custom implementation of the CountDownLatch class. - * The reason is we want the state of tested thread to be predictable. - * With java.util.concurrent.CountDownLatch it is not clear what thread state is expected. - */ -class CountDownLatch { - private int count = 0; - - CountDownLatch(int count) { - this.count = count; - } - - public synchronized void countDown() { - count--; - notify(); - } - - public synchronized void await() throws InterruptedException { - while (count > 0) { - wait(1); - } - } -} +import jdk.test.lib.thread.VThreadRunner; public class VThreadEventTest { - static final int TCNT1 = 10; - static final int TCNT2 = 4; - static final int TCNT3 = 4; - static final int THREAD_CNT = TCNT1 + TCNT2 + TCNT3; + static final int PARKED_THREAD_COUNT = 4; + static final int SPINNING_THREAD_COUNT = 4; private static void log(String msg) { System.out.println(msg); } @@ -77,128 +52,96 @@ public class VThreadEventTest { private static native int threadUnmountCount(); private static volatile boolean attached; - private static boolean failed; - private static List test1Threads = new ArrayList(TCNT1); - - private static CountDownLatch ready0 = new CountDownLatch(THREAD_CNT); - private static CountDownLatch ready1 = new CountDownLatch(TCNT1); - private static CountDownLatch ready2 = new CountDownLatch(THREAD_CNT); - private static CountDownLatch mready = new CountDownLatch(1); - - private static void await(CountDownLatch dumpedLatch) { - try { - dumpedLatch.await(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } + + // called by agent when it is initialized and has enabled events + static void agentStarted() { + attached = true; } - // The test1 vthreads are kept unmounted until interrupted after agent attach. - static final Runnable test1 = () -> { - synchronized (test1Threads) { - test1Threads.add(Thread.currentThread()); + public static void main(String[] args) throws Exception { + if (Thread.currentThread().isVirtual()) { + System.out.println("Skipping test as current thread is a virtual thread"); + return; } - log("test1 vthread started"); - ready0.countDown(); - await(mready); - ready1.countDown(); // to guaranty state is not State.WAITING after await(mready) - try { - Thread.sleep(20000); // big timeout to keep unmounted until interrupted - } catch (InterruptedException ex) { - // it is expected, ignore + VThreadRunner.ensureParallelism(SPINNING_THREAD_COUNT+1); + + // start threads that park (unmount) + var threads1 = new ArrayList(); + for (int i = 0; i < PARKED_THREAD_COUNT; i++) { + var started = new AtomicBoolean(); + var thread = Thread.startVirtualThread(() -> { + started.set(true); + LockSupport.park(); + }); + + // wait for thread to start execution + park + while (!started.get()) { + Thread.sleep(10); + } + await(thread, Thread.State.WAITING); + threads1.add(thread); } - ready2.countDown(); - }; - - // The test2 vthreads are kept mounted until agent attach. - static final Runnable test2 = () -> { - log("test2 vthread started"); - ready0.countDown(); - await(mready); - while (!attached) { - // keep mounted + + // start threads that spin (stay mounted) + var threads2 = new ArrayList(); + for (int i = 0; i < SPINNING_THREAD_COUNT; i++) { + var started = new AtomicBoolean(); + var thread = Thread.startVirtualThread(() -> { + started.set(true); + while (!attached) { + Thread.onSpinWait(); + } + }); + + // wait for thread to start execution + while (!started.get()) { + Thread.sleep(10); + } + threads2.add(thread); } - ready2.countDown(); - }; - - // The test3 vthreads are kept mounted until agent attach. - static final Runnable test3 = () -> { - log("test3 vthread started"); - ready0.countDown(); - await(mready); + + // attach to the current VM + VirtualMachine vm = VirtualMachine.attach(String.valueOf(ProcessHandle.current().pid())); + vm.loadAgentLibrary("VThreadEventTest"); + + // wait for agent to start while (!attached) { - // keep mounted + Thread.sleep(10); } - LockSupport.parkNanos(10_000_000L); // will cause extra mount and unmount - ready2.countDown(); - }; - public static void main(String[] args) throws Exception { - if (Runtime.getRuntime().availableProcessors() < 8) { - log("WARNING: test expects at least 8 processors."); + // unpark the threads that were parked + for (Thread thread : threads1) { + LockSupport.unpark(thread); } - try (ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor()) { - for (int i = 0; i < TCNT1; i++) { - executorService.execute(test1); - } - for (int i = 0; i < TCNT2; i++) { - executorService.execute(test2); - } - for (int i = 0; i < TCNT3; i++) { - executorService.execute(test3); - } - await(ready0); - mready.countDown(); - await(ready1); // to guarantee state is not State.TIMED_WAITING after await(mready) in test1() - // wait for test1 threads to reach TIMED_WAITING state in sleep() - for (Thread t : test1Threads) { - Thread.State state = t.getState(); - log("DBG: state: " + state); - while (state != Thread.State.TIMED_WAITING) { - Thread.sleep(10); - state = t.getState(); - log("DBG: state: " + state); - } - } - - VirtualMachine vm = VirtualMachine.attach(String.valueOf(ProcessHandle.current().pid())); - vm.loadAgentLibrary("VThreadEventTest"); - Thread.sleep(200); // to allow the agent to get ready - attached = true; - for (Thread t : test1Threads) { - t.interrupt(); - } - ready2.await(); + // wait for all threads to terminate + for (Thread thread : threads1) { + thread.join(); } - // wait until all VirtualThreadEnd events have been sent - for (int sleepNo = 1; threadEndCount() < THREAD_CNT; sleepNo++) { - Thread.sleep(100); - if (sleepNo % 100 == 0) { // 10 sec period of waiting - log("main: waited seconds: " + sleepNo/10); - } + for (Thread thread : threads2) { + thread.join(); } + int threadEndCnt = threadEndCount(); int threadMountCnt = threadMountCount(); int threadUnmountCnt = threadUnmountCount(); - int threadEndExp = THREAD_CNT; - int threadMountExp = THREAD_CNT - TCNT2; - int threadUnmountExp = THREAD_CNT + TCNT3; - log("ThreadEnd cnt: " + threadEndCnt + " (expected: " + threadEndExp + ")"); - log("ThreadMount cnt: " + threadMountCnt + " (expected: " + threadMountExp + ")"); - log("ThreadUnmount cnt: " + threadUnmountCnt + " (expected: " + threadUnmountExp + ")"); + int threadCount = PARKED_THREAD_COUNT + SPINNING_THREAD_COUNT; + log("VirtualThreadEnd events: " + threadEndCnt + ", expected: " + threadCount); + log("VirtualThreadMount events: " + threadMountCnt + ", expected: " + PARKED_THREAD_COUNT); + log("VirtualThreadUnmount events: " + threadUnmountCnt + ", expected: " + threadCount); - if (threadEndCnt != threadEndExp) { - log("FAILED: unexpected count of ThreadEnd events"); + boolean failed = false; + if (threadEndCnt != threadCount) { + log("FAILED: unexpected count of VirtualThreadEnd events"); failed = true; } - if (threadMountCnt != threadMountExp) { - log("FAILED: unexpected count of ThreadMount events"); + if (threadMountCnt != PARKED_THREAD_COUNT) { + log("FAILED: unexpected count of VirtualThreadMount events"); failed = true; } - if (threadUnmountCnt != threadUnmountExp) { - log("FAILED: unexpected count of ThreadUnmount events"); + if (threadUnmountCnt != threadCount) { + log("FAILED: unexpected count of VirtualThreadUnmount events"); failed = true; } if (failed) { @@ -206,5 +149,14 @@ public static void main(String[] args) throws Exception { } } + private static void await(Thread thread, Thread.State expectedState) throws InterruptedException { + Thread.State state = thread.getState(); + while (state != expectedState) { + assert state != Thread.State.TERMINATED : "Thread has terminated"; + Thread.sleep(10); + state = thread.getState(); + } + } + } diff --git a/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/libVThreadEventTest.cpp b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/libVThreadEventTest.cpp index afd06e66b32..c97a8ef3471 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/libVThreadEventTest.cpp +++ b/test/hotspot/jtreg/serviceability/jvmti/vthread/VThreadEventTest/libVThreadEventTest.cpp @@ -65,6 +65,11 @@ Agent_OnAttach(JavaVM *vm, char *options, void *reserved) { jvmtiEventCallbacks callbacks; jvmtiCapabilities caps; jvmtiError err; + JNIEnv *env; + jsize nVMs; + jint res; + jclass clazz; + jmethodID mid; LOG("Agent_OnAttach started\n"); if (vm->GetEnv(reinterpret_cast(&jvmti), JVMTI_VERSION) != JNI_OK || !jvmti) { @@ -97,6 +102,41 @@ Agent_OnAttach(JavaVM *vm, char *options, void *reserved) { check_jvmti_error(err, "SetEventNotificationMode for VirtualThreadUnmount"); LOG("vthread events enabled\n"); + + // call VThreadEventTest.agentStarted to notify test that agent has started + + res = JNI_GetCreatedJavaVMs(&vm, 1, &nVMs); + if (res != JNI_OK) { + LOG("JNI_GetCreatedJavaVMs failed: %d\n", res); + return JNI_ERR; + } + + res = vm->GetEnv((void **) &env, JNI_VERSION_21); + if (res != JNI_OK) { + LOG("GetEnv failed: %d\n", res); + return JNI_ERR; + } + + clazz = env->FindClass("VThreadEventTest"); + if (clazz == NULL) { + LOG("FindClass failed\n"); + return JNI_ERR; + } + + mid = env->GetStaticMethodID(clazz, "agentStarted", "()V"); + if (mid == NULL) { + LOG("GetStaticMethodID failed\n"); + return JNI_ERR; + } + + env->CallStaticVoidMethod(clazz, mid); + if (env->ExceptionOccurred()) { + LOG("CallStaticVoidMethod failed\n"); + return JNI_ERR; + } + + LOG("Agent_OnAttach done\n"); + return JVMTI_ERROR_NONE; } diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt index 05e42b49330..8e627e27d7a 100644 --- a/test/jdk/ProblemList.txt +++ b/test/jdk/ProblemList.txt @@ -606,7 +606,6 @@ sun/security/smartcardio/TestExclusive.java 8039280 generic- sun/security/smartcardio/TestMultiplePresent.java 8039280 generic-all sun/security/smartcardio/TestPresent.java 8039280 generic-all sun/security/smartcardio/TestTransmit.java 8039280 generic-all -com/sun/crypto/provider/Cipher/DES/PerformanceTest.java 8039280 generic-all com/sun/security/auth/callback/TextCallbackHandler/Password.java 8039280 generic-all com/sun/security/sasl/gsskerb/AuthOnly.java 8039280 generic-all com/sun/security/sasl/gsskerb/ConfSecurityLayer.java 8039280 generic-all diff --git a/test/jdk/TEST.groups b/test/jdk/TEST.groups index 6a5be3736e8..55ae0e9c67b 100644 --- a/test/jdk/TEST.groups +++ b/test/jdk/TEST.groups @@ -621,7 +621,6 @@ jdk_core_manual_no_input = \ jdk_security_manual_no_input = \ :jdk_security_infra \ - com/sun/crypto/provider/Cipher/DES/PerformanceTest.java \ com/sun/crypto/provider/Cipher/AEAD/GCMIncrementByte4.java \ com/sun/crypto/provider/Cipher/AEAD/GCMIncrementDirect4.java \ com/sun/security/auth/callback/TextCallbackHandler/Password.java \ diff --git a/test/jdk/com/sun/crypto/provider/Cipher/DES/PerformanceTest.java b/test/jdk/com/sun/crypto/provider/Cipher/DES/PerformanceTest.java index 698acd083b4..33167cafbd9 100644 --- a/test/jdk/com/sun/crypto/provider/Cipher/DES/PerformanceTest.java +++ b/test/jdk/com/sun/crypto/provider/Cipher/DES/PerformanceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2024, 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 @@ -26,11 +26,8 @@ * @bug 0000000 * @summary This test checks performance of various ciphers. * @author Jan Luehe - * @run main/manual PerformanceTest */ -import java.security.*; import java.security.spec.*; -import java.io.*; import javax.crypto.*; import javax.crypto.spec.*; @@ -178,14 +175,16 @@ public void runTest(byte[] data, int count) throws Exception { long start, end; cipher.init(Cipher.ENCRYPT_MODE, cipherKey, params); - start = System.currentTimeMillis(); + start = getTimeInMicroseconds(); for (int i=0; i 20) { - throw new IllegalStateException("SegmentAccessor::doAccess method not being compiled"); - } Thread.sleep(1000); - retries++; } } } diff --git a/test/jdk/java/lang/Process/WaitForDuration.java b/test/jdk/java/lang/Process/WaitForDuration.java index d9172b305a0..76c69e36d13 100644 --- a/test/jdk/java/lang/Process/WaitForDuration.java +++ b/test/jdk/java/lang/Process/WaitForDuration.java @@ -55,9 +55,14 @@ static Stream durations() { @MethodSource("durations") void testEdgeDurations(Duration d, int sleepMillis, boolean expected) throws IOException, InterruptedException { - var pb = ProcessTools.createTestJavaProcessBuilder( - WaitForDuration.class.getSimpleName(), Integer.toString(sleepMillis)); - assertEquals(expected, pb.start().waitFor(d)); + var child = ProcessTools.createTestJavaProcessBuilder( + WaitForDuration.class.getSimpleName(), Integer.toString(sleepMillis)) + .start(); + try { + assertEquals(expected, child.waitFor(d)); + } finally { + child.destroy(); + } } @Test diff --git a/test/jdk/java/lang/Thread/virtual/CarrierThreadInfo.java b/test/jdk/java/lang/Thread/virtual/CarrierThreadInfo.java new file mode 100644 index 00000000000..75ef0376a7e --- /dev/null +++ b/test/jdk/java/lang/Thread/virtual/CarrierThreadInfo.java @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2022, 2024, 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 id=default + * @bug 8284161 8286788 + * @summary Test java.lang.management.ThreadInfo contains expected information for carrier threads + * @requires vm.continuations + * @modules java.base/java.lang:+open + * @library /test/lib + * @run junit CarrierThreadInfo + */ + +/** + * @test id=LM_LIGHTWEIGHT + * @requires vm.continuations + * @modules java.base/java.lang:+open + * @library /test/lib + * @run junit/othervm -XX:LockingMode=2 CarrierThreadInfo + */ + +/** + * @test id=LM_LEGACY + * @requires vm.continuations + * @modules java.base/java.lang:+open + * @library /test/lib + * @run junit/othervm -XX:LockingMode=1 CarrierThreadInfo + */ + +/** + * @test id=LM_MONITOR + * @requires vm.continuations + * @modules java.base/java.lang:+open + * @library /test/lib + * @run junit/othervm -XX:LockingMode=0 CarrierThreadInfo + */ + +import java.lang.management.LockInfo; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.util.Arrays; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import jdk.test.lib.thread.VThreadScheduler; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +class CarrierThreadInfo { + + /** + * Test that ThreadInfo.getLockedMonitors returns information about a lock held by + * a carrier thread. + */ + @Test + void testCarrierThreadHoldsLock() throws Exception { + Object lock = new Object(); + ThreadFactory factory = task -> Thread.ofPlatform().unstarted(() -> { + synchronized (lock) { + task.run(); + } + }); + + try (var scheduler = new CustomScheduler(factory)) { + var started = new AtomicBoolean(); + var done = new AtomicBoolean(); + Thread vthread = scheduler.forkVirtualThread(() -> { + started.set(true); + while (!done.get()) { + Thread.onSpinWait(); + } + }); + try { + awaitTrue(started); + + // carrier threads holds the lock + long carrierId = scheduler.carrier().threadId(); + ThreadInfo threadInfo = ManagementFactory.getPlatformMXBean(ThreadMXBean.class) + .getThreadInfo(new long[] { carrierId }, true, true)[0]; + boolean holdsLock = Arrays.stream(threadInfo.getLockedMonitors()) + .anyMatch(mi -> mi.getIdentityHashCode() == System.identityHashCode(lock)); + assertTrue(holdsLock, "Carrier should hold lock"); + + } finally { + done.set(true); + } + } + } + + /** + * Test that ThreadInfo.getLockedMonitors does not return information about a lock + * held by mounted virtual thread. + */ + @Test + void testVirtualThreadHoldsLock() throws Exception { + ThreadFactory factory = Executors.defaultThreadFactory(); + try (var scheduler = new CustomScheduler(factory)) { + var started = new AtomicBoolean(); + var lock = new Object(); + var done = new AtomicBoolean(); + Thread vthread = scheduler.forkVirtualThread(() -> { + started.set(true); + while (!done.get()) { + Thread.onSpinWait(); + } + }); + try { + awaitTrue(started); + + // carrier threads does not hold lock + long carrierId = scheduler.carrier().threadId(); + ThreadInfo threadInfo = ManagementFactory.getPlatformMXBean(ThreadMXBean.class) + .getThreadInfo(new long[] { carrierId }, true, true)[0]; + boolean holdsLock = Arrays.stream(threadInfo.getLockedMonitors()) + .anyMatch(mi -> mi.getIdentityHashCode() == System.identityHashCode(lock)); + assertFalse(holdsLock, "Carrier should not hold lock"); + + } finally { + done.set(true); + } + } + } + + /** + * Test that ThreadInfo.getLockOwnerId and getLockInfo return information about a + * synthetic lock that make it appear that the carrier is blocking waiting on the + * virtual thread. + */ + @Test + void testCarrierThreadWaits() throws Exception { + ThreadFactory factory = Executors.defaultThreadFactory(); + try (var scheduler = new CustomScheduler(factory)) { + var started = new AtomicBoolean(); + var done = new AtomicBoolean(); + Thread vthread = scheduler.forkVirtualThread(() -> { + started.set(true); + while (!done.get()) { + Thread.onSpinWait(); + } + }); + try { + awaitTrue(started); + + long carrierId = scheduler.carrier().threadId(); + long vthreadId = vthread.threadId(); + + ThreadInfo threadInfo = ManagementFactory.getThreadMXBean().getThreadInfo(carrierId); + assertNotNull(threadInfo); + + // carrier should be blocked waiting for lock owned by virtual thread + assertEquals(vthreadId, threadInfo.getLockOwnerId()); + + // carrier thread should be on blocked waiting on virtual thread + LockInfo lockInfo = threadInfo.getLockInfo(); + assertNotNull(lockInfo); + assertEquals(vthread.getClass().getName(), lockInfo.getClassName()); + assertEquals(System.identityHashCode(vthread), lockInfo.getIdentityHashCode()); + + } finally { + done.set(true); + } + } + } + + /** + * Custom scheduler with a single carrier thread. + */ + private static class CustomScheduler implements AutoCloseable { + private final ExecutorService pool; + private final Executor scheduler; + private final AtomicReference carrierRef = new AtomicReference<>(); + + CustomScheduler(ThreadFactory factory) { + pool = Executors.newSingleThreadExecutor(factory); + scheduler = task -> { + pool.submit(() -> { + carrierRef.set(Thread.currentThread()); + try { + task.run(); + } finally { + carrierRef.set(null); + } + }); + }; + } + + /** + * Returns the carrier thread if a virtual thread is mounted. + */ + Thread carrier() throws InterruptedException { + return carrierRef.get(); + } + + /** + * Starts a virtual thread to execute the give task. + */ + Thread forkVirtualThread(Runnable task) { + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); + Thread thread = factory.newThread(task); + thread.start(); + return thread; + } + + @Override + public void close() { + pool.close(); + } + } + + /** + * Waits for the boolean value to become true. + */ + private static void awaitTrue(AtomicBoolean ref) throws InterruptedException { + while (!ref.get()) { + Thread.sleep(20); + } + } +} diff --git a/test/jdk/java/lang/Thread/virtual/CarrierThreadWaits.java b/test/jdk/java/lang/Thread/virtual/CarrierThreadWaits.java deleted file mode 100644 index d1d5e72204f..00000000000 --- a/test/jdk/java/lang/Thread/virtual/CarrierThreadWaits.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2022, 2023, 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 8284161 8286788 - * @summary Test that a carrier thread waits on a virtual thread - * @requires vm.continuations - * @modules java.base/java.lang:+open - * @run junit CarrierThreadWaits - */ - -/** - * @test - * @requires vm.continuations & vm.debug - * @modules java.base/java.lang:+open - * @run junit/othervm -XX:LockingMode=0 CarrierThreadWaits - */ - -import java.lang.management.LockInfo; -import java.lang.management.ManagementFactory; -import java.lang.management.ThreadInfo; -import java.util.concurrent.Executor; -import java.util.concurrent.ForkJoinPool; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; - -import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; - -class CarrierThreadWaits { - - @Test - void testCarrierThreadWaiting() throws Exception { - try (ForkJoinPool pool = new ForkJoinPool(1)) { - var carrierRef = new AtomicReference(); - var vthreadRef = new AtomicReference(); - - Executor scheduler = task -> { - pool.submit(() -> { - Thread carrier = Thread.currentThread(); - carrierRef.set(carrier); - Thread vthread = vthreadRef.get(); - - System.err.format("%s run task (%s) ...%n", carrier, vthread); - task.run(); - System.err.format("%s task done (%s)%n", carrier, vthread); - }); - }; - - // start a virtual thread that spins and remains mounted until "done" - var started = new AtomicBoolean(); - var done = new AtomicBoolean(); - Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler); - Thread vthread = builder.unstarted(() -> { - started.set(true); - while (!done.get()) { - Thread.onSpinWait(); - } - }); - vthreadRef.set(vthread); - vthread.start(); - - try { - // wait for virtual thread to start - while (!started.get()) { - Thread.sleep(10); - } - - Thread carrier = carrierRef.get(); - - long carrierId = carrier.threadId(); - long vthreadId = vthread.threadId(); - - // carrier thread should be on WAITING on virtual thread - ThreadInfo ti = ManagementFactory.getThreadMXBean().getThreadInfo(carrierId); - Thread.State state = ti.getThreadState(); - LockInfo lockInfo = ti.getLockInfo(); - assertEquals(Thread.State.WAITING, state); - assertNotNull(lockInfo); - assertEquals(vthread.getClass().getName(), lockInfo.getClassName()); - assertEquals(System.identityHashCode(vthread), lockInfo.getIdentityHashCode()); - assertEquals(vthreadId, ti.getLockOwnerId()); - } finally { - done.set(true); - } - } - } - -} diff --git a/test/jdk/java/lang/Thread/virtual/CustomScheduler.java b/test/jdk/java/lang/Thread/virtual/CustomScheduler.java index 5bde81905ec..4289add0a3a 100644 --- a/test/jdk/java/lang/Thread/virtual/CustomScheduler.java +++ b/test/jdk/java/lang/Thread/virtual/CustomScheduler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, 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 @@ -26,6 +26,7 @@ * @summary Test virtual threads using a custom scheduler * @requires vm.continuations * @modules java.base/java.lang:+open + * @library /test/lib * @run junit CustomScheduler */ @@ -35,9 +36,12 @@ import java.util.List; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.LockSupport; +import jdk.test.lib.thread.VThreadScheduler; +import jdk.test.lib.thread.VThreadRunner; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.AfterAll; @@ -65,10 +69,13 @@ static void shutdown() { */ @Test void testCustomScheduler1() throws Exception { - AtomicReference ref = new AtomicReference<>(); - ThreadBuilders.virtualThreadBuilder(scheduler1).start(() -> { - ref.set(scheduler(Thread.currentThread())); - }).join(); + var ref = new AtomicReference(); + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler1); + Thread thread = factory.newThread(() -> { + ref.set(VThreadScheduler.scheduler(Thread.currentThread())); + }); + thread.start(); + thread.join(); assertTrue(ref.get() == scheduler1); } @@ -77,17 +84,7 @@ void testCustomScheduler1() throws Exception { */ @Test void testCustomScheduler2() throws Exception { - AtomicReference ref = new AtomicReference<>(); - Thread.ofVirtual().start(() -> { - try { - ThreadBuilders.virtualThreadBuilder(scheduler1).start(() -> { - ref.set(scheduler(Thread.currentThread())); - }).join(); - } catch (Exception e) { - e.printStackTrace(); - } - }).join(); - assertTrue(ref.get() == scheduler1); + VThreadRunner.run(this::testCustomScheduler1); } /** @@ -96,16 +93,19 @@ void testCustomScheduler2() throws Exception { */ @Test void testCustomScheduler3() throws Exception { - AtomicReference ref = new AtomicReference<>(); - ThreadBuilders.virtualThreadBuilder(scheduler1).start(() -> { + var ref = new AtomicReference(); + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler1); + Thread thread = factory.newThread(() -> { try { Thread.ofVirtual().start(() -> { - ref.set(scheduler(Thread.currentThread())); + ref.set(VThreadScheduler.scheduler(Thread.currentThread())); }).join(); } catch (Exception e) { e.printStackTrace(); } - }).join(); + }); + thread.start(); + thread.join(); assertTrue(ref.get() == scheduler1); } @@ -115,16 +115,22 @@ void testCustomScheduler3() throws Exception { */ @Test void testCustomScheduler4() throws Exception { - AtomicReference ref = new AtomicReference<>(); - ThreadBuilders.virtualThreadBuilder(scheduler1).start(() -> { + var ref = new AtomicReference(); + ThreadFactory factory1 = VThreadScheduler.virtualThreadFactory(scheduler1); + ThreadFactory factory2 = VThreadScheduler.virtualThreadFactory(scheduler2); + Thread thread1 = factory1.newThread(() -> { try { - ThreadBuilders.virtualThreadBuilder(scheduler2).start(() -> { - ref.set(scheduler(Thread.currentThread())); - }).join(); + Thread thread2 = factory2.newThread(() -> { + ref.set(VThreadScheduler.scheduler(Thread.currentThread())); + }); + thread2.start(); + thread2.join(); } catch (Exception e) { e.printStackTrace(); } - }).join(); + }); + thread1.start(); + thread1.join(); assertTrue(ref.get() == scheduler2); } @@ -149,8 +155,9 @@ void testBadCarrier() { } assertTrue(exc.get() instanceof WrongThreadException); }; - - ThreadBuilders.virtualThreadBuilder(scheduler).start(LockSupport::park); + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); + Thread thread = factory.newThread(LockSupport::park); + thread.start(); } /** @@ -162,11 +169,12 @@ void testParkWithInterruptSet() { Thread carrier = Thread.currentThread(); assumeFalse(carrier.isVirtual(), "Main thread is a virtual thread"); try { - var builder = ThreadBuilders.virtualThreadBuilder(Runnable::run); - Thread vthread = builder.start(() -> { + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(Runnable::run); + Thread vthread = factory.newThread(() -> { Thread.currentThread().interrupt(); Thread.yield(); }); + vthread.start(); assertTrue(vthread.isInterrupted()); assertFalse(carrier.isInterrupted()); } finally { @@ -183,10 +191,11 @@ void testTerminateWithInterruptSet() { Thread carrier = Thread.currentThread(); assumeFalse(carrier.isVirtual(), "Main thread is a virtual thread"); try { - var builder = ThreadBuilders.virtualThreadBuilder(Runnable::run); - Thread vthread = builder.start(() -> { + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(Runnable::run); + Thread vthread = factory.newThread(() -> { Thread.currentThread().interrupt(); }); + vthread.start(); assertTrue(vthread.isInterrupted()); assertFalse(carrier.isInterrupted()); } finally { @@ -204,11 +213,13 @@ void testRunWithInterruptSet() throws Exception { Thread.currentThread().interrupt(); task.run(); }; + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); try { AtomicBoolean interrupted = new AtomicBoolean(); - Thread vthread = ThreadBuilders.virtualThreadBuilder(scheduler).start(() -> { + Thread vthread = factory.newThread(() -> { interrupted.set(Thread.currentThread().isInterrupted()); }); + vthread.start(); assertFalse(vthread.isInterrupted()); } finally { Thread.interrupted(); @@ -216,18 +227,60 @@ void testRunWithInterruptSet() throws Exception { } /** - * Returns the scheduler for the given virtual thread. + * Test custom scheduler throwing OOME when starting a thread. */ - private static Executor scheduler(Thread thread) { - if (!thread.isVirtual()) - throw new IllegalArgumentException("Not a virtual thread"); - try { - Field scheduler = Class.forName("java.lang.VirtualThread") - .getDeclaredField("scheduler"); - scheduler.setAccessible(true); - return (Executor) scheduler.get(thread); - } catch (Exception e) { - throw new RuntimeException(e); + @Test + void testThreadStartOOME() throws Exception { + Executor scheduler = task -> { + System.err.println("OutOfMemoryError"); + throw new OutOfMemoryError(); + }; + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); + Thread thread = factory.newThread(() -> { }); + assertThrows(OutOfMemoryError.class, thread::start); + } + + /** + * Test custom scheduler throwing OOME when unparking a thread. + */ + @Test + void testThreadUnparkOOME() throws Exception { + try (ExecutorService executor = Executors.newFixedThreadPool(1)) { + AtomicInteger counter = new AtomicInteger(); + Executor scheduler = task -> { + switch (counter.getAndIncrement()) { + case 0 -> executor.execute(task); // Thread.start + case 1, 2 -> { // unpark attempt 1+2 + System.err.println("OutOfMemoryError"); + throw new OutOfMemoryError(); + } + default -> executor.execute(task); + } + executor.execute(task); + }; + + // start thread and wait for it to park + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); + var thread = factory.newThread(LockSupport::park); + thread.start(); + await(thread, Thread.State.WAITING); + + // unpark thread, this should retry until OOME is not thrown + LockSupport.unpark(thread); + thread.join(); + } + + } + + /** + * Waits for the given thread to reach a given state. + */ + private void await(Thread thread, Thread.State expectedState) throws InterruptedException { + Thread.State state = thread.getState(); + while (state != expectedState) { + assertTrue(state != Thread.State.TERMINATED, "Thread has terminated"); + Thread.sleep(10); + state = thread.getState(); } } } diff --git a/test/jdk/java/lang/Thread/virtual/GetStackTrace.java b/test/jdk/java/lang/Thread/virtual/GetStackTrace.java deleted file mode 100644 index 86d5cf7319c..00000000000 --- a/test/jdk/java/lang/Thread/virtual/GetStackTrace.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (c) 2022, 2023, 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 - * @summary Test Thread.getStackTrace to examine the stack trace of a virtual - * thread and its carrier - * @requires vm.continuations - * @modules java.base/java.lang:+open - * @run main GetStackTrace - */ - -import java.util.Objects; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.Executor; -import java.util.concurrent.LinkedTransferQueue; -import java.util.stream.Stream; - -public class GetStackTrace { - - private static final Object LOCK = new Object(); - - public static void main(String[] args) throws Exception { - try (var scheduler = new Scheduler()) { - Thread vthread = scheduler.startVirtualThread(() -> { - synchronized (LOCK) { - try { - LOCK.wait(); - } catch (InterruptedException e) { } - } - }); - - try { - // wait for virtual thread to wait - while (vthread.getState() != Thread.State.WAITING) { - Thread.sleep(10); - } - - // bottom-most frame of virtual thread should be VirtualThread.run - System.out.println(vthread); - StackTraceElement[] vthreadStack = vthread.getStackTrace(); - Stream.of(vthreadStack).forEach(System.out::println); - assertEquals("run", vthreadStack[vthreadStack.length - 1].getMethodName()); - - System.out.println(); - - // top-most frame of carrier thread should be Continuation.run - // bottom-most frame of carrier thread should be Thread.run - var carrier = scheduler.thread(); - System.out.println(carrier); - StackTraceElement[] carrierStack = carrier.getStackTrace(); - Stream.of(carrierStack).forEach(System.out::println); - assertEquals("run", carrierStack[0].getMethodName()); - assertEquals("run", carrierStack[carrierStack.length - 1].getMethodName()); - } finally { - vthread.interrupt(); - } - } - } - - /** - * A scheduler with one thread. - */ - private static class Scheduler implements AutoCloseable, Executor { - private final BlockingQueue tasks = new LinkedTransferQueue<>(); - private final Thread thread; - private volatile boolean done; - - Scheduler() { - this.thread = Thread.ofPlatform().start(() -> { - try { - while (!done) { - Runnable task = tasks.take(); - task.run(); - } - } catch (InterruptedException e) { } - }); - } - - Thread thread() { - return thread; - } - - @Override - public void close() throws InterruptedException { - done = true; - thread.interrupt(); - thread.join(); - } - - @Override - public void execute(Runnable task) { - tasks.add(task); - } - - Thread startVirtualThread(Runnable task) { - return ThreadBuilders.virtualThreadBuilder(this).start(task); - } - } - - private static void assertTrue(boolean e) { - if (!e) throw new RuntimeException(); - } - - private static void assertEquals(Object x, Object y) { - if (!Objects.equals(x, y)) - throw new RuntimeException(); - } -} diff --git a/test/jdk/java/lang/Thread/virtual/GetStackTraceWhenRunnable.java b/test/jdk/java/lang/Thread/virtual/GetStackTraceWhenRunnable.java deleted file mode 100644 index 38760eb52a8..00000000000 --- a/test/jdk/java/lang/Thread/virtual/GetStackTraceWhenRunnable.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2021, 2023, 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 - * @summary Test Thread::getStackTrace on a virtual thread that is runnable-unmounted - * @requires vm.continuations - * @run main/othervm -Djdk.virtualThreadScheduler.maxPoolSize=1 GetStackTraceWhenRunnable - */ - -import java.io.IOException; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.locks.LockSupport; - -public class GetStackTraceWhenRunnable { - - public static void main(String[] args) throws Exception { - - // start thread1 and wait for it to park - Thread thread1 = Thread.startVirtualThread(LockSupport::park); - while (thread1.getState() != Thread.State.WAITING) { - Thread.sleep(20); - } - - // start thread2 to pin the carrier thread - var started = new AtomicBoolean(); - var done = new AtomicBoolean(); - Thread thread2 = Thread.startVirtualThread(() -> { - started.set(true); - while (!done.get()) { - Thread.onSpinWait(); - } - }); - try { - // wait for thread2 to start - while (!started.get()) { - Thread.sleep(10); - } - - // unpark thread1 and check that it is "stuck" in the runnable state - // (the carrier thread is pinned, no other virtual thread can run) - LockSupport.unpark(thread1); - for (int i = 0; i < 5; i++) { - assertTrue(thread1.getState() == Thread.State.RUNNABLE); - Thread.sleep(100); - } - - // print thread1's stack trace - StackTraceElement[] stack = thread1.getStackTrace(); - assertTrue(stack.length > 0); - for (StackTraceElement e : stack) { - System.out.println(e); - } - } finally { - done.set(true); - thread2.join(); - thread1.join(); - } - } - - static void assertTrue(boolean e) { - if (!e) throw new RuntimeException(); - } -} diff --git a/test/jdk/java/lang/Thread/virtual/JfrEvents.java b/test/jdk/java/lang/Thread/virtual/JfrEvents.java index 282a8959fe8..539ac2104ed 100644 --- a/test/jdk/java/lang/Thread/virtual/JfrEvents.java +++ b/test/jdk/java/lang/Thread/virtual/JfrEvents.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, 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 @@ -40,9 +40,7 @@ import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.LockSupport; -import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -52,15 +50,25 @@ import jdk.jfr.consumer.RecordingFile; import jdk.test.lib.thread.VThreadPinner; -import jdk.test.lib.thread.VThreadRunner.ThrowingRunnable; +import jdk.test.lib.thread.VThreadRunner; +import jdk.test.lib.thread.VThreadScheduler; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import static org.junit.jupiter.api.Assertions.*; class JfrEvents { + @BeforeAll + static void setup() { + int minParallelism = 2; + if (Thread.currentThread().isVirtual()) { + minParallelism++; + } + VThreadRunner.ensureParallelism(minParallelism); + } + /** * Test jdk.VirtualThreadStart and jdk.VirtualThreadEnd events. */ @@ -93,100 +101,42 @@ void testVirtualThreadStartAndEnd() throws Exception { } /** - * Arguments for testVirtualThreadPinned to test jdk.VirtualThreadPinned event. - * [0] label/description - * [1] the operation to park/wait - * [2] the Thread.State when parked/waiting - * [3] the action to unpark/notify the thread - */ - static Stream pinnedCases() { - Object lock = new Object(); - - // park with native frame on stack - var finish1 = new AtomicBoolean(); - var parkWhenPinned = Arguments.of( - "LockSupport.park when pinned", - (ThrowingRunnable) () -> { - VThreadPinner.runPinned(() -> { - while (!finish1.get()) { - LockSupport.park(); - } - }); - }, - Thread.State.WAITING, - (Consumer) t -> { - finish1.set(true); - LockSupport.unpark(t); - } - ); - - // timed park with native frame on stack - var finish2 = new AtomicBoolean(); - var timedParkWhenPinned = Arguments.of( - "LockSupport.parkNanos when pinned", - (ThrowingRunnable) () -> { - VThreadPinner.runPinned(() -> { - while (!finish2.get()) { - LockSupport.parkNanos(Long.MAX_VALUE); - } - }); - }, - Thread.State.TIMED_WAITING, - (Consumer) t -> { - finish2.set(true); - LockSupport.unpark(t); - } - ); - - return Stream.of(parkWhenPinned, timedParkWhenPinned); - } - - /** - * Test jdk.VirtualThreadPinned event. + * Test jdk.VirtualThreadPinned event when parking while pinned. */ @ParameterizedTest - @MethodSource("pinnedCases") - void testVirtualThreadPinned(String label, - ThrowingRunnable parker, - Thread.State expectedState, - Consumer unparker) throws Exception { - + @ValueSource(booleans = { true, false }) + void testParkWhenPinned(boolean timed) throws Exception { try (Recording recording = new Recording()) { recording.enable("jdk.VirtualThreadPinned"); - recording.start(); - try { - var exception = new AtomicReference(); - var thread = Thread.ofVirtual().start(() -> { - try { - parker.run(); - } catch (Throwable e) { - exception.set(e); + + var started = new AtomicBoolean(); + var done = new AtomicBoolean(); + var vthread = Thread.startVirtualThread(() -> { + VThreadPinner.runPinned(() -> { + started.set(true); + while (!done.get()) { + if (timed) { + LockSupport.parkNanos(Long.MAX_VALUE); + } else { + LockSupport.park(); + } } }); - try { - // wait for thread to park/wait - Thread.State state = thread.getState(); - while (state != expectedState) { - assertTrue(state != Thread.State.TERMINATED, thread.toString()); - Thread.sleep(10); - state = thread.getState(); - } - } finally { - unparker.accept(thread); - thread.join(); - assertNull(exception.get()); - } + }); + + try { + // wait for thread to start and park + awaitTrue(started); + await(vthread, timed ? Thread.State.TIMED_WAITING : Thread.State.WAITING); } finally { + done.set(true); + LockSupport.unpark(vthread); + vthread.join(); recording.stop(); } - Map events = sumEvents(recording); - System.err.println(events); - - // should have at least one pinned event - int pinnedCount = events.getOrDefault("jdk.VirtualThreadPinned", 0); - assertTrue(pinnedCount >= 1, "Expected one or more events"); + assertContainsPinnedEvent(recording, vthread); } } @@ -203,16 +153,14 @@ void testVirtualThreadSubmitFailed() throws Exception { Executor scheduler = task -> pool.execute(task); // create virtual thread that uses custom scheduler - ThreadFactory factory = ThreadBuilders.virtualThreadBuilder(scheduler).factory(); + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); // start a thread Thread thread = factory.newThread(LockSupport::park); thread.start(); // wait for thread to park - while (thread.getState() != Thread.State.WAITING) { - Thread.sleep(10); - } + await(thread, Thread.State.WAITING); // shutdown scheduler pool.shutdown(); @@ -232,14 +180,23 @@ void testVirtualThreadSubmitFailed() throws Exception { recording.stop(); } - Map events = sumEvents(recording); - System.err.println(events); - - int count = events.getOrDefault("jdk.VirtualThreadSubmitFailed", 0); - assertEquals(2, count); + List submitFailedEvents = find(recording, "jdk.VirtualThreadSubmitFailed"); + System.err.println(submitFailedEvents); + assertTrue(submitFailedEvents.size() == 2, "Expected two events"); } } + /** + * Returns the list of events in the given recording with the given name. + */ + private static List find(Recording recording, String name) throws IOException { + Path recordingFile = recordingFile(recording); + return RecordingFile.readAllEvents(recordingFile) + .stream() + .filter(e -> e.getEventType().getName().equals(name)) + .toList(); + } + /** * Read the events from the recording and return a map of event name to count. */ @@ -264,4 +221,38 @@ private static Path recordingFile(Recording recording) throws IOException { } return recordingFile; } + + /** + * Assert that a recording contains a jdk.VirtualThreadPinned event on the given thread. + */ + private void assertContainsPinnedEvent(Recording recording, Thread thread) throws IOException { + List pinnedEvents = find(recording, "jdk.VirtualThreadPinned"); + assertTrue(pinnedEvents.size() > 0, "No jdk.VirtualThreadPinned events in recording"); + System.err.println(pinnedEvents); + + long tid = thread.threadId(); + assertTrue(pinnedEvents.stream() + .anyMatch(e -> e.getThread().getJavaThreadId() == tid), + "jdk.VirtualThreadPinned for javaThreadId = " + tid + " not found"); + } + + /** + * Waits for the given boolean to be set to true. + */ + private void awaitTrue(AtomicBoolean b) throws InterruptedException { + while (!b.get()) { + Thread.sleep(10); + } + } + + /** + * Waits for the given thread to reach a given state. + */ + private static void await(Thread thread, Thread.State expectedState) throws InterruptedException { + Thread.State state = thread.getState(); + while (state != expectedState) { + Thread.sleep(10); + state = thread.getState(); + } + } } diff --git a/test/jdk/java/lang/Thread/virtual/MonitorEnterExit.java b/test/jdk/java/lang/Thread/virtual/MonitorEnterExit.java new file mode 100644 index 00000000000..d1f2bf0c1bf --- /dev/null +++ b/test/jdk/java/lang/Thread/virtual/MonitorEnterExit.java @@ -0,0 +1,410 @@ +/* + * Copyright (c) 2023, 2024, 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 id=default + * @summary Test virtual thread with monitor enter/exit + * @modules java.base/java.lang:+open + * @library /test/lib + * @run junit/othervm --enable-native-access=ALL-UNNAMED MonitorEnterExit + */ + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.LockSupport; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import jdk.test.lib.thread.VThreadPinner; +import jdk.test.lib.thread.VThreadRunner; +import jdk.test.lib.thread.VThreadScheduler; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.RepeatedTest; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ValueSource; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.api.condition.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assumptions.*; + +class MonitorEnterExit { + static final int MAX_ENTER_DEPTH = 256; + + @BeforeAll + static void setup() { + // need >=2 carriers for testing pinning when main thread is a virtual thread + if (Thread.currentThread().isVirtual()) { + VThreadRunner.ensureParallelism(2); + } + } + + /** + * Test monitor enter with no contention. + */ + @Test + void testEnterNoContention() throws Exception { + var lock = new Object(); + VThreadRunner.run(() -> { + synchronized (lock) { + assertTrue(Thread.holdsLock(lock)); + } + assertFalse(Thread.holdsLock(lock)); + }); + } + + /** + * Test monitor enter with contention, monitor is held by platform thread. + */ + @Test + void testEnterWhenHeldByPlatformThread() throws Exception { + testEnterWithContention(); + } + + /** + * Test monitor enter with contention, monitor is held by virtual thread. + */ + @Test + void testEnterWhenHeldByVirtualThread() throws Exception { + VThreadRunner.run(this::testEnterWithContention); + } + + /** + * Test monitor enter with contention, monitor will be held by caller thread. + */ + private void testEnterWithContention() throws Exception { + var lock = new Object(); + var started = new CountDownLatch(1); + var entered = new AtomicBoolean(); + var vthread = Thread.ofVirtual().unstarted(() -> { + started.countDown(); + synchronized (lock) { + assertTrue(Thread.holdsLock(lock)); + entered.set(true); + } + assertFalse(Thread.holdsLock(lock)); + }); + try { + synchronized (lock) { + vthread.start(); + + // wait for thread to start and block + started.await(); + await(vthread, Thread.State.BLOCKED); + + assertFalse(entered.get()); + } + } finally { + vthread.join(); + } + assertTrue(entered.get()); + } + + /** + * Test monitor reenter. + */ + @Test + void testReenter() throws Exception { + var lock = new Object(); + VThreadRunner.run(() -> { + testReenter(lock, 0); + assertFalse(Thread.holdsLock(lock)); + }); + } + + private void testReenter(Object lock, int depth) { + if (depth < MAX_ENTER_DEPTH) { + synchronized (lock) { + assertTrue(Thread.holdsLock(lock)); + testReenter(lock, depth + 1); + assertTrue(Thread.holdsLock(lock)); + } + } + } + + /** + * Test monitor enter when pinned. + */ + @Test + void testEnterWhenPinned() throws Exception { + var lock = new Object(); + VThreadPinner.runPinned(() -> { + synchronized (lock) { + assertTrue(Thread.holdsLock(lock)); + } + assertFalse(Thread.holdsLock(lock)); + }); + } + + /** + * Test monitor reenter when pinned. + */ + @Test + void testReenterWhenPinned() throws Exception { + VThreadRunner.run(() -> { + var lock = new Object(); + synchronized (lock) { + VThreadPinner.runPinned(() -> { + assertTrue(Thread.holdsLock(lock)); + synchronized (lock) { + assertTrue(Thread.holdsLock(lock)); + } + assertTrue(Thread.holdsLock(lock)); + }); + } + assertFalse(Thread.holdsLock(lock)); + }); + } + + /** + * Test contended monitor enter when pinned. Monitor is held by platform thread. + */ + @Test + void testContendedEnterWhenPinnedHeldByPlatformThread() throws Exception { + testEnterWithContentionWhenPinned(); + } + + /** + * Test contended monitor enter when pinned. Monitor is held by virtual thread. + */ + @Test + void testContendedEnterWhenPinnedHeldByVirtualThread() throws Exception { + // need at least two carrier threads + int previousParallelism = VThreadRunner.ensureParallelism(2); + try { + VThreadRunner.run(this::testEnterWithContentionWhenPinned); + } finally { + VThreadRunner.setParallelism(previousParallelism); + } + } + + /** + * Test contended monitor enter when pinned, monitor will be held by caller thread. + */ + private void testEnterWithContentionWhenPinned() throws Exception { + var lock = new Object(); + var started = new CountDownLatch(1); + var entered = new AtomicBoolean(); + Thread vthread = Thread.ofVirtual().unstarted(() -> { + VThreadPinner.runPinned(() -> { + started.countDown(); + synchronized (lock) { + entered.set(true); + } + }); + }); + synchronized (lock) { + // start thread and wait for it to block + vthread.start(); + started.await(); + await(vthread, Thread.State.BLOCKED); + assertFalse(entered.get()); + } + vthread.join(); + + // check thread entered monitor + assertTrue(entered.get()); + } + + /** + * Returns a stream of elements that are ordered pairs of platform and virtual thread + * counts. 0,2,4,..16 platform threads. 2,4,6,..32 virtual threads. + */ + static Stream threadCounts() { + return IntStream.range(0, 17) + .filter(i -> i % 2 == 0) + .mapToObj(i -> i) + .flatMap(np -> IntStream.range(2, 33) + .filter(i -> i % 2 == 0) + .mapToObj(vp -> Arguments.of(np, vp))); + } + + /** + * Test mutual exclusion of monitors with platform and virtual threads. + */ + @ParameterizedTest + @MethodSource("threadCounts") + void testMutualExclusion(int nPlatformThreads, int nVirtualThreads) throws Exception { + class Counter { + int count; + synchronized void increment() { + count++; + Thread.yield(); + } + } + var counter = new Counter(); + int nThreads = nPlatformThreads + nVirtualThreads; + var threads = new Thread[nThreads]; + int index = 0; + for (int i = 0; i < nPlatformThreads; i++) { + threads[index] = Thread.ofPlatform() + .name("platform-" + index) + .unstarted(counter::increment); + index++; + } + for (int i = 0; i < nVirtualThreads; i++) { + threads[index] = Thread.ofVirtual() + .name("virtual-" + index) + .unstarted(counter::increment); + index++; + } + // start all threads + for (Thread thread : threads) { + thread.start(); + } + // wait for all threads to terminate + for (Thread thread : threads) { + thread.join(); + } + assertEquals(nThreads, counter.count); + } + + /** + * Test unblocking a virtual thread waiting to enter a monitor held by a platform thread. + */ + @RepeatedTest(20) + void testUnblockingByPlatformThread() throws Exception { + testUnblocking(); + } + + /** + * Test unblocking a virtual thread waiting to enter a monitor held by another + * virtual thread. + */ + @RepeatedTest(20) + void testUnblockingByVirtualThread() throws Exception { + VThreadRunner.run(this::testUnblocking); + } + + /** + * Test unblocking a virtual thread waiting to enter a monitor, monitor will be + * initially be held by caller thread. + */ + private void testUnblocking() throws Exception { + var lock = new Object(); + var started = new CountDownLatch(1); + var entered = new AtomicBoolean(); + var vthread = Thread.ofVirtual().unstarted(() -> { + started.countDown(); + synchronized (lock) { + entered.set(true); + } + }); + try { + synchronized (lock) { + vthread.start(); + started.await(); + + // random delay before exiting monitor + switch (ThreadLocalRandom.current().nextInt(4)) { + case 0 -> { /* no delay */} + case 1 -> Thread.onSpinWait(); + case 2 -> Thread.yield(); + case 3 -> await(vthread, Thread.State.BLOCKED); + default -> fail(); + } + + assertFalse(entered.get()); + } + } finally { + vthread.join(); + } + assertTrue(entered.get()); + } + + /** + * Test that unblocking a virtual thread waiting to enter a monitor does not consume + * the thread's parking permit. + */ + @Test + void testParkingPermitNotConsumed() throws Exception { + var lock = new Object(); + var started = new CountDownLatch(1); + var vthread = Thread.ofVirtual().unstarted(() -> { + started.countDown(); + LockSupport.unpark(Thread.currentThread()); + synchronized (lock) { } // should block + LockSupport.park(); // should not park + }); + + synchronized (lock) { + vthread.start(); + // wait for thread to start and block + started.await(); + await(vthread, Thread.State.BLOCKED); + } + vthread.join(); + } + + /** + * Test that unblocking a virtual thread waiting to enter a monitor does not make + * available the thread's parking permit. + */ + @Test + void testParkingPermitNotOffered() throws Exception { + var lock = new Object(); + var started = new CountDownLatch(1); + var vthread = Thread.ofVirtual().unstarted(() -> { + started.countDown(); + synchronized (lock) { } // should block + LockSupport.park(); // should park + }); + + synchronized (lock) { + vthread.start(); + // wait for thread to start and block + started.await(); + await(vthread, Thread.State.BLOCKED); + } + + try { + // wait for thread to park, it should not terminate + await(vthread, Thread.State.WAITING); + vthread.join(Duration.ofMillis(100)); + assertEquals(Thread.State.WAITING, vthread.getState()); + } finally { + LockSupport.unpark(vthread); + vthread.join(); + } + } + + /** + * Waits for the given thread to reach a given state. + */ + private void await(Thread thread, Thread.State expectedState) throws InterruptedException { + Thread.State state = thread.getState(); + while (state != expectedState) { + assertTrue(state != Thread.State.TERMINATED, "Thread has terminated"); + Thread.sleep(10); + state = thread.getState(); + } + } +} diff --git a/test/jdk/java/lang/Thread/virtual/MonitorWaitNotify.java b/test/jdk/java/lang/Thread/virtual/MonitorWaitNotify.java index 1320cc8f15f..5f730dbbf0a 100644 --- a/test/jdk/java/lang/Thread/virtual/MonitorWaitNotify.java +++ b/test/jdk/java/lang/Thread/virtual/MonitorWaitNotify.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, 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 @@ -21,41 +21,78 @@ * questions. */ -/** - * @test +/* + * @test id=default * @summary Test virtual threads using Object.wait/notifyAll * @modules java.base/java.lang:+open * @library /test/lib - * @run junit MonitorWaitNotify + * @run junit/othervm --enable-native-access=ALL-UNNAMED MonitorWaitNotify */ -import java.util.concurrent.Semaphore; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.LockSupport; +import java.util.stream.IntStream; +import java.util.stream.Stream; +import java.util.stream.Collectors; +import jdk.test.lib.thread.VThreadScheduler; +import jdk.test.lib.thread.VThreadRunner; import jdk.test.lib.thread.VThreadRunner; +import jdk.test.lib.thread.VThreadPinner; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assumptions.*; class MonitorWaitNotify { + @BeforeAll + static void setup() { + // need >=2 carriers for testing pinning + VThreadRunner.ensureParallelism(2); + } + /** * Test virtual thread waits, notified by platform thread. */ - @Test - void testWaitNotify1() throws Exception { + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testWaitNotify1(boolean pinned) throws Exception { var lock = new Object(); - var ready = new Semaphore(0); + var ready = new AtomicBoolean(); var thread = Thread.ofVirtual().start(() -> { synchronized (lock) { - ready.release(); try { - lock.wait(); + if (pinned) { + VThreadPinner.runPinned(() -> { + ready.set(true); + lock.wait(); + }); + } else { + ready.set(true); + lock.wait(); + } } catch (InterruptedException e) { } } }); - // thread invokes notify - ready.acquire(); + awaitTrue(ready); + + // notify, thread should block waiting to reenter synchronized (lock) { lock.notifyAll(); + await(thread, Thread.State.BLOCKED); } thread.join(); } @@ -66,15 +103,13 @@ void testWaitNotify1() throws Exception { @Test void testWaitNotify2() throws Exception { var lock = new Object(); - var ready = new Semaphore(0); - var thread = Thread.ofVirtual().start(() -> { - ready.acquireUninterruptibly(); + var thread = Thread.ofVirtual().unstarted(() -> { synchronized (lock) { lock.notifyAll(); } }); synchronized (lock) { - ready.release(); + thread.start(); lock.wait(); } thread.join(); @@ -83,89 +118,447 @@ void testWaitNotify2() throws Exception { /** * Test virtual thread waits, notified by another virtual thread. */ - @Test - void testWaitNotify3() throws Exception { - // need at least two carrier threads due to pinning - int previousParallelism = VThreadRunner.ensureParallelism(2); - try { - var lock = new Object(); - var ready = new Semaphore(0); - var thread1 = Thread.ofVirtual().start(() -> { - synchronized (lock) { - ready.release(); - try { + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testWaitNotify3(boolean pinned) throws Exception { + var lock = new Object(); + var ready = new AtomicBoolean(); + var thread1 = Thread.ofVirtual().start(() -> { + synchronized (lock) { + try { + if (pinned) { + VThreadPinner.runPinned(() -> { + ready.set(true); + lock.wait(); + }); + } else { + ready.set(true); lock.wait(); - } catch (InterruptedException e) { } + } + } catch (InterruptedException e) { + e.printStackTrace(); } - }); - var thread2 = Thread.ofVirtual().start(() -> { - ready.acquireUninterruptibly(); + } + }); + var thread2 = Thread.ofVirtual().start(() -> { + try { + awaitTrue(ready); + + // notify, thread should block waiting to reenter synchronized (lock) { lock.notifyAll(); + await(thread1, Thread.State.BLOCKED); } - }); - thread1.join(); - thread2.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + }); + thread1.join(); + thread2.join(); + } + + /** + * Test notifyAll when there are no threads waiting. + */ + @ParameterizedTest + @ValueSource(ints = { 0, 30000, Integer.MAX_VALUE }) + void testNotifyBeforeWait(int timeout) throws Exception { + var lock = new Object(); + + // no threads waiting + synchronized (lock) { + lock.notifyAll(); + } + + var ready = new AtomicBoolean(); + var thread = Thread.ofVirtual().start(() -> { + try { + synchronized (lock) { + ready.set(true); + + // thread should wait + if (timeout > 0) { + lock.wait(timeout); + } else { + lock.wait(); + } + } + } catch (InterruptedException e) { } + }); + + try { + // wait for thread to start and wait + awaitTrue(ready); + Thread.State expectedState = timeout > 0 + ? Thread.State.TIMED_WAITING + : Thread.State.WAITING; + await(thread, expectedState); + + // poll thread state again, it should still be waiting + Thread.sleep(10); + assertEquals(thread.getState(), expectedState); } finally { - // restore - VThreadRunner.setParallelism(previousParallelism); + synchronized (lock) { + lock.notifyAll(); + } + thread.join(); } } + /** + * Test duration of timed Object.wait. + */ + @Test + void testTimedWaitDuration1() throws Exception { + var lock = new Object(); + + var durationRef = new AtomicReference(); + var thread = Thread.ofVirtual().start(() -> { + try { + synchronized (lock) { + long start = millisTime(); + lock.wait(2000); + durationRef.set(millisTime() - start); + } + } catch (InterruptedException e) { } + }); + + thread.join(); + + long duration = durationRef.get(); + checkDuration(duration, 1900, 20_000); + } /** - * Test interrupt status set when calling Object.wait. + * Test duration of timed Object.wait. This test invokes wait twice, first with a short + * timeout, the second with a longer timeout. The test scenario ensures that the + * timeout from the first wait doesn't interfere with the second wait. */ @Test - void testWaitNotify4() throws Exception { + void testTimedWaitDuration2() throws Exception { + var lock = new Object(); + + var ready = new AtomicBoolean(); + var waited = new AtomicBoolean(); + var durationRef = new AtomicReference(); + var thread = Thread.ofVirtual().start(() -> { + try { + synchronized (lock) { + ready.set(true); + lock.wait(200); + waited.set(true); + + long start = millisTime(); + lock.wait(2000); + durationRef.set(millisTime() - start); + } + } catch (InterruptedException e) { } + }); + + awaitTrue(ready); + synchronized (lock) { + // wake thread if waiting in first wait + if (!waited.get()) { + lock.notifyAll(); + } + } + + thread.join(); + + long duration = durationRef.get(); + checkDuration(duration, 1900, 20_000); + } + + /** + * Testing invoking Object.wait with interrupt status set. + */ + @ParameterizedTest + @ValueSource(ints = { 0, 30000, Integer.MAX_VALUE }) + void testWaitWithInterruptSet(int timeout) throws Exception { VThreadRunner.run(() -> { - Thread t = Thread.currentThread(); - t.interrupt(); Object lock = new Object(); + synchronized (lock) { + Thread.currentThread().interrupt(); + if (timeout > 0) { + assertThrows(InterruptedException.class, () -> lock.wait(timeout)); + } else { + assertThrows(InterruptedException.class, lock::wait); + } + assertFalse(Thread.currentThread().isInterrupted()); + } + }); + } + + /** + * Test interrupting a virtual thread waiting in Object.wait. + */ + @ParameterizedTest + @ValueSource(ints = { 0, 30000, Integer.MAX_VALUE }) + void testInterruptWait(int timeout) throws Exception { + var lock = new Object(); + var ready = new AtomicBoolean(); + var interruptedException = new AtomicBoolean(); + var vthread = Thread.ofVirtual().start(() -> { + synchronized (lock) { + try { + ready.set(true); + if (timeout > 0) { + lock.wait(timeout); + } else { + lock.wait(); + } + } catch (InterruptedException e) { + // check stack trace has the expected frames + Set expected = Set.of("wait0", "wait", "run"); + Set methods = Stream.of(e.getStackTrace()) + .map(StackTraceElement::getMethodName) + .collect(Collectors.toSet()); + assertTrue(methods.containsAll(expected)); + + interruptedException.set(true); + } + } + }); + + // wait for thread to start and wait + awaitTrue(ready); + await(vthread, timeout > 0 ? Thread.State.TIMED_WAITING : Thread.State.WAITING); + + // interrupt thread, should block, then throw InterruptedException + synchronized (lock) { + vthread.interrupt(); + await(vthread, Thread.State.BLOCKED); + } + vthread.join(); + assertTrue(interruptedException.get()); + } + + /** + * Test interrupting a virtual thread blocked waiting to reenter after waiting. + */ + @ParameterizedTest + @ValueSource(ints = { 0, 30000, Integer.MAX_VALUE }) + void testInterruptReenterAfterWait(int timeout) throws Exception { + var lock = new Object(); + var ready = new AtomicBoolean(); + var interruptedException = new AtomicBoolean(); + var vthread = Thread.ofVirtual().start(() -> { + synchronized (lock) { + try { + ready.set(true); + if (timeout > 0) { + lock.wait(timeout); + } else { + lock.wait(); + } + } catch (InterruptedException e) { + interruptedException.set(true); + } + } + }); + + // wait for thread to start and wait + awaitTrue(ready); + await(vthread, timeout > 0 ? Thread.State.TIMED_WAITING : Thread.State.WAITING); + + // notify, thread should block waiting to reenter + synchronized (lock) { + lock.notifyAll(); + await(vthread, Thread.State.BLOCKED); + + // interrupt when blocked + vthread.interrupt(); + } + + vthread.join(); + assertFalse(interruptedException.get()); + assertTrue(vthread.isInterrupted()); + } + + /** + * Test Object.wait when the monitor entry count > 1. + */ + @ParameterizedTest + @ValueSource(ints = { 0, 30000, Integer.MAX_VALUE }) + void testWaitWhenEnteredManyTimes(int timeout) throws Exception { + var lock = new Object(); + var ready = new AtomicBoolean(); + var vthread = Thread.ofVirtual().start(() -> { + synchronized (lock) { + synchronized (lock) { + synchronized (lock) { + try { + ready.set(true); + if (timeout > 0) { + lock.wait(timeout); + } else { + lock.wait(); + } + } catch (InterruptedException e) { } + } + } + } + }); + + // wait for thread to start and wait + awaitTrue(ready); + await(vthread, timeout > 0 ? Thread.State.TIMED_WAITING : Thread.State.WAITING); + + // notify, thread should block waiting to reenter + synchronized (lock) { + lock.notifyAll(); + await(vthread, Thread.State.BLOCKED); + } + vthread.join(); + } + + /** + * Test that Object.wait does not consume the thread's parking permit. + */ + @Test + void testParkingPermitNotConsumed() throws Exception { + var lock = new Object(); + var started = new CountDownLatch(1); + var completed = new AtomicBoolean(); + var vthread = Thread.ofVirtual().start(() -> { + started.countDown(); + LockSupport.unpark(Thread.currentThread()); synchronized (lock) { try { lock.wait(); - fail(); } catch (InterruptedException e) { - // interrupt status should be cleared - assertFalse(t.isInterrupted()); + fail("wait interrupted"); } } + LockSupport.park(); // should not park + completed.set(true); }); + + // wait for thread to start and wait + started.await(); + await(vthread, Thread.State.WAITING); + + // wakeup thread + synchronized (lock) { + lock.notifyAll(); + } + + // thread should terminate + vthread.join(); + assertTrue(completed.get()); } /** - * Test interrupt when blocked in Object.wait. + * Test that Object.wait does not make available the thread's parking permit. */ @Test - void testWaitNotify5() throws Exception { - VThreadRunner.run(() -> { - Thread t = Thread.currentThread(); - scheduleInterrupt(t, 1000); - Object lock = new Object(); + void testParkingPermitNotOffered() throws Exception { + var lock = new Object(); + var started = new CountDownLatch(1); + var readyToPark = new CountDownLatch(1); + var completed = new AtomicBoolean(); + var vthread = Thread.ofVirtual().start(() -> { + started.countDown(); synchronized (lock) { try { lock.wait(); - fail(); } catch (InterruptedException e) { - // interrupt status should be cleared - assertFalse(t.isInterrupted()); + fail("wait interrupted"); } } + readyToPark.countDown(); + LockSupport.park(); // should park + completed.set(true); }); + + // wait for thread to start and wait + started.await(); + await(vthread, Thread.State.WAITING); + + // wakeup thread + synchronized (lock) { + lock.notifyAll(); + } + + // thread should park + readyToPark.await(); + await(vthread, Thread.State.WAITING); + + LockSupport.unpark(vthread); + + // thread should terminate + vthread.join(); + assertTrue(completed.get()); } /** - * Schedule a thread to be interrupted after a delay. + * Test that wait(long) throws IAE when timeout is negative. */ - private static void scheduleInterrupt(Thread thread, long delay) { - Runnable interruptTask = () -> { - try { - Thread.sleep(delay); - thread.interrupt(); - } catch (Exception e) { - e.printStackTrace(); + @Test + void testIllegalArgumentException() throws Exception { + VThreadRunner.run(() -> { + Object obj = new Object(); + synchronized (obj) { + assertThrows(IllegalArgumentException.class, () -> obj.wait(-1L)); + assertThrows(IllegalArgumentException.class, () -> obj.wait(-1000L)); + assertThrows(IllegalArgumentException.class, () -> obj.wait(Long.MIN_VALUE)); } - }; - new Thread(interruptTask).start(); + }); + } + + /** + * Test that wait throws IMSE when not owner. + */ + @Test + void testIllegalMonitorStateException() throws Exception { + VThreadRunner.run(() -> { + Object obj = new Object(); + assertThrows(IllegalMonitorStateException.class, () -> obj.wait()); + assertThrows(IllegalMonitorStateException.class, () -> obj.wait(0)); + assertThrows(IllegalMonitorStateException.class, () -> obj.wait(1000)); + assertThrows(IllegalMonitorStateException.class, () -> obj.wait(Long.MAX_VALUE)); + }); + } + + /** + * Waits for the boolean value to become true. + */ + private static void awaitTrue(AtomicBoolean ref) throws InterruptedException { + while (!ref.get()) { + Thread.sleep(20); + } + } + + /** + * Waits for the given thread to reach a given state. + */ + private void await(Thread thread, Thread.State expectedState) throws InterruptedException { + Thread.State state = thread.getState(); + while (state != expectedState) { + assertTrue(state != Thread.State.TERMINATED, "Thread has terminated"); + Thread.sleep(10); + state = thread.getState(); + } + } + + /** + * Returns the current time in milliseconds. + */ + private static long millisTime() { + long now = System.nanoTime(); + return TimeUnit.MILLISECONDS.convert(now, TimeUnit.NANOSECONDS); + } + + /** + * Check a duration is within expected bounds. + * @param duration, in milliseconds + * @param min minimum expected duration, in milliseconds + * @param max maximum expected duration, in milliseconds + * @return the duration (now - start), in milliseconds + */ + private static void checkDuration(long duration, long min, long max) { + assertTrue(duration >= min, + "Duration " + duration + "ms, expected >= " + min + "ms"); + assertTrue(duration <= max, + "Duration " + duration + "ms, expected <= " + max + "ms"); } } diff --git a/test/jdk/java/lang/Thread/virtual/ParkWithFixedThreadPool.java b/test/jdk/java/lang/Thread/virtual/ParkWithFixedThreadPool.java index 950f036458d..28a32fc504a 100644 --- a/test/jdk/java/lang/Thread/virtual/ParkWithFixedThreadPool.java +++ b/test/jdk/java/lang/Thread/virtual/ParkWithFixedThreadPool.java @@ -26,12 +26,14 @@ * @summary Test virtual thread park when scheduler is a fixed thread pool * @requires vm.continuations * @modules java.base/java.lang:+open + * @library /test/lib * @run main ParkWithFixedThreadPool */ import java.util.concurrent.*; import java.util.concurrent.atomic.*; import java.util.concurrent.locks.LockSupport; +import jdk.test.lib.thread.VThreadScheduler; public class ParkWithFixedThreadPool { public static void main(String[] args) throws Exception { @@ -58,9 +60,7 @@ public void run() { } }; - ThreadFactory factory = ThreadBuilders.virtualThreadBuilder(scheduler) - .name("vthread-", 0) - .factory(); + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); for (int i = 0; i < vthreadCount; i++) { vthreads[i] = factory.newThread(target); diff --git a/test/jdk/java/lang/Thread/virtual/Reflection.java b/test/jdk/java/lang/Thread/virtual/Reflection.java index 4dd34e6c726..f508b7949ff 100644 --- a/test/jdk/java/lang/Thread/virtual/Reflection.java +++ b/test/jdk/java/lang/Thread/virtual/Reflection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, 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 @@ -38,6 +38,7 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.locks.LockSupport; +import jdk.test.lib.thread.VThreadScheduler; import jdk.test.lib.thread.VThreadRunner; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; @@ -142,11 +143,10 @@ static void foo() { } */ @Test void testInvokeStatic6() throws Exception { - assumeTrue(ThreadBuilders.supportsCustomScheduler(), "No support for custom schedulers"); + assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); Method parkMethod = Parker.class.getDeclaredMethod("park"); try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { - Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler); - ThreadFactory factory = builder.factory(); + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); var ready = new CountDownLatch(1); Thread vthread = factory.newThread(() -> { @@ -321,11 +321,10 @@ static void foo() { } */ @Test void testNewInstance6() throws Exception { - assumeTrue(ThreadBuilders.supportsCustomScheduler(), "No support for custom schedulers"); + assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); Constructor ctor = Parker.class.getDeclaredConstructor(); try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { - Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler); - ThreadFactory factory = builder.factory(); + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); var ready = new CountDownLatch(1); Thread vthread = factory.newThread(() -> { diff --git a/test/jdk/java/lang/Thread/virtual/StackFrames.java b/test/jdk/java/lang/Thread/virtual/StackFrames.java new file mode 100644 index 00000000000..6ae65d031dd --- /dev/null +++ b/test/jdk/java/lang/Thread/virtual/StackFrames.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2020, 2024, 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 + * @summary Test stack traces in exceptions, stack frames walked by the StackWalker, + * and the stack trace returned by Thread.getStackTrace + * @requires vm.continuations + * @modules java.base/java.lang:+open java.management + * @library /test/lib + * @run junit StackFrames + * @run junit/othervm -XX:+UnlockDiagnosticVMOptions -XX:+ShowCarrierFrames StackFrames + */ + +import java.lang.management.ManagementFactory; +import java.util.Arrays; +import java.util.Set; +import java.util.concurrent.Executor; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinWorkerThread; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Stream; +import static java.lang.StackWalker.Option.*; + +import jdk.test.lib.thread.VThreadRunner; +import jdk.test.lib.thread.VThreadScheduler; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +class StackFrames { + + /** + * Test that the stack trace in exceptions does not include the carrier thread + * frames, except when running with -XX:+ShowCarrierFrames. + */ + @Test + void testStackTraceException() throws Exception { + VThreadRunner.run(() -> { + Exception e = new Exception(); + boolean found = Arrays.stream(e.getStackTrace()) + .map(StackTraceElement::getClassName) + .anyMatch("java.util.concurrent.ForkJoinPool"::equals); + assertTrue(found == hasJvmArgument("-XX:+ShowCarrierFrames")); + }); + } + + /** + * Test that StackWalker does not include carrier thread frames in the stream of + * stack frames. + */ + @Test + void testStackWalker() throws Exception { + VThreadRunner.run(() -> { + StackWalker walker = StackWalker.getInstance(Set.of(RETAIN_CLASS_REFERENCE)); + boolean found = walker.walk(sf -> + sf.map(StackWalker.StackFrame::getDeclaringClass) + .anyMatch(c -> c == ForkJoinPool.class)); + assertFalse(found); + }); + } + + /** + * Test Thread.getStackTrace returns the expected bottom frame for both the carrier + * and virtual thread. + */ + @Test + void testBottomFrames() throws Exception { + try (ForkJoinPool pool = new ForkJoinPool(1)) { + var carrierRef = new AtomicReference(); + Executor scheduler = task -> { + pool.submit(() -> { + carrierRef.set(Thread.currentThread()); + task.run(); + }); + }; + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); + + var ready = new AtomicBoolean(); + var done = new AtomicBoolean(); + + // create virtual thread to use custom scheduler + var vthread = factory.newThread(() -> { + ready.set(true); + while (!done.get()) { + Thread.onSpinWait(); + } + }); + + vthread.start(); + try { + awaitTrue(ready); + + // get carrier Thread + Thread carrier = carrierRef.get(); + assertTrue(carrier instanceof ForkJoinWorkerThread); + + // bottom-most frame of virtual thread should be VirtualThread.run + System.err.println(vthread); + StackTraceElement[] vthreadStack = vthread.getStackTrace(); + Stream.of(vthreadStack).forEach(e -> System.err.println(" " + e)); + StackTraceElement bottomFrame = vthreadStack[vthreadStack.length - 1]; + assertEquals("java.lang.VirtualThread.run", + bottomFrame.getClassName() + "." + bottomFrame.getMethodName()); + + // bottom-most frame of carrier thread should be Thread.run + System.err.println(carrier); + StackTraceElement[] carrierStack = carrier.getStackTrace(); + Stream.of(carrierStack).forEach(e -> System.err.println(" " + e)); + bottomFrame = carrierStack[carrierStack.length - 1]; + assertEquals("java.util.concurrent.ForkJoinWorkerThread.run", + bottomFrame.getClassName() + "." + bottomFrame.getMethodName()); + + } finally { + done.set(true); + vthread.join(); + } + } + } + + /** + * Returns true if started with the given VM option. + */ + private static boolean hasJvmArgument(String arg) { + for (String argument : ManagementFactory.getRuntimeMXBean().getInputArguments()) { + if (argument.equals(arg)) return true; + } + return false; + } + + /** + * Waits for the boolean value to become true. + */ + private static void awaitTrue(AtomicBoolean ref) throws InterruptedException { + while (!ref.get()) { + Thread.sleep(20); + } + } +} diff --git a/test/jdk/java/lang/Thread/virtual/StackTraces.java b/test/jdk/java/lang/Thread/virtual/StackTraces.java deleted file mode 100644 index cb1877090fe..00000000000 --- a/test/jdk/java/lang/Thread/virtual/StackTraces.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2020, 2023, 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 - * @summary Test stack traces in exceptions and stack frames walked by the StackWalker - * API do not include the carrier stack frames - * @requires vm.continuations - * @modules java.management - * @library /test/lib - * @run junit StackTraces - * @run junit/othervm -XX:+UnlockDiagnosticVMOptions -XX:+ShowCarrierFrames StackTraces - */ - -import java.lang.management.ManagementFactory; -import java.util.Arrays; -import java.util.Set; -import java.util.concurrent.ForkJoinPool; -import static java.lang.StackWalker.Option.*; - -import jdk.test.lib.thread.VThreadRunner; -import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; - -class StackTraces { - - /** - * Test that the stack trace in exceptions does not include the carrier thread - * frames, except when running with -XX:+ShowCarrierFrames. - */ - @Test - void testStackTrace() throws Exception { - VThreadRunner.run(() -> { - Exception e = new Exception(); - boolean found = Arrays.stream(e.getStackTrace()) - .map(StackTraceElement::getClassName) - .anyMatch("java.util.concurrent.ForkJoinPool"::equals); - assertTrue(found == hasJvmArgument("-XX:+ShowCarrierFrames")); - }); - } - - /** - * Test that StackWalker does not include carrier thread frames. - */ - @Test - void testStackWalker() throws Exception { - VThreadRunner.run(() -> { - StackWalker walker = StackWalker.getInstance(Set.of(RETAIN_CLASS_REFERENCE)); - boolean found = walker.walk(sf -> - sf.map(StackWalker.StackFrame::getDeclaringClass) - .anyMatch(c -> c == ForkJoinPool.class)); - assertFalse(found); - }); - } - - private static boolean hasJvmArgument(String arg) { - for (String argument : ManagementFactory.getRuntimeMXBean().getInputArguments()) { - if (argument.equals(arg)) return true; - } - return false; - } -} diff --git a/test/jdk/java/lang/Thread/virtual/ThreadAPI.java b/test/jdk/java/lang/Thread/virtual/ThreadAPI.java index b1ccb21910a..3bb5c75a1b5 100644 --- a/test/jdk/java/lang/Thread/virtual/ThreadAPI.java +++ b/test/jdk/java/lang/Thread/virtual/ThreadAPI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, 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 @@ -61,13 +61,16 @@ import java.util.stream.Stream; import java.nio.channels.Selector; -import jdk.test.lib.thread.VThreadRunner; import jdk.test.lib.thread.VThreadPinner; +import jdk.test.lib.thread.VThreadRunner; +import jdk.test.lib.thread.VThreadScheduler; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assumptions.*; @@ -78,9 +81,12 @@ class ThreadAPI { private static ScheduledExecutorService scheduler; @BeforeAll - static void setup() throws Exception { + static void setup() { ThreadFactory factory = Executors.defaultThreadFactory(); scheduler = Executors.newSingleThreadScheduledExecutor(factory); + + // need >=2 carriers for testing pinning + VThreadRunner.ensureParallelism(2); } @AfterAll @@ -719,14 +725,7 @@ void testJoin33() throws Exception { */ @Test void testJoin34() throws Exception { - // need at least two carrier threads due to pinning - int previousParallelism = VThreadRunner.ensureParallelism(2); - try { - VThreadRunner.run(this::testJoin33); - } finally { - // restore - VThreadRunner.setParallelism(previousParallelism); - } + VThreadRunner.run(this::testJoin33); } /** @@ -1084,11 +1083,10 @@ void testSetDaemon2() throws Exception { */ @Test void testYield1() throws Exception { - assumeTrue(ThreadBuilders.supportsCustomScheduler(), "No support for custom schedulers"); + assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); var list = new CopyOnWriteArrayList(); try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { - Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler); - ThreadFactory factory = builder.factory(); + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); var thread = factory.newThread(() -> { list.add("A"); var child = factory.newThread(() -> { @@ -1112,11 +1110,10 @@ void testYield1() throws Exception { */ @Test void testYield2() throws Exception { - assumeTrue(ThreadBuilders.supportsCustomScheduler(), "No support for custom schedulers"); + assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); var list = new CopyOnWriteArrayList(); try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { - Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler); - ThreadFactory factory = builder.factory(); + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); var thread = factory.newThread(() -> { list.add("A"); var child = factory.newThread(() -> { @@ -1708,50 +1705,59 @@ void testGetState3() throws Exception { */ @Test void testGetState4() throws Exception { - assumeTrue(ThreadBuilders.supportsCustomScheduler(), "No support for custom schedulers"); + assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); AtomicBoolean completed = new AtomicBoolean(); try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { - Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler); - Thread t1 = builder.start(() -> { - Thread t2 = builder.unstarted(LockSupport::park); - assertEquals(Thread.State.NEW, t2.getState()); + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); + Thread thread1 = factory.newThread(() -> { + Thread thread2 = factory.newThread(LockSupport::park); + assertEquals(Thread.State.NEW, thread2.getState()); // start t2 to make it runnable - t2.start(); + thread2.start(); try { - assertEquals(Thread.State.RUNNABLE, t2.getState()); + assertEquals(Thread.State.RUNNABLE, thread2.getState()); // yield to allow t2 to run and park Thread.yield(); - assertEquals(Thread.State.WAITING, t2.getState()); + assertEquals(Thread.State.WAITING, thread2.getState()); } finally { // unpark t2 to make it runnable again - LockSupport.unpark(t2); + LockSupport.unpark(thread2); } // t2 should be runnable (not mounted) - assertEquals(Thread.State.RUNNABLE, t2.getState()); + assertEquals(Thread.State.RUNNABLE, thread2.getState()); completed.set(true); }); - t1.join(); + thread1.start(); + thread1.join(); } assertTrue(completed.get() == true); } /** - * Test Thread::getState when thread is waiting to enter a monitor. + * Test Thread::getState when thread is blocked waiting to enter a monitor. */ - @Test - void testGetState5() throws Exception { - var started = new CountDownLatch(1); + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testGetState5(boolean pinned) throws Exception { + var ready = new AtomicBoolean(); var thread = Thread.ofVirtual().unstarted(() -> { - started.countDown(); - synchronized (lock) { } + if (pinned) { + VThreadPinner.runPinned(() -> { + ready.set(true); + synchronized (lock) { } + }); + } else { + ready.set(true); + synchronized (lock) { } + } }); synchronized (lock) { thread.start(); - started.await(); + awaitTrue(ready); // wait for thread to block await(thread, Thread.State.BLOCKED); @@ -1762,16 +1768,35 @@ void testGetState5() throws Exception { /** * Test Thread::getState when thread is waiting in Object.wait. */ - @Test - void testGetState6() throws Exception { + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testGetState6(boolean pinned) throws Exception { + var ready = new AtomicBoolean(); var thread = Thread.ofVirtual().start(() -> { synchronized (lock) { - try { lock.wait(); } catch (InterruptedException e) { } + try { + if (pinned) { + VThreadPinner.runPinned(() -> { + ready.set(true); + lock.wait(); + }); + } else { + ready.set(true); + lock.wait(); + } + } catch (InterruptedException e) { } } }); try { // wait for thread to wait + awaitTrue(ready); await(thread, Thread.State.WAITING); + + // notify, thread should block trying to reenter + synchronized (lock) { + lock.notifyAll(); + await(thread, Thread.State.BLOCKED); + } } finally { thread.interrupt(); thread.join(); @@ -1781,18 +1806,35 @@ void testGetState6() throws Exception { /** * Test Thread::getState when thread is waiting in Object.wait(millis). */ - @Test - void testGetState7() throws Exception { + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testGetState7(boolean pinned) throws Exception { + var ready = new AtomicBoolean(); var thread = Thread.ofVirtual().start(() -> { synchronized (lock) { try { - lock.wait(Long.MAX_VALUE); + if (pinned) { + VThreadPinner.runPinned(() -> { + ready.set(true); + lock.wait(Long.MAX_VALUE); + }); + } else { + ready.set(true); + lock.wait(Long.MAX_VALUE); + } } catch (InterruptedException e) { } } }); try { - // wait for thread to wait + // wait for thread to timed-wait + awaitTrue(ready); await(thread, Thread.State.TIMED_WAITING); + + // notify, thread should block trying to reenter + synchronized (lock) { + lock.notifyAll(); + await(thread, Thread.State.BLOCKED); + } } finally { thread.interrupt(); thread.join(); @@ -1933,7 +1975,7 @@ void testHoldsLock2() throws Exception { * Test Thread::getStackTrace on unstarted thread. */ @Test - void testGetStackTrace1() { + void testGetStackTraceUnstarted() { var thread = Thread.ofVirtual().unstarted(() -> { }); StackTraceElement[] stack = thread.getStackTrace(); assertTrue(stack.length == 0); @@ -1943,136 +1985,224 @@ void testGetStackTrace1() { * Test Thread::getStackTrace on thread that has been started but has not run. */ @Test - void testGetStackTrace2() throws Exception { - assumeTrue(ThreadBuilders.supportsCustomScheduler(), "No support for custom schedulers"); + void testGetStackTraceStarted() throws Exception { + assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); Executor scheduler = task -> { }; - Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler); - Thread thread = builder.start(() -> { }); + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); + Thread thread = factory.newThread(() -> { }); + thread.start(); StackTraceElement[] stack = thread.getStackTrace(); assertTrue(stack.length == 0); } /** - * Test Thread::getStackTrace on running thread. + * Test Thread::getStackTrace on thread that is runnable-mounted. */ @Test - void testGetStackTrace3() throws Exception { - var sel = Selector.open(); - var thread = Thread.ofVirtual().start(() -> { - try { sel.select(); } catch (Exception e) { } - }); - try { - while (!contains(thread.getStackTrace(), "select")) { - assertTrue(thread.isAlive()); - Thread.sleep(20); + void testGetStackTraceRunnableMounted() throws Exception { + var ready = new AtomicBoolean(); + var done = new AtomicBoolean(); + + class Foo { + void spinUntilDone() { + ready.set(true); + while (!done.get()) { + Thread.onSpinWait(); + } } + } + + Foo foo = new Foo(); + var thread = Thread.ofVirtual().start(foo::spinUntilDone); + try { + awaitTrue(ready); + StackTraceElement[] stack = thread.getStackTrace(); + assertTrue(contains(stack, Foo.class.getName() + ".spinUntilDone")); } finally { - sel.close(); + done.set(true); thread.join(); } } /** - * Test Thread::getStackTrace on thread waiting in Object.wait. + * Test Thread::getStackTrace on thread that is runnable-unmounted. */ @Test - void testGetStackTrace4() throws Exception { - assumeTrue(ThreadBuilders.supportsCustomScheduler(), "No support for custom schedulers"); - try (ForkJoinPool pool = new ForkJoinPool(1)) { - AtomicReference ref = new AtomicReference<>(); - Executor scheduler = task -> { - pool.submit(() -> { - ref.set(Thread.currentThread()); - task.run(); - }); - }; + void testGetStackTraceRunnableUnmounted() throws Exception { + assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); - Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler); - Thread vthread = builder.start(() -> { - synchronized (lock) { - try { - lock.wait(); - } catch (Exception e) { } + // custom scheduler with one carrier thread + try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); + + // start thread1 to park + Thread thread1 = factory.newThread(LockSupport::park); + thread1.start(); + await(thread1, Thread.State.WAITING); + + // start thread2 to spin and pin the carrier thread + var started = new AtomicBoolean(); + var done = new AtomicBoolean(); + Thread thread2 = factory.newThread(() -> { + started.set(true); + while (!done.get()) { + Thread.onSpinWait(); } }); + thread2.start(); + awaitTrue(started); - // get carrier Thread - Thread carrier; - while ((carrier = ref.get()) == null) { - Thread.sleep(20); - } + try { + // unpark thread1, it should be "stuck" in runnable state + // (the carrier thread is pinned, no other virtual thread can run) + LockSupport.unpark(thread1); + assertEquals(Thread.State.RUNNABLE, thread1.getState()); - // wait for virtual thread to block in wait - await(vthread, Thread.State.WAITING); + // print thread1's stack trace + StackTraceElement[] stack = thread1.getStackTrace(); + assertTrue(contains(stack, "LockSupport.park")); - // get stack trace of both carrier and virtual thread - StackTraceElement[] carrierStackTrace = carrier.getStackTrace(); - StackTraceElement[] vthreadStackTrace = vthread.getStackTrace(); + } finally { + done.set(true); + } + } + } - // allow virtual thread to terminate - synchronized (lock) { - lock.notifyAll(); + /** + * Test Thread::getStackTrace on thread blocked on monitor enter. + */ + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testGetStackTraceBlocked(boolean pinned) throws Exception { + class Foo { + void enter() { + synchronized (this) { } + } + } + Foo foo = new Foo(); + var ready = new AtomicBoolean(); + var thread = Thread.ofVirtual().unstarted(() -> { + if (pinned) { + VThreadPinner.runPinned(() -> { + ready.set(true); + foo.enter(); + }); + } else { + ready.set(true); + foo.enter(); } + }); + synchronized (foo) { + thread.start(); + awaitTrue(ready); - // check carrier thread's stack trace - assertTrue(contains(carrierStackTrace, "java.util.concurrent.ForkJoinPool.runWorker")); - assertFalse(contains(carrierStackTrace, "java.lang.Object.wait")); + // wait for thread to block + await(thread, Thread.State.BLOCKED); - // check virtual thread's stack trace - assertFalse(contains(vthreadStackTrace, "java.util.concurrent.ForkJoinPool.runWorker")); - assertTrue(contains(vthreadStackTrace, "java.lang.Object.wait")); + StackTraceElement[] stack = thread.getStackTrace(); + assertTrue(contains(stack, Foo.class.getName() + ".enter")); } + thread.join(); } /** - * Test Thread::getStackTrace on parked thread. + * Test Thread::getStackTrace when thread is waiting in Object.wait. */ - @Test - void testGetStackTrace5() throws Exception { - var thread = Thread.ofVirtual().start(LockSupport::park); - await(thread, Thread.State.WAITING); + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testGetStackTraceWaiting(boolean pinned) throws Exception { + var ready = new AtomicBoolean(); + var thread = Thread.ofVirtual().start(() -> { + synchronized (lock) { + try { + if (pinned) { + VThreadPinner.runPinned(() -> { + ready.set(true); + lock.wait(); + }); + } else { + ready.set(true); + lock.wait(); + } + } catch (InterruptedException e) { } + } + }); try { + // wait for thread to wait + awaitTrue(ready); + await(thread, Thread.State.WAITING); + StackTraceElement[] stack = thread.getStackTrace(); - assertTrue(contains(stack, "LockSupport.park")); + assertTrue(contains(stack, "Object.wait")); } finally { - LockSupport.unpark(thread); + thread.interrupt(); thread.join(); } } /** - * Test Thread::getStackTrace on timed-parked thread. + * Test Thread::getStackTrace when thread is waiting in timed-Object.wait. */ - @Test - void testGetStackTrace6() throws Exception { + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testGetStackTraceTimedWaiting(boolean pinned) throws Exception { + var ready = new AtomicBoolean(); var thread = Thread.ofVirtual().start(() -> { - LockSupport.parkNanos(Long.MAX_VALUE); + synchronized (lock) { + try { + if (pinned) { + VThreadPinner.runPinned(() -> { + ready.set(true); + lock.wait(Long.MAX_VALUE); + }); + } else { + ready.set(true); + lock.wait(Long.MAX_VALUE); + } + } catch (InterruptedException e) { } + } }); - await(thread, Thread.State.TIMED_WAITING); try { + // wait for thread to wait + awaitTrue(ready); + await(thread, Thread.State.TIMED_WAITING); + StackTraceElement[] stack = thread.getStackTrace(); - assertTrue(contains(stack, "LockSupport.parkNanos")); + assertTrue(contains(stack, "Object.wait")); } finally { - LockSupport.unpark(thread); + thread.interrupt(); thread.join(); } } /** - * Test Thread::getStackTrace on parked thread that is pinned. + * Test Thread::getStackTrace when thread in park. */ - @Test - void testGetStackTrace7() throws Exception { - AtomicBoolean done = new AtomicBoolean(); + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testGetStackTraceParked(boolean pinned) throws Exception { + var ready = new AtomicBoolean(); + var done = new AtomicBoolean(); var thread = Thread.ofVirtual().start(() -> { - VThreadPinner.runPinned(() -> { + if (pinned) { + VThreadPinner.runPinned(() -> { + ready.set(true); + while (!done.get()) { + LockSupport.park(); + } + }); + } else { + ready.set(true); while (!done.get()) { LockSupport.park(); } - }); + } }); - await(thread, Thread.State.WAITING); try { + // wait for thread to park + awaitTrue(ready); + await(thread, Thread.State.WAITING); + StackTraceElement[] stack = thread.getStackTrace(); assertTrue(contains(stack, "LockSupport.park")); } finally { @@ -2083,20 +2213,33 @@ void testGetStackTrace7() throws Exception { } /** - * Test Thread::getStackTrace on timed-parked thread that is pinned. + * Test Thread::getStackTrace when thread in timed-park. */ - @Test - void testGetStackTrace8() throws Exception { - AtomicBoolean done = new AtomicBoolean(); + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void testGetStackTraceTimedPark(boolean pinned) throws Exception { + var ready = new AtomicBoolean(); + var done = new AtomicBoolean(); var thread = Thread.ofVirtual().start(() -> { - VThreadPinner.runPinned(() -> { + if (pinned) { + ready.set(true); + VThreadPinner.runPinned(() -> { + while (!done.get()) { + LockSupport.parkNanos(Long.MAX_VALUE); + } + }); + } else { + ready.set(true); while (!done.get()) { LockSupport.parkNanos(Long.MAX_VALUE); } - }); + } }); - await(thread, Thread.State.TIMED_WAITING); try { + // wait for thread to park + awaitTrue(ready); + await(thread, Thread.State.TIMED_WAITING); + StackTraceElement[] stack = thread.getStackTrace(); assertTrue(contains(stack, "LockSupport.parkNanos")); } finally { @@ -2110,7 +2253,7 @@ void testGetStackTrace8() throws Exception { * Test Thread::getStackTrace on terminated thread. */ @Test - void testGetStackTrace9() throws Exception { + void testGetStackTraceTerminated() throws Exception { var thread = Thread.ofVirtual().start(() -> { }); thread.join(); StackTraceElement[] stack = thread.getStackTrace(); @@ -2133,7 +2276,7 @@ void testGetAllStackTraces1() throws Exception { */ @Test void testGetAllStackTraces2() throws Exception { - assumeTrue(ThreadBuilders.supportsCustomScheduler(), "No support for custom schedulers"); + assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); try (ForkJoinPool pool = new ForkJoinPool(1)) { AtomicReference ref = new AtomicReference<>(); Executor scheduler = task -> { @@ -2143,14 +2286,15 @@ void testGetAllStackTraces2() throws Exception { }); }; - Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler); - Thread vthread = builder.start(() -> { + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); + Thread vthread = factory.newThread(() -> { synchronized (lock) { try { lock.wait(); } catch (Exception e) { } } }); + vthread.start(); // get carrier Thread Thread carrier; @@ -2168,8 +2312,9 @@ void testGetAllStackTraces2() throws Exception { synchronized (lock) { lock.notifyAll(); } + vthread.join(); - // get stack trace for the carrier thread + // stack trace for the carrier thread StackTraceElement[] stackTrace = map.get(carrier); assertNotNull(stackTrace); assertTrue(contains(stackTrace, "java.util.concurrent.ForkJoinPool")); @@ -2417,6 +2562,15 @@ Throwable exception() { } } + /** + * Waits for the boolean value to become true. + */ + private static void awaitTrue(AtomicBoolean ref) throws Exception { + while (!ref.get()) { + Thread.sleep(20); + } + } + /** * Waits for the given thread to reach a given state. */ diff --git a/test/jdk/java/lang/Thread/virtual/VirtualThreadPinnedEventThrows.java b/test/jdk/java/lang/Thread/virtual/VirtualThreadPinnedEventThrows.java index 1ae73b280cb..0c5f1c3d6b4 100644 --- a/test/jdk/java/lang/Thread/virtual/VirtualThreadPinnedEventThrows.java +++ b/test/jdk/java/lang/Thread/virtual/VirtualThreadPinnedEventThrows.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2024, 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 @@ -24,7 +24,7 @@ /** * @test * @summary Test parking when pinned and emitting the JFR VirtualThreadPinnedEvent throws - * @modules java.base/jdk.internal.event + * @modules java.base/java.lang:+open java.base/jdk.internal.event * @library /test/lib * @compile/module=java.base jdk/internal/event/VirtualThreadPinnedEvent.java * @run junit/othervm --enable-native-access=ALL-UNNAMED VirtualThreadPinnedEventThrows @@ -36,12 +36,22 @@ import java.util.concurrent.locks.LockSupport; import jdk.internal.event.VirtualThreadPinnedEvent; +import jdk.test.lib.thread.VThreadRunner; import jdk.test.lib.thread.VThreadPinner; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeAll; import static org.junit.jupiter.api.Assertions.*; class VirtualThreadPinnedEventThrows { + @BeforeAll + static void setup() { + // need >=2 carriers for testing pinning when main thread is a virtual thread + if (Thread.currentThread().isVirtual()) { + VThreadRunner.ensureParallelism(2); + } + } + /** * Test parking when pinned and creating the VirtualThreadPinnedEvent fails with OOME. */ diff --git a/test/jdk/java/lang/Thread/virtual/stress/TimedGet.java b/test/jdk/java/lang/Thread/virtual/stress/CompletableFutureTimedGet.java similarity index 79% rename from test/jdk/java/lang/Thread/virtual/stress/TimedGet.java rename to test/jdk/java/lang/Thread/virtual/stress/CompletableFutureTimedGet.java index 60125e1ac56..29ad90895f5 100644 --- a/test/jdk/java/lang/Thread/virtual/stress/TimedGet.java +++ b/test/jdk/java/lang/Thread/virtual/stress/CompletableFutureTimedGet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, 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 @@ -25,17 +25,18 @@ * @test * @summary Stress parking with CompletableFuture timed get * @requires vm.debug != true & vm.continuations - * @run main/othervm -Xmx1g TimedGet 100000 + * @run main/othervm -Xmx1g CompletableFutureTimedGet 100000 */ import java.time.Duration; +import java.time.Instant; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -public class TimedGet { +public class CompletableFutureTimedGet { static final String RESULT = "foo"; @@ -77,30 +78,27 @@ public static void main(String... args) throws InterruptedException { // wait for all threads to terminate long lastTimestamp = System.currentTimeMillis(); - int i = 0; - while (i < threadCount) { - Thread t = threads.get(i); - boolean terminated; - if (t.isAlive()) { - terminated = t.join(Duration.ofMillis(500)); - - // print trace message so the output tracks progress - long currentTime = System.currentTimeMillis(); - if ((currentTime - lastTimestamp) > 500) { - System.out.println(completed.get()); - lastTimestamp = currentTime; + boolean done; + do { + done = true; + for (Thread t : threads) { + if (!t.join(Duration.ofSeconds(1))) { + done = false; } - } else { - terminated = true; } - if (terminated) { - i++; + + // print trace message so the output tracks progress + long currentTime = System.currentTimeMillis(); + if (done || ((currentTime - lastTimestamp) > 500)) { + System.out.format("%s => completed %d of %d%n", + Instant.now(), completed.get(), threadCount); + lastTimestamp = currentTime; } - } + + } while (!done); // all tasks should have completed successfully int completedCount = completed.get(); - System.out.println(completedCount); if (completedCount != threadCount) { throw new RuntimeException("completed = " + completedCount); } diff --git a/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWhenBlocking.java b/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWhenBlocking.java new file mode 100644 index 00000000000..a5aea9ee398 --- /dev/null +++ b/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWhenBlocking.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2023, 2024, 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 + * @summary Stress test Thread.getStackTrace on virtual threads that are blocking or + * blocked on monitorenter + * @requires vm.debug != true + * @modules java.base/java.lang:+open + * @library /test/lib + * @run main/othervm GetStackTraceALotWhenBlocking 500000 + */ + +/* + * @test + * @requires vm.debug == true & vm.continuations + * @modules java.base/java.lang:+open + * @library /test/lib + * @run main/othervm/timeout=300 GetStackTraceALotWhenBlocking 50000 + */ + +import java.time.Instant; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.atomic.AtomicBoolean; +import jdk.test.lib.thread.VThreadRunner; + +public class GetStackTraceALotWhenBlocking { + + public static void main(String[] args) throws Exception { + // need at least two carriers + VThreadRunner.ensureParallelism(2); + + int iterations = args.length > 0 ? Integer.parseInt(args[0]) : 100_000; + + var done = new AtomicBoolean(); + var lock = new Object(); + + Runnable task = () -> { + long count = 0L; + while (!done.get()) { + synchronized (lock) { + pause(); + } + count++; + } + System.out.format("%s %s => %d loops%n", Instant.now(), Thread.currentThread(), count); + }; + + var thread1 = Thread.ofVirtual().start(task); + var thread2 = Thread.ofVirtual().start(task); + try { + for (int i = 1; i <= iterations; i++) { + if ((i % 10_000) == 0) { + System.out.format("%s => %d of %d%n", Instant.now(), i, iterations); + } + + thread1.getStackTrace(); + pause(); + thread2.getStackTrace(); + pause(); + } + } finally { + done.set(true); + thread1.join(); + thread2.join(); + } + } + + private static void pause() { + if (ThreadLocalRandom.current().nextBoolean()) { + Thread.onSpinWait(); + } else { + Thread.yield(); + } + } +} diff --git a/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALot.java b/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWhenParking.java similarity index 85% rename from test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALot.java rename to test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWhenParking.java index c86daf1bc6f..d35f39a991a 100644 --- a/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALot.java +++ b/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWhenParking.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, 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 @@ -23,30 +23,32 @@ /** * @test - * @summary Stress test asynchronous Thread.getStackTrace + * @summary Stress test asynchronous Thread.getStackTrace when parking * @requires vm.debug != true & vm.continuations * @modules java.base/java.lang:+open - * @compile GetStackTraceALot.java ../ThreadBuilders.java - * @run main GetStackTraceALot + * @library /test/lib + * @run main GetStackTraceALotWhenParking */ /** * @test * @requires vm.debug == true & vm.continuations * @modules java.base/java.lang:+open - * @compile GetStackTraceALot.java ../ThreadBuilders.java - * @run main/timeout=300 GetStackTraceALot 1000 + * @library /test/lib + * @run main/timeout=300 GetStackTraceALotWhenParking 1000 */ import java.time.Duration; +import java.time.Instant; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.LockSupport; +import jdk.test.lib.thread.VThreadScheduler; -public class GetStackTraceALot { +public class GetStackTraceALotWhenParking { static class RoundRobinExecutor implements Executor, AutoCloseable { private final ExecutorService[] executors; private int next; @@ -83,7 +85,9 @@ public static void main(String[] args) throws Exception { AtomicInteger count = new AtomicInteger(); try (RoundRobinExecutor executor = new RoundRobinExecutor()) { - Thread thread = ThreadBuilders.virtualThreadBuilder(executor).start(() -> { + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(executor); + + Thread thread = factory.newThread(() -> { while (count.incrementAndGet() < ITERATIONS) { long start = System.nanoTime(); while ((System.nanoTime() - start) < SPIN_NANOS) { @@ -92,6 +96,7 @@ public static void main(String[] args) throws Exception { LockSupport.parkNanos(500_000); } }); + thread.start(); long start = System.nanoTime(); while (thread.isAlive()) { @@ -99,7 +104,7 @@ public static void main(String[] args) throws Exception { // printStackTrace(stackTrace); Thread.sleep(5); if ((System.nanoTime() - start) > 500_000_000) { - System.out.println(count.get()); + System.out.format("%s => %d of %d%n", Instant.now(), count.get(), ITERATIONS); start = System.nanoTime(); } } diff --git a/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWhenPinned.java b/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWhenPinned.java index 35986718a38..d7e6c3a8de4 100644 --- a/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWhenPinned.java +++ b/test/jdk/java/lang/Thread/virtual/stress/GetStackTraceALotWhenPinned.java @@ -28,7 +28,7 @@ * @requires vm.debug != true * @modules java.base/java.lang:+open * @library /test/lib - * @run main/othervm GetStackTraceALotWhenPinned 500000 + * @run main/othervm --enable-native-access=ALL-UNNAMED GetStackTraceALotWhenPinned 500000 */ /* @@ -36,7 +36,7 @@ * @requires vm.debug == true * @modules java.base/java.lang:+open * @library /test/lib - * @run main/othervm/timeout=300 GetStackTraceALotWhenPinned 200000 + * @run main/othervm/timeout=300 --enable-native-access=ALL-UNNAMED GetStackTraceALotWhenPinned 200000 */ import java.time.Instant; @@ -79,7 +79,7 @@ public static void main(String[] args) throws Exception { }); long lastTimestamp = System.currentTimeMillis(); - for (int i = 0; i < iterations; i++) { + for (int i = 1; i <= iterations; i++) { // wait for virtual thread to arrive barrier.await(); @@ -87,8 +87,8 @@ public static void main(String[] args) throws Exception { LockSupport.unpark(thread); long currentTime = System.currentTimeMillis(); - if ((currentTime - lastTimestamp) > 500) { - System.out.format("%s %d remaining ...%n", Instant.now(), (iterations - i)); + if (i == iterations || ((currentTime - lastTimestamp) > 500)) { + System.out.format("%s => %d of %d%n", Instant.now(), i, iterations); lastTimestamp = currentTime; } } diff --git a/test/jdk/java/lang/Thread/virtual/stress/PinALot.java b/test/jdk/java/lang/Thread/virtual/stress/PinALot.java index a420ebb330e..7f95847fc76 100644 --- a/test/jdk/java/lang/Thread/virtual/stress/PinALot.java +++ b/test/jdk/java/lang/Thread/virtual/stress/PinALot.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, 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 @@ -46,17 +46,17 @@ public class PinALot { public static void main(String[] args) throws Exception { - int iterations = 1_000_000; + int iterations; if (args.length > 0) { iterations = Integer.parseInt(args[0]); + } else { + iterations = 1_000_000; } - final int ITERATIONS = iterations; AtomicInteger count = new AtomicInteger(); - Thread thread = Thread.ofVirtual().start(() -> { VThreadPinner.runPinned(() -> { - while (count.incrementAndGet() < ITERATIONS) { + while (count.incrementAndGet() < iterations) { LockSupport.parkNanos(1); } }); @@ -65,12 +65,12 @@ public static void main(String[] args) throws Exception { boolean terminated; do { terminated = thread.join(Duration.ofSeconds(1)); - System.out.println(Instant.now() + " => " + count.get()); + System.out.println(Instant.now() + " => " + count.get() + " of " + iterations); } while (!terminated); int countValue = count.get(); - if (countValue != ITERATIONS) { - throw new RuntimeException("count = " + countValue); + if (countValue != iterations) { + throw new RuntimeException("Thread terminated, count=" + countValue); } } } diff --git a/test/jdk/java/lang/Thread/virtual/stress/PingPong.java b/test/jdk/java/lang/Thread/virtual/stress/PingPong.java index 404444b99ce..2266d192fca 100644 --- a/test/jdk/java/lang/Thread/virtual/stress/PingPong.java +++ b/test/jdk/java/lang/Thread/virtual/stress/PingPong.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, 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 @@ -36,6 +36,7 @@ */ import java.time.Duration; +import java.time.Instant; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.LinkedTransferQueue; import java.util.concurrent.atomic.AtomicInteger; @@ -91,11 +92,12 @@ public static void main(String[] args) throws Exception { boolean terminated; do { terminated = t1.join(Duration.ofMillis(500)); - if (terminated) + if (terminated) { terminated = t2.join(Duration.ofMillis(500)); - System.out.format("%d %s%n", count1.get(), count2.get()); + } + System.out.format("%s => T1 %d of %d, T2 %d of %d%n", + Instant.now(), count1.get(), iterations, count2.get(), iterations); } while (!terminated); - } interface Exchanger { diff --git a/test/jdk/java/lang/Thread/virtual/stress/Skynet.java b/test/jdk/java/lang/Thread/virtual/stress/Skynet.java index ee45fc827b0..5b63fe84b1d 100644 --- a/test/jdk/java/lang/Thread/virtual/stress/Skynet.java +++ b/test/jdk/java/lang/Thread/virtual/stress/Skynet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, 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,7 +69,7 @@ static void skynet(int num, long expected) { long end = System.currentTimeMillis(); System.out.format("Result: %d in %s ms%n", sum, (end-start)); if (sum != expected) - throw new AssertionError("unexpected result!"); + throw new RuntimeException("Expected " + expected); } static void skynet(Channel result, int num, int size, int div) { diff --git a/test/jdk/java/lang/Thread/virtual/stress/SleepALot.java b/test/jdk/java/lang/Thread/virtual/stress/SleepALot.java index 12771ab499a..ed1c985ba7c 100644 --- a/test/jdk/java/lang/Thread/virtual/stress/SleepALot.java +++ b/test/jdk/java/lang/Thread/virtual/stress/SleepALot.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, 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 @@ -41,16 +41,16 @@ public class SleepALot { public static void main(String[] args) throws Exception { - int iterations = 1_000_000; + int iterations; if (args.length > 0) { iterations = Integer.parseInt(args[0]); + } else { + iterations = 1_000_000; } - final int ITERATIONS = iterations; AtomicInteger count = new AtomicInteger(); - Thread thread = Thread.ofVirtual().start(() -> { - while (count.incrementAndGet() < ITERATIONS) { + while (count.incrementAndGet() < iterations) { try { Thread.sleep(Duration.ofNanos(100)); } catch (InterruptedException ignore) { } @@ -60,12 +60,12 @@ public static void main(String[] args) throws Exception { boolean terminated; do { terminated = thread.join(Duration.ofSeconds(1)); - System.out.println(Instant.now() + " => " + count.get()); + System.out.println(Instant.now() + " => " + count.get() + " of " + iterations); } while (!terminated); int countValue = count.get(); - if (countValue != ITERATIONS) { - throw new RuntimeException("count = " + countValue); + if (countValue != iterations) { + throw new RuntimeException("Thread terminated, count=" + countValue); } } } diff --git a/test/jdk/java/lang/Thread/virtual/stress/TimedWaitALot.java b/test/jdk/java/lang/Thread/virtual/stress/TimedWaitALot.java new file mode 100644 index 00000000000..2a2fce62d62 --- /dev/null +++ b/test/jdk/java/lang/Thread/virtual/stress/TimedWaitALot.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2024, 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 id=timeout + * @summary Stress test timed-Object.wait + * @run main/othervm TimedWaitALot 200 + */ + +/* + * @test id=timeout-notify + * @summary Test timed-Object.wait where the waiting thread is awakened with Object.notify + * at around the same time that the timeout expires. + * @run main/othervm TimedWaitALot 200 true false + */ + +/* + * @test id=timeout-interrupt + * @summary Test timed-Object.wait where the waiting thread is awakened with Thread.interrupt + * at around the same time that the timeout expires. + * @run main/othervm TimedWaitALot 200 false true + */ + +/* + * @test id=timeout-notify-interrupt + * @summary Test timed-Object.wait where the waiting thread is awakened with Object.notify + * and Thread.interrupt at around the same time that the timeout expires. + * @run main/othervm TimedWaitALot 100 true true + */ + +import java.time.Instant; +import java.util.concurrent.Executors; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.ThreadLocalRandom; + +public class TimedWaitALot { + public static void main(String[] args) throws Exception { + int iterations = Integer.parseInt(args[0]); + boolean notify = args.length >= 2 && "true".equals(args[1]); + boolean interrupt = args.length >=3 && "true".equals(args[2]); + + // test all timeouts concurrently + int[] timeouts = { 10, 20, 50, 100 }; + for (int i = 1; i <= iterations; i++) { + System.out.println(Instant.now() + " => " + i + " of " + iterations); + test(notify, interrupt, timeouts); + } + } + + /** + * Start a first virtual thread to wait in Object.wait(millis). + * If {@code notify} is true, start a virtual thread to use Object.notifyAll at around + * the same time that the timeout expires. + * If {@code interrupt} is true, start virtual thread to interrupts the first virtual + * thread at around the same time as the timeout expires. + */ + static void test(boolean notify, boolean interrupt, int... timeouts) throws Exception { + try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { + for (int timeout : timeouts) { + var queue = new SynchronousQueue(); + var lock = new Object(); + + // virtual thread waits with Object.wait(timeout) + executor.submit(() -> { + queue.put(Thread.currentThread()); + synchronized (lock) { + lock.wait(timeout); + } + return null; + }); + + // wait for thread to start + Thread thread = queue.take(); + + // start thread to Object.notifyAll at around time that the timeout expires + if (notify) { + if (ThreadLocalRandom.current().nextBoolean()) { + synchronized (lock) { + sleepLessThan(timeout); + lock.notifyAll(); + } + } else { + sleepLessThan(timeout); + synchronized (lock) { + lock.notifyAll(); + } + } + } + + // start thread to interrupt first thread at around time that the timeout expires + if (interrupt) { + executor.submit(() -> { + sleepLessThan(timeout); + thread.interrupt(); + return null; + }); + } + } + } + } + + /** + * Sleeps for just less than the given timeout, in millis. + */ + private static void sleepLessThan(long timeout) throws InterruptedException { + int delta = ThreadLocalRandom.current().nextInt(10); + Thread.sleep(timeout - delta); + } +} diff --git a/test/jdk/java/lang/Thread/virtual/stress/YieldALot.java b/test/jdk/java/lang/Thread/virtual/stress/YieldALot.java index 3eabfff1c4c..b793993a1b7 100644 --- a/test/jdk/java/lang/Thread/virtual/stress/YieldALot.java +++ b/test/jdk/java/lang/Thread/virtual/stress/YieldALot.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, 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 @@ -25,7 +25,7 @@ * @test * @summary Stress test Thread.yield * @requires vm.debug != true - * @run main YieldALot 350000 + * @run main YieldALot 500000 */ /* @@ -35,34 +35,35 @@ */ import java.time.Duration; +import java.time.Instant; import java.util.concurrent.atomic.AtomicInteger; public class YieldALot { public static void main(String[] args) throws Exception { - int iterations = 1_000_000; + int iterations; if (args.length > 0) { iterations = Integer.parseInt(args[0]); + } else { + iterations = 1_000_000; } - final int ITERATIONS = iterations; AtomicInteger count = new AtomicInteger(); - Thread thread = Thread.ofVirtual().start(() -> { - while (count.incrementAndGet() < ITERATIONS) { + while (count.incrementAndGet() < iterations) { Thread.yield(); } }); boolean terminated; do { - terminated = thread.join(Duration.ofMillis(500)); - System.out.println(count.get()); + terminated = thread.join(Duration.ofSeconds(1)); + System.out.println(Instant.now() + " => " + count.get() + " of " + iterations); } while (!terminated); int countValue = count.get(); - if (countValue != ITERATIONS) { - throw new RuntimeException("count = " + countValue); + if (countValue != iterations) { + throw new RuntimeException("Thread terminated, count=" + countValue); } } } diff --git a/test/jdk/java/lang/instrument/RetransformRecordAnnotation.java b/test/jdk/java/lang/instrument/RetransformRecordAnnotation.java deleted file mode 100644 index 90d76d1f923..00000000000 --- a/test/jdk/java/lang/instrument/RetransformRecordAnnotation.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (c) 2024, 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 8315575 8328137 - * @summary test that records with invisible annotation can be retransformed - * - * @library /test/lib - * @run shell MakeJAR.sh retransformAgent - * @run main/othervm -javaagent:retransformAgent.jar -Xlog:redefine+class=trace RetransformRecordAnnotation - * @run main/othervm -javaagent:retransformAgent.jar -XX:+PreserveAllAnnotations -Xlog:redefine+class=trace RetransformRecordAnnotation - */ - -import java.io.File; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.lang.instrument.ClassFileTransformer; -import java.nio.file.Files; -import java.security.ProtectionDomain; - -public class RetransformRecordAnnotation extends AInstrumentationTestCase { - - @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) - @Retention(RetentionPolicy.RUNTIME) - @interface RuntimeTypeAnno {} - - @Retention(RetentionPolicy.RUNTIME) - @interface RuntimeParamAnno { - String s() default "foo"; - } - - @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) - @Retention(RetentionPolicy.CLASS) - @interface ClassTypeAnno {} - - @Retention(RetentionPolicy.CLASS) - @interface ClassParamAnno { - String s() default "bar"; - } - - @RuntimeTypeAnno - @RuntimeParamAnno(s = "1") - public record VisibleAnnos(@RuntimeTypeAnno @RuntimeParamAnno(s = "2") Object o, Object other) { - } - - @ClassTypeAnno - @ClassParamAnno(s = "3") - public record InvisibleAnnos(@ClassTypeAnno @ClassParamAnno(s = "4") Object o, Object other) { - } - - @RuntimeTypeAnno - @RuntimeParamAnno(s = "5") - @ClassTypeAnno - @ClassParamAnno(s = "6") - public record MixedAnnos(@RuntimeTypeAnno @RuntimeParamAnno(s = "7") - @ClassTypeAnno @ClassParamAnno(s = "8") Object o, Object other) { - } - - public static void main (String[] args) throws Throwable { - ATestCaseScaffold test = new RetransformRecordAnnotation(); - test.beVerbose(); - test.runTest(); - } - - private Transformer transformer; - - public RetransformRecordAnnotation() throws Throwable { - super("RetransformRecordAnnotation"); - } - - private void log(Object o) { - System.out.println(String.valueOf(o)); - } - - // Retransforms target class using provided class bytes; - private void retransform(Class targetClass, byte[] classBytes) throws Throwable { - transformer.prepare(targetClass, classBytes); - fInst.retransformClasses(targetClass); - assertTrue(targetClass.getName() + " was not seen by transform()", - transformer.getSeenClassBytes() != null); - } - - protected final void doRunTest() throws Throwable { - transformer = new Transformer(); - fInst.addTransformer(transformer, true); - - { - log("Sanity: retransform to original class bytes"); - retransform(InvisibleAnnos.class, loadClassBytes(InvisibleAnnos.class)); - log(""); - } - - // The following testcases use null as new class bytes (i.e. no transform is performed). - // However, it is enough for testing purposes as the JvmtiClassFileReconstituter is still involved - // in preparation of the initial class bytes. - { - log("Test: retransform VisibleAnnos to null"); - retransform(VisibleAnnos.class, null); - log(""); - } - - { - log("Test: retransform InvisibleAnnos to null"); - retransform(InvisibleAnnos.class, null); - log(""); - } - - { - log("Test: retransform MixedAnnos to null"); - retransform(MixedAnnos.class, null); - log(""); - } - } - - private byte[] loadClassBytes(Class cls) throws Exception { - String classFileName = cls.getName() + ".class"; - File classFile = new File(System.getProperty("test.classes", "."), classFileName); - log("Reading test class from " + classFile); - byte[] classBytes = Files.readAllBytes(classFile.toPath()); - log("Read " + classBytes.length + " bytes."); - return classBytes; - } - - public class Transformer implements ClassFileTransformer { - private String targetClassName; - private byte[] seenClassBytes; - private byte[] newClassBytes; - - public Transformer() { - } - - // Prepares transformer for Instrumentation.retransformClasses. - public void prepare(Class targetClass, byte[] classBytes) { - targetClassName = targetClass.getName(); - newClassBytes = classBytes; - seenClassBytes = null; - } - - byte[] getSeenClassBytes() { - return seenClassBytes; - } - - public String toString() { - return Transformer.this.getClass().getName(); - } - - public byte[] transform(ClassLoader loader, String className, - Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { - - if (className.equals(targetClassName)) { - log(this + ".transform() sees '" + className - + "' of " + classfileBuffer.length + " bytes."); - seenClassBytes = classfileBuffer; - if (newClassBytes != null) { - log(this + ".transform() sets new classbytes for '" + className - + "' of " + newClassBytes.length + " bytes."); - } - return newClassBytes; - } - - return null; - } - } -} diff --git a/test/jdk/java/lang/management/ThreadMXBean/LockedMonitorInNative.java b/test/jdk/java/lang/management/ThreadMXBean/LockedMonitorInNative.java new file mode 100644 index 00000000000..e1e490aa040 --- /dev/null +++ b/test/jdk/java/lang/management/ThreadMXBean/LockedMonitorInNative.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2024, 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 + * @summary Test ThreadMXBean.getLockedMonitors returns information about an object + * monitor lock entered with a synchronized native method or JNI MonitorEnter + * @run junit/othervm LockedMonitorInNative + */ + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.util.Arrays; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeAll; +import static org.junit.jupiter.api.Assertions.*; + +public class LockedMonitorInNative { + + @BeforeAll + static void setup() throws Exception { + System.loadLibrary("LockedMonitorInNative"); + } + + /** + * Test ThreadMXBean.getLockedMonitors returns information about an object + * monitor lock entered with a synchronized native method. + */ + @Test + void testSynchronizedNative() { + Object lock = this; + runWithSynchronizedNative(() -> { + assertTrue(holdsLock(lock), "Thread does not hold lock"); + }); + } + + /** + * Test ThreadMXBean.getLockedMonitors returns information about an object + * monitor lock entered with JNI MonitorEnter. + */ + @Test + void testMonitorEnteredInNative() { + var lock = new Object(); + runWithMonitorEnteredInNative(lock, () -> { + assertTrue(holdsLock(lock), "Thread does not hold lock"); + }); + } + + private boolean holdsLock(Object lock) { + int hc = System.identityHashCode(lock); + long tid = Thread.currentThread().threadId(); + ThreadInfo ti = ManagementFactory.getPlatformMXBean(ThreadMXBean.class) + .getThreadInfo(new long[] { tid }, true, true)[0]; + return Arrays.stream(ti.getLockedMonitors()) + .anyMatch(mi -> mi.getIdentityHashCode() == hc); + } + + /** + * Invokes the given task's run method while holding the monitor for "this". + */ + private synchronized native void runWithSynchronizedNative(Runnable task); + + /** + * Invokes the given task's run method while holding the monitor for the given + * object. The monitor is entered with JNI MonitorEnter, and exited with JNI MonitorExit. + */ + private native void runWithMonitorEnteredInNative(Object lock, Runnable task); + + /** + * Called from native methods to run the given task. + */ + private void run(Runnable task) { + task.run(); + } +} diff --git a/test/jdk/java/lang/management/ThreadMXBean/VirtualThreads.java b/test/jdk/java/lang/management/ThreadMXBean/VirtualThreads.java index 35fe819313a..7830c93ac13 100644 --- a/test/jdk/java/lang/management/ThreadMXBean/VirtualThreads.java +++ b/test/jdk/java/lang/management/ThreadMXBean/VirtualThreads.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2024, 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 @@ -41,19 +41,20 @@ import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; import java.nio.channels.Selector; import java.util.Arrays; import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.LockSupport; import java.util.stream.Collectors; +import jdk.test.lib.thread.VThreadPinner; import jdk.test.lib.thread.VThreadRunner; +import jdk.test.lib.thread.VThreadScheduler; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -195,7 +196,7 @@ void testGetThreadInfo4(int maxDepth) { */ @Test void testGetThreadInfoCarrierThread() throws Exception { - assumeTrue(supportsCustomScheduler(), "No support for custom schedulers"); + assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); try (ExecutorService pool = Executors.newFixedThreadPool(1)) { var carrierRef = new AtomicReference(); Executor scheduler = (task) -> { @@ -204,19 +205,30 @@ void testGetThreadInfoCarrierThread() throws Exception { task.run(); }); }; + ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); // start virtual thread so carrier Thread can be captured - virtualThreadBuilder(scheduler).start(() -> { }).join(); + Thread thread = factory.newThread(() -> { }); + thread.start(); + thread.join(); Thread carrier = carrierRef.get(); assertTrue(carrier != null && !carrier.isVirtual()); try (Selector sel = Selector.open()) { - // start virtual thread that blocks in a native method - virtualThreadBuilder(scheduler).start(() -> { + String selClassName = sel.getClass().getName(); + + // start virtual thread that blocks while pinned + Thread vthread = factory.newThread(() -> { try { - sel.select(); + VThreadPinner.runPinned(sel::select); } catch (Exception e) { } }); + vthread.start(); + + // wait for virtual thread to block in select + while (!contains(vthread.getStackTrace(), selClassName)) { + Thread.sleep(20); + } // invoke getThreadInfo get and check the stack trace long tid = carrier.getId(); @@ -225,7 +237,7 @@ void testGetThreadInfoCarrierThread() throws Exception { // should only see carrier frames StackTraceElement[] stack = info.getStackTrace(); assertTrue(contains(stack, "java.util.concurrent.ThreadPoolExecutor")); - assertFalse(contains(stack, "java.nio.channels.Selector")); + assertFalse(contains(stack, selClassName)); // carrier should not be holding any monitors assertEquals(0, info.getLockedMonitors().length); @@ -351,40 +363,4 @@ private static boolean contains(StackTraceElement[] stack, String className) { .map(StackTraceElement::getClassName) .anyMatch(className::equals); } - - /** - * Returns a builder to create virtual threads that use the given scheduler. - * @throws UnsupportedOperationException if there is no support for custom schedulers - */ - private static Thread.Builder.OfVirtual virtualThreadBuilder(Executor scheduler) { - Thread.Builder.OfVirtual builder = Thread.ofVirtual(); - try { - Class clazz = Class.forName("java.lang.ThreadBuilders$VirtualThreadBuilder"); - Constructor ctor = clazz.getDeclaredConstructor(Executor.class); - ctor.setAccessible(true); - return (Thread.Builder.OfVirtual) ctor.newInstance(scheduler); - } catch (InvocationTargetException e) { - Throwable cause = e.getCause(); - if (cause instanceof RuntimeException re) { - throw re; - } - throw new RuntimeException(e); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - /** - * Return true if custom schedulers are supported. - */ - private static boolean supportsCustomScheduler() { - try (var pool = Executors.newCachedThreadPool()) { - try { - virtualThreadBuilder(pool); - return true; - } catch (UnsupportedOperationException e) { - return false; - } - } - } } diff --git a/test/jdk/java/lang/management/ThreadMXBean/libLockedMonitorInNative.c b/test/jdk/java/lang/management/ThreadMXBean/libLockedMonitorInNative.c new file mode 100644 index 00000000000..20975188907 --- /dev/null +++ b/test/jdk/java/lang/management/ThreadMXBean/libLockedMonitorInNative.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024, 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. + */ + +#include "jni.h" + +JNIEXPORT void JNICALL +Java_LockedMonitorInNative_runWithSynchronizedNative(JNIEnv *env, jobject obj, jobject task) { + jclass clazz = (*env)->GetObjectClass(env, obj); + jmethodID mid = (*env)->GetMethodID(env, clazz, "run", "(Ljava/lang/Runnable;)V"); + if (mid != NULL) { + (*env)->CallVoidMethod(env, obj, mid, task); + } +} + +JNIEXPORT void JNICALL +Java_LockedMonitorInNative_runWithMonitorEnteredInNative(JNIEnv *env, jobject obj, jobject lock, jobject task) { + jclass clazz = (*env)->GetObjectClass(env, obj); + jmethodID mid = (*env)->GetMethodID(env, clazz, "run", "(Ljava/lang/Runnable;)V"); + if (mid != NULL && (*env)->MonitorEnter(env, lock) == 0) { + (*env)->CallVoidMethod(env, obj, mid, task); + (*env)->MonitorExit(env, lock); // can be called with pending exception + } +} diff --git a/test/jdk/javax/swing/JColorChooser/Test6977726.java b/test/jdk/javax/swing/JColorChooser/Test6977726.java index a79931c93eb..1ec72036f14 100644 --- a/test/jdk/javax/swing/JColorChooser/Test6977726.java +++ b/test/jdk/javax/swing/JColorChooser/Test6977726.java @@ -41,7 +41,14 @@ public static void main(String[] args) throws Exception { String instructions = """ Check that there is a panel with "Text Preview Panel" text and with title "Preview" in the JColorChooser. - Test passes if the panel is as described, test fails otherwise."""; + Test passes if the panel is as described, test fails otherwise. + + Note: "Preview" title is not applicable for GTK Look and Feel."""; + + // In case this test is run with GTK L&F, the preview panel title + // is missing due to the "ColorChooser.showPreviewPanelText" property + // which is set to "Boolean.FALSE" for GTK L&F. Test instructions are + // modified to reflect that "Preview" title is not applicable for GTK L&F. PassFailJFrame.builder() .title("Test6977726") diff --git a/test/jdk/javax/swing/text/html/HTMLDocument/HTMLTextDecorationNone.java b/test/jdk/javax/swing/text/html/HTMLDocument/HTMLTextDecorationNone.java new file mode 100644 index 00000000000..318626e5110 --- /dev/null +++ b/test/jdk/javax/swing/text/html/HTMLDocument/HTMLTextDecorationNone.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2024, 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. + */ + +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +import javax.imageio.ImageIO; +import javax.swing.JEditorPane; +import javax.swing.text.View; +import javax.swing.text.html.CSS; + +/* + * @test + * @bug 8335967 + * @summary Tests 'text-decoration: none' is respected + * @run main HTMLTextDecorationNone + */ +public final class HTMLTextDecorationNone { + private static final String HTML = """ + + + + + text-decoration: none (<a>) + + + +

underlined

+

not underlined

+

not underlined

+

underlined?

+

underlined?

+ + + """; + + private static final boolean[] underlined = {true, false, false, true, true}; + + public static void main(String[] args) { + final JEditorPane html = new JEditorPane("text/html", HTML); + html.setEditable(false); + + final Dimension size = html.getPreferredSize(); + html.setSize(size); + + BufferedImage image = new BufferedImage(size.width, size.height, + BufferedImage.TYPE_INT_RGB); + Graphics g = image.createGraphics(); + // Paint the editor pane to ensure all views are created + html.paint(g); + g.dispose(); + + int errorCount = 0; + String firstError = null; + + System.out.println("----- Views -----"); + final View bodyView = html.getUI() + .getRootView(html) + .getView(1) + .getView(1); + for (int i = 0; i < bodyView.getViewCount(); i++) { + View pView = bodyView.getView(i); + View contentView = getContentView(pView); + + Object decorationAttr = + contentView.getAttributes() + .getAttribute(CSS.Attribute.TEXT_DECORATION); + String decoration = decorationAttr == null + ? "none" : decorationAttr.toString(); + + System.out.println(i + ": " + decoration); + if (decoration.contains("underline") != underlined[i]) { + errorCount++; + if (firstError == null) { + firstError = "Line " + i + ": " + decoration + " vs " + + (underlined[i] ? "underline" : "none"); + } + } + } + + if (errorCount > 0) { + saveImage(image); + throw new RuntimeException(errorCount + " error(s) found, " + + "the first one: " + firstError); + } + } + + private static View getContentView(View parent) { + View view = parent.getView(0); + return view.getViewCount() > 0 + ? getContentView(view) + : view; + } + + private static void saveImage(BufferedImage image) { + try { + ImageIO.write(image, "png", + new File("html.png")); + } catch (IOException ignored) { } + } +} diff --git a/test/jdk/jdk/classfile/AccessFlagsTest.java b/test/jdk/jdk/classfile/AccessFlagsTest.java index 97c0d97f8f9..379dcddb9c8 100644 --- a/test/jdk/jdk/classfile/AccessFlagsTest.java +++ b/test/jdk/jdk/classfile/AccessFlagsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, 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 @@ -26,6 +26,9 @@ * @summary Testing ClassFile AccessFlags. * @run junit AccessFlagsTest */ +import java.lang.classfile.ClassFile; +import java.lang.constant.ClassDesc; +import java.util.Arrays; import java.util.EnumSet; import java.util.Random; import java.util.Set; @@ -34,6 +37,10 @@ import java.util.function.IntFunction; import java.lang.reflect.AccessFlag; import java.lang.classfile.AccessFlags; + +import static java.lang.classfile.ClassFile.ACC_STATIC; +import static java.lang.constant.ConstantDescs.CD_int; +import static java.lang.constant.ConstantDescs.MTD_void; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.provider.EnumSource; @@ -45,15 +52,38 @@ class AccessFlagsTest { @EnumSource(names = { "CLASS", "METHOD", "FIELD" }) void testRandomAccessFlagsConverions(AccessFlag.Location ctx) { IntFunction intFactory = switch (ctx) { - case CLASS -> AccessFlags::ofClass; - case METHOD -> AccessFlags::ofMethod; - case FIELD -> AccessFlags::ofField; + case CLASS -> v -> { + var bytes = ClassFile.of().build(ClassDesc.of("Test"), clb -> clb.withFlags(v)); + return ClassFile.of().parse(bytes).flags(); + }; + case METHOD -> v -> { + var bytes = ClassFile.of().build(ClassDesc.of("Test"), clb -> + clb.withMethod("test", MTD_void, v & ACC_STATIC, mb -> mb.withFlags(v))); + return ClassFile.of().parse(bytes).methods().getFirst().flags(); + }; + case FIELD -> v -> { + var bytes = ClassFile.of().build(ClassDesc.of("Test"), clb -> + clb.withField("test", CD_int, fb -> fb.withFlags(v))); + return ClassFile.of().parse(bytes).fields().getFirst().flags(); + }; default -> null; }; Function flagsFactory = switch (ctx) { - case CLASS -> AccessFlags::ofClass; - case METHOD -> AccessFlags::ofMethod; - case FIELD -> AccessFlags::ofField; + case CLASS -> v -> { + var bytes = ClassFile.of().build(ClassDesc.of("Test"), clb -> clb.withFlags(v)); + return ClassFile.of().parse(bytes).flags(); + }; + case METHOD -> v -> { + boolean hasStatic = Arrays.stream(v).anyMatch(f -> f == AccessFlag.STATIC); + var bytes = ClassFile.of().build(ClassDesc.of("Test"), clb -> + clb.withMethod("test", MTD_void, hasStatic ? ACC_STATIC : 0, mb -> mb.withFlags(v))); + return ClassFile.of().parse(bytes).methods().getFirst().flags(); + }; + case FIELD -> v -> { + var bytes = ClassFile.of().build(ClassDesc.of("Test"), clb -> + clb.withField("test", CD_int, fb -> fb.withFlags(v))); + return ClassFile.of().parse(bytes).fields().getFirst().flags(); + }; default -> null; }; @@ -72,11 +102,11 @@ void testRandomAccessFlagsConverions(AccessFlag.Location ctx) { @Test void testInvalidFlagsUse() { - assertAll( - () -> assertThrowsForInvalidFlagsUse(AccessFlags::ofClass), - () -> assertThrowsForInvalidFlagsUse(AccessFlags::ofField), - () -> assertThrowsForInvalidFlagsUse(AccessFlags::ofMethod) - ); + ClassFile.of().build(ClassDesc.of("Test"), clb -> { + assertThrowsForInvalidFlagsUse(clb::withFlags); + clb.withMethod("test", MTD_void, ACC_STATIC, mb -> assertThrowsForInvalidFlagsUse(mb::withFlags)); + clb.withField("test", CD_int, fb -> assertThrowsForInvalidFlagsUse(fb::withFlags)); + }); } void assertThrowsForInvalidFlagsUse(Consumer factory) { diff --git a/test/jdk/jdk/classfile/BuilderBlockTest.java b/test/jdk/jdk/classfile/BuilderBlockTest.java index c8e13b79f72..30a44491e6c 100644 --- a/test/jdk/jdk/classfile/BuilderBlockTest.java +++ b/test/jdk/jdk/classfile/BuilderBlockTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, 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 @@ -23,36 +23,49 @@ /* * @test + * @bug 8337225 * @summary Testing ClassFile builder blocks. * @run junit BuilderBlockTest */ +import java.io.IOException; +import java.lang.classfile.Attributes; +import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassTransform; +import java.lang.classfile.CodeBuilder; +import java.lang.classfile.CodeElement; +import java.lang.classfile.CodeTransform; +import java.lang.classfile.Label; +import java.lang.classfile.MethodModel; +import java.lang.classfile.MethodTransform; +import java.lang.classfile.Opcode; +import java.lang.classfile.TypeKind; +import java.lang.classfile.constantpool.StringEntry; +import java.lang.classfile.instruction.ConstantInstruction; import java.lang.constant.ClassDesc; - -import static java.lang.constant.ConstantDescs.*; - import java.lang.constant.MethodTypeDesc; +import java.lang.reflect.AccessFlag; import java.lang.reflect.Method; +import java.net.URI; import java.nio.file.Path; -import java.nio.file.Paths; import helpers.ByteArrayClassLoader; -import java.lang.classfile.AccessFlags; -import java.lang.reflect.AccessFlag; -import java.lang.classfile.ClassFile; -import java.lang.classfile.Label; -import java.lang.classfile.Opcode; -import java.lang.classfile.TypeKind; import jdk.internal.classfile.impl.LabelImpl; -import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; +import static java.lang.classfile.ClassFile.ACC_PUBLIC; +import static java.lang.classfile.ClassFile.ACC_STATIC; +import static java.lang.constant.ConstantDescs.CD_int; +import static java.lang.constant.ConstantDescs.CD_void; + +import static org.junit.jupiter.api.Assertions.*; + /** * BuilderBlockTest */ class BuilderBlockTest { - static final String testClassName = "AdaptCodeTest$TestClass"; - static final Path testClassPath = Paths.get("target/test-classes/" + testClassName + ".class"); + static final String testClassName = "BuilderBlockTest$TestClass"; + static final Path testClassPath = Path.of(URI.create(BuilderBlockTest.class.getResource(testClassName + ".class").toString())); @Test void testStartEnd() throws Exception { @@ -104,7 +117,7 @@ void testIfThenReturn() throws Exception { byte[] bytes = ClassFile.of().build(ClassDesc.of("Foo"), cb -> { cb.withFlags(AccessFlag.PUBLIC); cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int), - AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), + ACC_PUBLIC | ACC_STATIC, mb -> mb.withCode(xb -> xb.iload(0) .ifThen(xxb -> xxb.iconst_1().ireturn()) .iconst_2() @@ -123,7 +136,7 @@ void testIfThenElseReturn() throws Exception { byte[] bytes = ClassFile.of().build(ClassDesc.of("Foo"), cb -> { cb.withFlags(AccessFlag.PUBLIC); cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int), - AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), + ACC_PUBLIC | ACC_STATIC, mb -> mb.withCode(xb -> xb.iload(0) .ifThenElse(xxb -> xxb.iconst_1().ireturn(), xxb -> xxb.iconst_2().ireturn()))); @@ -141,7 +154,7 @@ void testIfThenBadOpcode() { ClassFile.of().build(ClassDesc.of("Foo"), cb -> { cb.withFlags(AccessFlag.PUBLIC); cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int, CD_int), - AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), + ACC_PUBLIC | ACC_STATIC, mb -> mb.withCode(xb -> { xb.iload(0); xb.iload(1); @@ -161,7 +174,7 @@ void testIfThenElseImplicitBreak() throws Exception { byte[] bytes = ClassFile.of().build(ClassDesc.of("Foo"), cb -> { cb.withFlags(AccessFlag.PUBLIC); cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int), - AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), + ACC_PUBLIC | ACC_STATIC, mb -> mb.withCode(xb -> xb.iload(0) .ifThenElse(xxb -> xxb.iconst_1().istore(2), xxb -> xxb.iconst_2().istore(2)) @@ -181,7 +194,7 @@ void testIfThenElseExplicitBreak() throws Exception { byte[] bytes = ClassFile.of().build(ClassDesc.of("Foo"), cb -> { cb.withFlags(AccessFlag.PUBLIC); cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int), - AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), + ACC_PUBLIC | ACC_STATIC, mb -> mb.withCode(xb -> xb.iload(0) .ifThenElse(xxb -> xxb.iconst_1().istore(2).goto_(xxb.breakLabel()), xxb -> xxb.iconst_2().istore(2).goto_(xxb.breakLabel())) @@ -200,7 +213,7 @@ void testIfThenElseOpcode() throws Exception { byte[] bytes = ClassFile.of().build(ClassDesc.of("Foo"), cb -> { cb.withFlags(AccessFlag.PUBLIC); cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int, CD_int), - AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), + ACC_PUBLIC | ACC_STATIC, mb -> mb.withCode(xb -> xb.iload(0) .iload(1) @@ -225,7 +238,7 @@ void testIfThenElseBadOpcode() { ClassFile.of().build(ClassDesc.of("Foo"), cb -> { cb.withFlags(AccessFlag.PUBLIC); cb.withMethod("foo", MethodTypeDesc.of(CD_int, CD_int, CD_int), - AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), + ACC_PUBLIC | ACC_STATIC, mb -> mb.withCode(xb -> { xb.iload(0); xb.iload(1); @@ -305,4 +318,81 @@ void testAllocateLocalIfThen() { })); }); } + + private static final CodeTransform ALLOCATE_LOCAL_EXAMINER = CodeTransform.ofStateful(() -> new CodeTransform() { + boolean foundItem = false; + + @Override + public void atStart(CodeBuilder builder) { + foundItem = false; + } + + @Override + public void accept(CodeBuilder cob, CodeElement coe) { + cob.with(coe); + if (coe instanceof ConstantInstruction.LoadConstantInstruction ldc + && ldc.constantEntry() instanceof StringEntry se + && se.utf8().equalsString("Output")) { + assertFalse(foundItem); + foundItem = true; + var i = cob.allocateLocal(TypeKind.IntType); + assertEquals(7, i, "Allocated new int slot"); + } + } + + @Override + public void atEnd(CodeBuilder builder) { + assertTrue(foundItem); + } + }); + + // Test updating local variable slot management from + // source code models in transformingCode; + // CodeBuilder.transform(CodeModel, CodeTransform) is + // not managed for now + @Test + void testAllocateLocalTransformingCodeAttribute() throws IOException { + var cf = ClassFile.of(); + var code = cf.parse(testClassPath) + .methods() + .stream() + .filter(f -> f.methodName().equalsString("work")) + .findFirst() + .orElseThrow() + .findAttribute(Attributes.code()) + .orElseThrow(); + ClassFile.of().build(ClassDesc.of("Foo"), cb -> cb + .withMethod("foo", MethodTypeDesc.ofDescriptor("(IJI)V"), 0, mb -> mb + .transformCode(code, ALLOCATE_LOCAL_EXAMINER))); + } + + @Test + void testAllocateLocalTransformingBufferedCode() throws IOException { + var cf = ClassFile.of(); + var testClass = cf.parse(testClassPath); + ClassTransform bufferingTransform = (clb, cle) -> { + if (cle instanceof MethodModel mm && mm.methodName().equalsString("work")) { + clb.withMethodBody(mm.methodName(), mm.methodType(), mm.flags().flagsMask(), cob -> { + int d = cob.allocateLocal(TypeKind.IntType); + int e = cob.allocateLocal(TypeKind.IntType); + + assertEquals(5, d); + assertEquals(6, e); + + mm.code().ifPresent(code -> code.forEach(cob)); + }); + } + }; + cf.transformClass(testClass, bufferingTransform.andThen(ClassTransform.transformingMethods(MethodTransform.transformingCode(ALLOCATE_LOCAL_EXAMINER)))); + } + + public static class TestClass { + public void work(int a, long b, int c) { + int d = Math.addExact(a, 25); + int e = Math.multiplyExact(d, c); + System.out.println("Output"); + System.out.println(e + b); + throw new IllegalArgumentException("foo"); + } + } } diff --git a/test/jdk/jdk/classfile/BuilderTryCatchTest.java b/test/jdk/jdk/classfile/BuilderTryCatchTest.java index 666b36e11a7..192125c4bb6 100644 --- a/test/jdk/jdk/classfile/BuilderTryCatchTest.java +++ b/test/jdk/jdk/classfile/BuilderTryCatchTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, 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 @@ -27,7 +27,6 @@ * @run junit BuilderTryCatchTest */ -import java.lang.classfile.AccessFlags; import java.lang.classfile.ClassFile; import java.lang.classfile.CodeBuilder; import java.lang.classfile.CompoundElement; @@ -35,6 +34,9 @@ import java.lang.classfile.TypeKind; import java.lang.classfile.instruction.BranchInstruction; import java.lang.classfile.instruction.ExceptionCatch; + +import static java.lang.classfile.ClassFile.ACC_PUBLIC; +import static java.lang.classfile.ClassFile.ACC_STATIC; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; @@ -43,7 +45,6 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; -import java.lang.reflect.AccessFlag; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; @@ -184,7 +185,7 @@ void testTryEmptyCatch() { void testEmptyTry() { byte[] bytes = ClassFile.of().build(ClassDesc.of("C"), cb -> { cb.withMethod("main", MethodTypeDesc.of(CD_String, CD_String.arrayType()), - AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), mb -> { + ACC_PUBLIC | ACC_STATIC, mb -> { mb.withCode(xb -> { int stringSlot = xb.allocateLocal(TypeKind.ReferenceType); xb.loadConstant("S"); @@ -215,7 +216,7 @@ void testEmptyTry() { void testLocalAllocation() throws Throwable { byte[] bytes = ClassFile.of().build(ClassDesc.of("C"), cb -> { cb.withMethod("main", MethodTypeDesc.of(CD_String, CD_String.arrayType()), - AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), mb -> { + ACC_PUBLIC | ACC_STATIC, mb -> { mb.withCode(xb -> { int stringSlot = xb.allocateLocal(TypeKind.ReferenceType); xb.loadConstant("S"); @@ -278,7 +279,7 @@ void testLocalAllocation() throws Throwable { static byte[] generateTryCatchMethod(Consumer c) { byte[] bytes = ClassFile.of().build(ClassDesc.of("C"), cb -> { cb.withMethod("main", MethodTypeDesc.of(CD_String, CD_String.arrayType()), - AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), mb -> { + ACC_PUBLIC | ACC_STATIC, mb -> { mb.withCode(xb -> { int stringSlot = xb.allocateLocal(TypeKind.ReferenceType); xb.loadConstant("S"); diff --git a/test/jdk/jdk/classfile/CorpusTest.java b/test/jdk/jdk/classfile/CorpusTest.java index a2af1667978..631e2f8afaa 100644 --- a/test/jdk/jdk/classfile/CorpusTest.java +++ b/test/jdk/jdk/classfile/CorpusTest.java @@ -39,6 +39,7 @@ import org.junit.jupiter.api.parallel.ExecutionMode; import java.io.ByteArrayInputStream; +import java.lang.classfile.attribute.CodeAttribute; import java.util.*; import static helpers.ClassRecord.assertEqualsDeep; @@ -222,9 +223,11 @@ void testNullAdaptations(Path path) throws Exception { var m1 = itStack.next(); var m2 = itNoStack.next(); var text1 = m1.methodName().stringValue() + m1.methodType().stringValue() + ": " - + m1.code().map(c -> c.maxLocals() + " / " + c.maxStack()).orElse("-"); + + m1.code().map(CodeAttribute.class::cast) + .map(c -> c.maxLocals() + " / " + c.maxStack()).orElse("-"); var text2 = m2.methodName().stringValue() + m2.methodType().stringValue() + ": " - + m2.code().map(c -> c.maxLocals() + " / " + c.maxStack()).orElse("-"); + + m2.code().map(CodeAttribute.class::cast) + .map(c -> c.maxLocals() + " / " + c.maxStack()).orElse("-"); assertEquals(text1, text2); } assertFalse(itNoStack.hasNext()); diff --git a/test/jdk/jdk/classfile/DiscontinuedInstructionsTest.java b/test/jdk/jdk/classfile/DiscontinuedInstructionsTest.java index 9070f0b1d45..5e51e585634 100644 --- a/test/jdk/jdk/classfile/DiscontinuedInstructionsTest.java +++ b/test/jdk/jdk/classfile/DiscontinuedInstructionsTest.java @@ -26,6 +26,7 @@ * @summary Testing ClassFile handling JSR and RET instructions. * @run junit DiscontinuedInstructionsTest */ +import java.lang.classfile.attribute.CodeAttribute; import java.lang.constant.ClassDesc; import java.lang.constant.MethodTypeDesc; import java.util.ArrayList; @@ -63,7 +64,7 @@ void testJsrAndRetProcessing() throws Exception { .pop() .with(DiscontinuedInstruction.RetInstruction.of(355)))); - var c = cc.parse(bytes).methods().get(0).code().get(); + var c = (CodeAttribute) cc.parse(bytes).methods().get(0).code().get(); assertEquals(356, c.maxLocals()); assertEquals(6, c.maxStack()); diff --git a/test/jdk/jdk/classfile/LDCTest.java b/test/jdk/jdk/classfile/LDCTest.java index 1e7639de4bb..7379218840e 100644 --- a/test/jdk/jdk/classfile/LDCTest.java +++ b/test/jdk/jdk/classfile/LDCTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, 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 @@ -27,6 +27,9 @@ * @run junit LDCTest */ import java.lang.constant.ClassDesc; + +import static java.lang.classfile.ClassFile.ACC_PUBLIC; +import static java.lang.classfile.ClassFile.ACC_STATIC; import static java.lang.constant.ConstantDescs.*; import java.lang.constant.MethodTypeDesc; @@ -38,7 +41,6 @@ import org.junit.jupiter.api.Test; import static helpers.TestConstants.MTD_VOID; import static java.lang.classfile.Opcode.*; -import static java.lang.classfile.TypeKind.VoidType; import java.lang.classfile.instruction.ConstantInstruction; class LDCTest { @@ -56,7 +58,7 @@ void testLDCisConvertedToLDCW() throws Exception { ) .withMethod("main", MethodTypeDesc.of(CD_void, CD_String.arrayType()), - AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), + ACC_PUBLIC | ACC_STATIC, mb -> mb.withCode(c0 -> { ConstantPoolBuilder cpb = cb.constantPool(); for (int i = 0; i <= 256/2 + 2; i++) { // two entries per String diff --git a/test/jdk/jdk/classfile/LowAdaptTest.java b/test/jdk/jdk/classfile/LowAdaptTest.java index 9fa2620913b..d47141a885a 100644 --- a/test/jdk/jdk/classfile/LowAdaptTest.java +++ b/test/jdk/jdk/classfile/LowAdaptTest.java @@ -27,6 +27,9 @@ * @run junit LowAdaptTest */ import java.lang.constant.ClassDesc; + +import static java.lang.classfile.ClassFile.ACC_PUBLIC; +import static java.lang.classfile.ClassFile.ACC_STATIC; import static java.lang.constant.ConstantDescs.*; import java.lang.constant.DirectMethodHandleDesc; import java.lang.constant.DynamicCallSiteDesc; @@ -35,7 +38,6 @@ import java.net.URI; import java.nio.file.Paths; -import java.lang.classfile.AccessFlags; import java.lang.reflect.AccessFlag; import java.lang.classfile.ClassModel; import java.lang.classfile.ClassFile; @@ -75,7 +77,7 @@ void testAdapt() throws Exception { cl.methods().forEach(cb::with); cb.withMethod("doit", MethodTypeDesc.of(CD_int, CD_int), - AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), + ACC_PUBLIC | ACC_STATIC, mb -> mb.withCode(xb -> { xb.invokedynamic(indy); xb.astore(1); diff --git a/test/jdk/jdk/classfile/LvtTest.java b/test/jdk/jdk/classfile/LvtTest.java index 3d789809798..d95a4fb7b29 100644 --- a/test/jdk/jdk/classfile/LvtTest.java +++ b/test/jdk/jdk/classfile/LvtTest.java @@ -35,13 +35,10 @@ import java.lang.constant.ClassDesc; import java.net.URI; import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; -import java.lang.classfile.AccessFlags; import java.lang.classfile.Attributes; import java.lang.classfile.attribute.SourceFileAttribute; import java.lang.classfile.constantpool.ConstantPoolBuilder; @@ -58,11 +55,10 @@ import static helpers.TestConstants.MTD_VOID; import static helpers.TestUtil.ExpectedLvRecord; import static helpers.TestUtil.ExpectedLvtRecord; +import static java.lang.classfile.ClassFile.ACC_PUBLIC; +import static java.lang.classfile.ClassFile.ACC_STATIC; import static java.lang.constant.ConstantDescs.*; import java.lang.constant.MethodTypeDesc; -import static java.lang.classfile.Opcode.*; -import static java.lang.classfile.Opcode.INVOKEVIRTUAL; -import static java.lang.classfile.TypeKind.VoidType; import static org.junit.jupiter.api.Assertions.*; class LvtTest { @@ -128,7 +124,7 @@ void testCreateLoadLVT() throws Exception { ) ) .withMethod("main", MethodTypeDesc.of(CD_void, CD_String.arrayType()), - AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), + ACC_PUBLIC | ACC_STATIC, mb -> mb .withCode(c0 -> { ConstantPoolBuilder cpb = cb.constantPool(); @@ -243,7 +239,7 @@ void testCreateLoadLVTT() throws Exception { ) .withMethod("m", MethodTypeDesc.of(CD_Object, CD_Object.arrayType()), - ClassFile.ACC_PUBLIC, + ACC_PUBLIC, mb -> mb.withFlags(AccessFlag.PUBLIC) .withCode(c0 -> { ConstantPoolBuilder cpb = cb.constantPool(); diff --git a/test/jdk/jdk/classfile/OneToOneTest.java b/test/jdk/jdk/classfile/OneToOneTest.java index b20d07025e6..4e21cbebbe3 100644 --- a/test/jdk/jdk/classfile/OneToOneTest.java +++ b/test/jdk/jdk/classfile/OneToOneTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, 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 @@ -27,18 +27,19 @@ * @run junit OneToOneTest */ import java.lang.constant.ClassDesc; + +import static java.lang.classfile.ClassFile.ACC_PUBLIC; +import static java.lang.classfile.ClassFile.ACC_STATIC; import static java.lang.constant.ConstantDescs.*; import java.lang.constant.MethodTypeDesc; import java.util.List; -import java.lang.classfile.AccessFlags; import java.lang.reflect.AccessFlag; import java.lang.classfile.ClassModel; import java.lang.classfile.ClassFile; import java.lang.classfile.Instruction; import java.lang.classfile.Label; import java.lang.classfile.MethodModel; -import java.lang.classfile.TypeKind; import java.lang.classfile.attribute.SourceFileAttribute; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; @@ -74,7 +75,7 @@ void testClassWriteRead() { ) ) .withMethod("main", MethodTypeDesc.of(CD_void, CD_String.arrayType()), - AccessFlags.ofMethod(AccessFlag.STATIC, AccessFlag.PUBLIC).flagsMask(), + ACC_PUBLIC | ACC_STATIC, mb -> mb.withCode(c0 -> { Label loopTop = c0.newLabel(); Label loopEnd = c0.newLabel(); diff --git a/test/jdk/jdk/classfile/StackMapsTest.java b/test/jdk/jdk/classfile/StackMapsTest.java index 211490c3f8e..a5109dd2a18 100644 --- a/test/jdk/jdk/classfile/StackMapsTest.java +++ b/test/jdk/jdk/classfile/StackMapsTest.java @@ -30,6 +30,7 @@ */ import java.lang.classfile.*; +import java.lang.classfile.attribute.CodeAttribute; import java.lang.classfile.components.ClassPrinter; import java.net.URI; import java.nio.file.FileSystem; @@ -329,7 +330,7 @@ void testEmptyCounters(ClassFile.StackMapsOption option) { var cm = ClassFile.of().parse(bytes); for (var method : cm.methods()) { var name = method.methodName(); - var code = method.code().orElseThrow(); + var code = (CodeAttribute) method.code().orElseThrow(); if (name.equalsString("a")) { assertEquals(0, code.maxLocals()); // static method assertEquals(0, code.maxStack()); diff --git a/test/jdk/jdk/classfile/SwapTest.java b/test/jdk/jdk/classfile/SwapTest.java index 10c0d66d513..71ea6189aa6 100644 --- a/test/jdk/jdk/classfile/SwapTest.java +++ b/test/jdk/jdk/classfile/SwapTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, 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 @@ -27,8 +27,10 @@ * @run junit SwapTest */ -import java.lang.classfile.AccessFlags; import java.lang.classfile.ClassFile; + +import static java.lang.classfile.ClassFile.ACC_PUBLIC; +import static java.lang.classfile.ClassFile.ACC_STATIC; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; @@ -38,9 +40,6 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; -import static java.lang.reflect.AccessFlag.PUBLIC; -import static java.lang.reflect.AccessFlag.STATIC; - class SwapTest { @Test void testTryCatchCatchAll() throws Throwable { @@ -48,7 +47,7 @@ void testTryCatchCatchAll() throws Throwable { MethodTypeDesc mtd = mt.describeConstable().get(); byte[] bytes = ClassFile.of().build(ClassDesc.of("C"), cb -> { - cb.withMethodBody("m", mtd, AccessFlags.ofMethod(PUBLIC, STATIC).flagsMask(), xb -> { + cb.withMethodBody("m", mtd, ACC_PUBLIC | ACC_STATIC, xb -> { xb.aload(0); // 0 xb.aload(1); // 1, 0 xb.swap(); // 0, 1 diff --git a/test/jdk/jdk/classfile/WriteTest.java b/test/jdk/jdk/classfile/WriteTest.java index b4af9135d12..72d847c474c 100644 --- a/test/jdk/jdk/classfile/WriteTest.java +++ b/test/jdk/jdk/classfile/WriteTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, 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 @@ -30,20 +30,16 @@ import java.lang.constant.MethodTypeDesc; import helpers.TestConstants; -import java.lang.classfile.AccessFlags; import java.lang.reflect.AccessFlag; import java.lang.classfile.ClassFile; -import java.lang.classfile.TypeKind; import java.lang.classfile.Label; import java.lang.classfile.attribute.SourceFileAttribute; import org.junit.jupiter.api.Test; import static helpers.TestConstants.MTD_VOID; +import static java.lang.classfile.ClassFile.ACC_PUBLIC; +import static java.lang.classfile.ClassFile.ACC_STATIC; import static java.lang.constant.ConstantDescs.*; -import static java.lang.classfile.Opcode.*; -import static java.lang.classfile.TypeKind.IntType; -import static java.lang.classfile.TypeKind.ReferenceType; -import static java.lang.classfile.TypeKind.VoidType; class WriteTest { @@ -61,7 +57,7 @@ void testJavapWrite() { ) ) .withMethod("main", MethodTypeDesc.of(CD_void, CD_String.arrayType()), - AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), + ACC_PUBLIC | ACC_STATIC, mb -> mb.withCode(c0 -> { Label loopTop = c0.newLabel(); Label loopEnd = c0.newLabel(); @@ -102,7 +98,7 @@ void testPrimitiveWrite() { ) ) .withMethod("main", MethodTypeDesc.of(CD_void, CD_String.arrayType()), - AccessFlags.ofMethod(AccessFlag.PUBLIC, AccessFlag.STATIC).flagsMask(), + ACC_PUBLIC | ACC_STATIC, mb -> mb.withCode(c0 -> { Label loopTop = c0.newLabel(); Label loopEnd = c0.newLabel(); diff --git a/test/jdk/jdk/classfile/examples/ExampleGallery.java b/test/jdk/jdk/classfile/examples/ExampleGallery.java index 7de4a78ffbf..f37f6b1b74d 100644 --- a/test/jdk/jdk/classfile/examples/ExampleGallery.java +++ b/test/jdk/jdk/classfile/examples/ExampleGallery.java @@ -207,7 +207,7 @@ public byte[] changeFieldSig(ClassModel cm) { public byte[] changeFieldFlags(ClassModel cm) { return ClassFile.of().transformClass(cm, ClassTransform.transformingFields((fb, fe) -> { switch (fe) { - case AccessFlags a -> fb.with(AccessFlags.ofField(a.flagsMask() & ~ClassFile.ACC_PUBLIC & ~ClassFile.ACC_PROTECTED)); + case AccessFlags a -> fb.withFlags(a.flagsMask() & ~ClassFile.ACC_PUBLIC & ~ClassFile.ACC_PROTECTED); default -> fb.with(fe); } })); diff --git a/test/jdk/jdk/internal/misc/ThreadFlock/ThreadFlockTest.java b/test/jdk/jdk/internal/misc/ThreadFlock/ThreadFlockTest.java index 2f1da20e052..e6bc191a3ba 100644 --- a/test/jdk/jdk/internal/misc/ThreadFlock/ThreadFlockTest.java +++ b/test/jdk/jdk/internal/misc/ThreadFlock/ThreadFlockTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2024, 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 @@ -426,7 +426,7 @@ void testAwaitAllWithTimeout1(ThreadFactory factory) throws Exception { long startMillis = millisTime(); boolean done = flock.awaitAll(Duration.ofSeconds(30)); assertTrue(done); - checkDuration(startMillis, 1900, 4000); + checkDuration(startMillis, 1900, 20_000); } } @@ -453,7 +453,7 @@ void testAwaitAllWithTimeout2(ThreadFactory factory) throws Exception { flock.awaitAll(Duration.ofSeconds(2)); fail("awaitAll did not throw"); } catch (TimeoutException e) { - checkDuration(startMillis, 1900, 4000); + checkDuration(startMillis, 1900, 20_000); } } finally { latch.countDown(); diff --git a/test/jdk/jdk/jfr/api/settings/TestSettingControl.java b/test/jdk/jdk/jfr/api/settings/TestSettingControl.java new file mode 100644 index 00000000000..5543953482d --- /dev/null +++ b/test/jdk/jdk/jfr/api/settings/TestSettingControl.java @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2024, 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. + */ + +package jdk.jfr.api.settings; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Instant; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +import jdk.jfr.EventType; +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; +import jdk.jfr.SettingDescriptor; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; + +/** + * @test + * @summary Tests that methods on all SettingControls have expected behavior. + * @key jfr + * @requires vm.hasJFR + * @library /test/lib /test/jdk + * @run main/othervm jdk.jfr.api.settings.TestSettingControl + */ +public class TestSettingControl { + record SettingTest(String setting, String defaultValue, String event, List exampleValues) { + public String eventSettingName() { + return event + "#" + setting; + } + } + + // Example values should be listed in precedence order with the lowest precedence first. + static List SETTING_TESTS = List.of( + new SettingTest("enabled", "false", "jdk.JavaMonitorWait",List.of("false", "true")), + new SettingTest("stackTrace", "true", "jdk.JavaMonitorWait", List.of("false", "true")), + new SettingTest("threshold", "0 ns", "jdk.JavaMonitorWait", List.of("infinity", "10 ms", "0 ns")), + new SettingTest("level", "forRemoval", "jdk.DeprecatedInvocation", List.of("off", "forRemoval")), + new SettingTest("period", "everyChunk", "jdk.ExceptionStatistics", List.of("everyChunk", "60 s", "1 s")), + new SettingTest("cutoff", "infinity", "jdk.OldObjectSample", List.of("0 ms", "1 s", "infinity")), + new SettingTest("throttle", "off", "jdk.ObjectAllocationSample", List.of("off", "100/s", "10/ms")) + ); + + public static void main(String... args) throws Exception { + testTesting(); + testDefault(); + testDefaultWithInvalid(); + testPrecedence(); + testPrecedenceWithInvalid(); + } + + // Ensure that all known SettingControl/types are tested at least once + private static void testTesting() throws Exception { + Set foundSettings = new HashSet<>(); + for (EventType eventType : allEventTypes()) { + for (SettingDescriptor s : eventType.getSettingDescriptors()) { + foundSettings.add(s.getName()); + } + } + for (SettingTest st : SETTING_TESTS) { + foundSettings.remove(st.setting()); + } + if (!foundSettings.isEmpty()) { + throw new Exception("All event SettingControls should be tested. Missing test for " + foundSettings); + } + } + + // Ensure that the default values for all SettingControls are correct + private static void testDefault() throws Exception { + for (SettingTest settingTest : SETTING_TESTS) { + SettingDescriptor s = findSettingDescriptor(settingTest); + if (!settingTest.defaultValue().equals(s.getDefaultValue())) { + String message = "Incorrect default value " + quote(s.getDefaultValue()); + message += " for setting " + settingTest.eventSettingName() + ". "; + message += "Expected " + quote(settingTest.defaultValue()); + throw new Exception(message); + } + } + } + + // Ensure that default settings are used if an invalid setting is specified. + private static void testDefaultWithInvalid() throws Exception { + Map settings = createEnabledMap(); + for (SettingTest settingTest : SETTING_TESTS) { + settings.put(settingTest.eventSettingName(), "%#&2672g"); + } + Map result = check("testDefaultWithInvalid", List.of(settings)); + for (var entry : new ArrayList<>(result.entrySet())) { + String key = entry.getKey(); + String removed = result.remove(key); + if (removed == null) { + throw new Exception("Expected setting " + quote(key) + " to exist"); + } + String setting = key.substring(key.indexOf("#") + 1); + SettingTest st = findSettingTest(setting); + if (st == null) { + throw new Exception("Found unexpected setting " + quote(key)); + } + if (!removed.equals(st.defaultValue())) { + String message = "Expected default value " + quote(st.defaultValue()); + message += " for setting " + quote(setting) + " when"; + message += " an invalid settings value was specified"; + throw new Exception(message); + } + } + if (!result.isEmpty()) { + throw new Exception("Found unexpected setting when testing preserved default"); + } + } + + // Only enabled events will use settings + private static Map createEnabledMap() { + Map settings = new TreeMap<>(); + for (SettingTest settingTest : SETTING_TESTS) { + settings.put(settingTest.event + "#enabled", "true"); + } + return settings; + } + + // Ensure that precedence are respected when multiple settings are specified + private static void testPrecedence() throws Exception { + testPrecedence("testPrecedence"); + } + + // Ensure that precedence are respected when an incorrect setting is used + private static void testPrecedenceWithInvalid() throws Exception { + testPrecedence("testPrecedenceWithInvalid"); + } + + // * * * HELPER METHODS * * * + + private static void testPrecedence(String testName) throws Exception { + List> settingsList = new ArrayList<>(); + int maxExamples = 0; + for (SettingTest t : SETTING_TESTS) { + maxExamples = Math.max(t.exampleValues().size(), maxExamples); + } + for (int i = 0; i < maxExamples; i++) { + Map settings = createEnabledMap(); + for (SettingTest settingTest : SETTING_TESTS) { + List examples = settingTest.exampleValues(); + String name = settingTest.eventSettingName(); + if (i < examples.size()) { + settings.put(name, examples.get(i)); + } + // Insert the invalid setting first + if (testName.contains("Invalid") && i == 0) { + settings.put(name, "%#&2672g"); + } + } + settingsList.add(settings); + } + Map results = check(testName, settingsList); + Map reversed = check(testName + "-reversed", settingsList.reversed()); + if (!reversed.equals(results)) { + throw new Exception("Active settings should not depend on the order event settings are applied"); + } + for (SettingTest t : SETTING_TESTS) { + String expected = t.exampleValues().get(t.exampleValues().size() - 1); + String found = results.get(t.eventSettingName()); + if (!expected.equals(found)) { + throw new Exception("Expected " + expected + " to be used with setting " + quote(t.setting()) + ", not " + quote(found)); + } + } + } + + private static Map check(String testName, List> settingsList) throws Exception { + System.out.println("*** Check for " + testName + " ****"); + System.out.println("Input:"); + int index = 0; + for (var settings : settingsList) { + System.out.println("Setting[" + index + "] = {"); + for (var e : settings.entrySet()) { + System.out.println(" " + e.getKey() + "=" + e.getValue()); + } + System.out.println("}"); + index++; + } + int settingsCount = settingsList.size(); + + // Start a recording for each settings + List recordings = new ArrayList<>(); + for (int i = 0; i < settingsCount; i++) { + Recording r = new Recording(); + Map settings = settingsList.get(i); + settings.put("jdk.ActiveSetting#enabled", "true"); + r.setSettings(settings); + r.start(); + recordings.add(r); + } + + // Stop all recordings + for (Recording r : recordings) { + r.stop(); + } + + // Dump the innermost recording + Path p = Path.of("recording.jfr"); + Recording inner = recordings.get(settingsCount - 1); + inner.dump(p); + + // Close all recordings + for (Recording r : recordings) { + r.close(); + } + System.out.println("Result:"); + Map r = lastSettings(p); + for (var e : r.entrySet()) { + System.out.println(e.getKey() + "=" + e.getValue()); + } + System.out.println("*************"); + System.out.println(); + Files.delete(p); + return r; + } + + private static SettingTest findSettingTest(String name) throws Exception { + for (SettingTest settingTest : SETTING_TESTS) { + if (name.equals(settingTest.setting())) { + return settingTest; + } + } + return null; + } + + private static Map lastSettings(Path p) throws Exception { + List events = RecordingFile.readAllEvents(p); + Instant timestamp = findLastActiveSetting(events); + Map lastInnerMostSettings = new HashMap<>(); + for (SettingTest t : SETTING_TESTS) { + long id = eventTypeNameToId(t.event()); + for (RecordedEvent event : events) { + if (event.getEventType().getName().equals("jdk.ActiveSetting")) { + if (event.getStartTime().equals(timestamp) && id == event.getLong("id")) { + String name = event.getString("name"); + String value = event.getString("value"); + if (t.setting.equals(name)) { + String fullName = t.event() + "#" + name; + String previous = lastInnerMostSettings.put(fullName, value); + if (previous != null) { + throw new Exception("Expected only one ActiveSetting event per event setting"); + } + } + } + } + } + } + return lastInnerMostSettings; + } + + private static Instant findLastActiveSetting(List events) { + Instant lastTimestamp = null; + for (RecordedEvent event : events) { + if (event.getEventType().getName().equals("jdk.ActiveSetting")) { + Instant t = event.getStartTime(); + if (lastTimestamp == null || t.isBefore(lastTimestamp)) { + lastTimestamp = t; + } + } + } + return lastTimestamp; + } + + private static long eventTypeNameToId(String name) throws Exception { + for (EventType eventType : allEventTypes()) { + if (eventType.getName().equals(name)) { + return eventType.getId(); + } + } + throw new Exception("Could not find event type with name " + name); + } + + private static SettingDescriptor findSettingDescriptor(SettingTest settingTest) throws Exception { + for (EventType eventType : allEventTypes()) { + if (eventType.getName().equals(settingTest.event())) { + for (SettingDescriptor s : eventType.getSettingDescriptors()) { + if (settingTest.setting().equals(s.getName())) { + return s; + } + } + } + } + throw new Exception("Could not find setting with name " + settingTest.event() + "#" + settingTest.setting()); + } + + private static List allEventTypes() { + return FlightRecorder.getFlightRecorder().getEventTypes(); + } + + private static String quote(String text) { + return "'" + text + "'"; + } +} diff --git a/test/jdk/jdk/jfr/event/gc/stacktrace/TestDefNewAllocationPendingStackTrace.java b/test/jdk/jdk/jfr/event/gc/stacktrace/TestDefNewAllocationPendingStackTrace.java index 2f290a748c2..fa91d77e2fb 100644 --- a/test/jdk/jdk/jfr/event/gc/stacktrace/TestDefNewAllocationPendingStackTrace.java +++ b/test/jdk/jdk/jfr/event/gc/stacktrace/TestDefNewAllocationPendingStackTrace.java @@ -29,7 +29,9 @@ * * @requires vm.gc == "null" | vm.gc == "Serial" * @library /test/lib /test/jdk - * @run main/othervm -XX:+UseSerialGC -Xlog:gc* jdk.jfr.event.gc.stacktrace.TestDefNewAllocationPendingStackTrace + * @run main/othervm -XX:+UseSerialGC -Xlog:gc* + * -XX:FlightRecorderOptions:stackdepth=256 + * jdk.jfr.event.gc.stacktrace.TestDefNewAllocationPendingStackTrace */ public class TestDefNewAllocationPendingStackTrace { diff --git a/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1HumongousAllocationPendingStackTrace.java b/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1HumongousAllocationPendingStackTrace.java index 2df5d95f524..ad633ea26de 100644 --- a/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1HumongousAllocationPendingStackTrace.java +++ b/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1HumongousAllocationPendingStackTrace.java @@ -29,7 +29,9 @@ * * @requires vm.gc == "null" | vm.gc == "G1" * @library /test/lib /test/jdk - * @run main/othervm -XX:+UseG1GC -Xlog:gc* -Xmx64M -XX:InitiatingHeapOccupancyPercent=100 jdk.jfr.event.gc.stacktrace.TestG1HumongousAllocationPendingStackTrace + * @run main/othervm -XX:+UseG1GC -Xlog:gc* -Xmx64M -XX:InitiatingHeapOccupancyPercent=100 + * -XX:FlightRecorderOptions:stackdepth=256 + * jdk.jfr.event.gc.stacktrace.TestG1HumongousAllocationPendingStackTrace */ public class TestG1HumongousAllocationPendingStackTrace { diff --git a/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1OldAllocationPendingStackTrace.java b/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1OldAllocationPendingStackTrace.java index 3541f117e1e..1cf75ff8fac 100644 --- a/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1OldAllocationPendingStackTrace.java +++ b/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1OldAllocationPendingStackTrace.java @@ -29,7 +29,9 @@ * * @requires vm.gc == "null" | vm.gc == "G1" * @library /test/lib /test/jdk - * @run main/othervm -XX:MaxNewSize=10M -Xmx128M -XX:+UseG1GC -Xlog:gc* jdk.jfr.event.gc.stacktrace.TestG1OldAllocationPendingStackTrace + * @run main/othervm -XX:MaxNewSize=10M -Xmx128M -XX:+UseG1GC -Xlog:gc* + * -XX:FlightRecorderOptions:stackdepth=256 + * jdk.jfr.event.gc.stacktrace.TestG1OldAllocationPendingStackTrace */ public class TestG1OldAllocationPendingStackTrace { diff --git a/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1YoungAllocationPendingStackTrace.java b/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1YoungAllocationPendingStackTrace.java index e5b7f849173..7259ff5f445 100644 --- a/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1YoungAllocationPendingStackTrace.java +++ b/test/jdk/jdk/jfr/event/gc/stacktrace/TestG1YoungAllocationPendingStackTrace.java @@ -29,7 +29,9 @@ * * @requires vm.gc == "null" | vm.gc == "G1" * @library /test/lib /test/jdk - * @run main/othervm -XX:+UseG1GC -Xlog:gc* jdk.jfr.event.gc.stacktrace.TestG1YoungAllocationPendingStackTrace + * @run main/othervm -XX:+UseG1GC -Xlog:gc* + * -XX:FlightRecorderOptions:stackdepth=256 + * jdk.jfr.event.gc.stacktrace.TestG1YoungAllocationPendingStackTrace */ public class TestG1YoungAllocationPendingStackTrace { diff --git a/test/jdk/jdk/jfr/event/gc/stacktrace/TestMarkSweepCompactAllocationPendingStackTrace.java b/test/jdk/jdk/jfr/event/gc/stacktrace/TestMarkSweepCompactAllocationPendingStackTrace.java index e7f5e709d15..c06862ad2e6 100644 --- a/test/jdk/jdk/jfr/event/gc/stacktrace/TestMarkSweepCompactAllocationPendingStackTrace.java +++ b/test/jdk/jdk/jfr/event/gc/stacktrace/TestMarkSweepCompactAllocationPendingStackTrace.java @@ -29,7 +29,9 @@ * * @requires vm.gc == "null" | vm.gc == "Serial" * @library /test/lib /test/jdk - * @run main/othervm -XX:MaxNewSize=10M -Xmx64M -XX:+UseSerialGC -Xlog:gc* jdk.jfr.event.gc.stacktrace.TestMarkSweepCompactAllocationPendingStackTrace + * @run main/othervm -XX:MaxNewSize=10M -Xmx64M -XX:+UseSerialGC -Xlog:gc* + * -XX:FlightRecorderOptions:stackdepth=256 + * jdk.jfr.event.gc.stacktrace.TestMarkSweepCompactAllocationPendingStackTrace */ public class TestMarkSweepCompactAllocationPendingStackTrace { diff --git a/test/jdk/jdk/jfr/event/gc/stacktrace/TestMetaspaceG1GCAllocationPendingStackTrace.java b/test/jdk/jdk/jfr/event/gc/stacktrace/TestMetaspaceG1GCAllocationPendingStackTrace.java index 81cf4191c6e..c74eeefa793 100644 --- a/test/jdk/jdk/jfr/event/gc/stacktrace/TestMetaspaceG1GCAllocationPendingStackTrace.java +++ b/test/jdk/jdk/jfr/event/gc/stacktrace/TestMetaspaceG1GCAllocationPendingStackTrace.java @@ -28,9 +28,10 @@ * * @requires vm.gc == "null" | vm.gc == "G1" * @library /test/lib /test/jdk - * @run main/othervm -XX:+UseG1GC -Xlog:gc* -XX:MaxMetaspaceSize=64M jdk.jfr.event.gc.stacktrace.TestMetaspaceG1GCAllocationPendingStackTrace + * @run main/othervm -XX:+UseG1GC -Xlog:gc* -XX:MaxMetaspaceSize=64M + * -XX:FlightRecorderOptions:stackdepth=256 + * jdk.jfr.event.gc.stacktrace.TestMetaspaceG1GCAllocationPendingStackTrace */ - public class TestMetaspaceG1GCAllocationPendingStackTrace { public static void main(String[] args) throws Exception { diff --git a/test/jdk/jdk/jfr/event/gc/stacktrace/TestMetaspaceParallelGCAllocationPendingStackTrace.java b/test/jdk/jdk/jfr/event/gc/stacktrace/TestMetaspaceParallelGCAllocationPendingStackTrace.java index ab31fe644bf..cc669024015 100644 --- a/test/jdk/jdk/jfr/event/gc/stacktrace/TestMetaspaceParallelGCAllocationPendingStackTrace.java +++ b/test/jdk/jdk/jfr/event/gc/stacktrace/TestMetaspaceParallelGCAllocationPendingStackTrace.java @@ -29,7 +29,9 @@ * * @requires vm.gc == "null" | vm.gc == "Parallel" * @library /test/lib /test/jdk - * @run main/othervm -XX:+UseParallelGC -Xlog:gc* -XX:MaxMetaspaceSize=64M jdk.jfr.event.gc.stacktrace.TestMetaspaceParallelGCAllocationPendingStackTrace + * @run main/othervm -XX:+UseParallelGC -Xlog:gc* -XX:MaxMetaspaceSize=64M + * -XX:FlightRecorderOptions:stackdepth=256 + * jdk.jfr.event.gc.stacktrace.TestMetaspaceParallelGCAllocationPendingStackTrace */ public class TestMetaspaceParallelGCAllocationPendingStackTrace { diff --git a/test/jdk/jdk/jfr/event/gc/stacktrace/TestMetaspaceSerialGCAllocationPendingStackTrace.java b/test/jdk/jdk/jfr/event/gc/stacktrace/TestMetaspaceSerialGCAllocationPendingStackTrace.java index 83aa4fb522e..7f0ce364b6a 100644 --- a/test/jdk/jdk/jfr/event/gc/stacktrace/TestMetaspaceSerialGCAllocationPendingStackTrace.java +++ b/test/jdk/jdk/jfr/event/gc/stacktrace/TestMetaspaceSerialGCAllocationPendingStackTrace.java @@ -29,7 +29,9 @@ * * @requires vm.gc == "null" | vm.gc == "Serial" * @library /test/lib /test/jdk - * @run main/othervm -XX:+UseSerialGC -Xlog:gc* -XX:MaxMetaspaceSize=64M jdk.jfr.event.gc.stacktrace.TestMetaspaceSerialGCAllocationPendingStackTrace + * @run main/othervm -XX:+UseSerialGC -Xlog:gc* -XX:MaxMetaspaceSize=64M + * -XX:FlightRecorderOptions:stackdepth=256 + * jdk.jfr.event.gc.stacktrace.TestMetaspaceSerialGCAllocationPendingStackTrace */ public class TestMetaspaceSerialGCAllocationPendingStackTrace { diff --git a/test/jdk/jdk/jfr/event/gc/stacktrace/TestParallelMarkSweepAllocationPendingStackTrace.java b/test/jdk/jdk/jfr/event/gc/stacktrace/TestParallelMarkSweepAllocationPendingStackTrace.java index 5ab67d2561a..ebd1efccfee 100644 --- a/test/jdk/jdk/jfr/event/gc/stacktrace/TestParallelMarkSweepAllocationPendingStackTrace.java +++ b/test/jdk/jdk/jfr/event/gc/stacktrace/TestParallelMarkSweepAllocationPendingStackTrace.java @@ -29,7 +29,9 @@ * * @requires vm.gc == "null" | vm.gc == "Parallel" * @library /test/lib /test/jdk - * @run main/othervm -XX:MaxNewSize=10M -Xmx64M -XX:+UseParallelGC -Xlog:gc* jdk.jfr.event.gc.stacktrace.TestParallelMarkSweepAllocationPendingStackTrace + * @run main/othervm -XX:MaxNewSize=10M -Xmx64M -XX:+UseParallelGC -Xlog:gc* + * -XX:FlightRecorderOptions:stackdepth=256 + * jdk.jfr.event.gc.stacktrace.TestParallelMarkSweepAllocationPendingStackTrace */ public class TestParallelMarkSweepAllocationPendingStackTrace { diff --git a/test/jdk/jdk/jfr/event/gc/stacktrace/TestParallelScavengeAllocationPendingStackTrace.java b/test/jdk/jdk/jfr/event/gc/stacktrace/TestParallelScavengeAllocationPendingStackTrace.java index 08ec7ffd05c..6dbbc16e283 100644 --- a/test/jdk/jdk/jfr/event/gc/stacktrace/TestParallelScavengeAllocationPendingStackTrace.java +++ b/test/jdk/jdk/jfr/event/gc/stacktrace/TestParallelScavengeAllocationPendingStackTrace.java @@ -29,7 +29,9 @@ * * @requires vm.gc == "null" | vm.gc == "Parallel" * @library /test/lib /test/jdk - * @run main/othervm -XX:+UseParallelGC -Xlog:gc* jdk.jfr.event.gc.stacktrace.TestParallelScavengeAllocationPendingStackTrace + * @run main/othervm -XX:+UseParallelGC -Xlog:gc* + * -XX:FlightRecorderOptions:stackdepth=256 + * jdk.jfr.event.gc.stacktrace.TestParallelScavengeAllocationPendingStackTrace */ public class TestParallelScavengeAllocationPendingStackTrace { diff --git a/test/jdk/sun/security/util/DerInputBuffer/B8336667/PoC.java b/test/jdk/sun/security/util/DerInputBuffer/B8336667/PoC.java new file mode 100644 index 00000000000..d14c213f7b2 --- /dev/null +++ b/test/jdk/sun/security/util/DerInputBuffer/B8336667/PoC.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2024, 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 8336667 + * @summary Ensure the unused bytes are calculated correctly when converting + * indefinite length BER to DER + * @modules java.base/sun.security.util + * @library /test/lib + */ +import jdk.test.lib.Asserts; +import sun.security.util.DerInputStream; + +import java.util.HexFormat; + +public class PoC { + public static void main(String[] args) throws Exception { + // A BER indefinite encoding with some unused bytes at the end + var data = HexFormat.of().parseHex(""" + 2480 0401AA 0401BB 0000 -- 2 byte string + 010100 -- boolean false + 12345678 -- 4 unused bytes""" + .replaceAll("(\\s|--.*)", "")); + var dis = new DerInputStream(data, 0, data.length - 4, true); + Asserts.assertEQ(dis.getDerValue().getOctetString().length, 2); + Asserts.assertFalse(dis.getDerValue().getBoolean()); + dis.atEnd(); + } +} diff --git a/test/jdk/sun/security/util/DerInputBuffer/B8336667/Reproducer.java b/test/jdk/sun/security/util/DerInputBuffer/B8336667/Reproducer.java new file mode 100644 index 00000000000..f1e458035ee --- /dev/null +++ b/test/jdk/sun/security/util/DerInputBuffer/B8336667/Reproducer.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2024, 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 8336667 + * @summary Ensure the unused bytes are calculated correctly when converting + * indefinite length BER to DER + */ +import java.io.ByteArrayInputStream; +import java.security.cert.CRLException; +import java.security.cert.CertificateException; +import java.util.Base64; + +public class Reproducer { + private static final String INPUT = """ + MIIBljCCAVMwgAaB/////////yb////////////////////9////AgDv//////////////////// + /////2RjPWNvbf////8k/////////yb///////////////////9vbf////8k/////////yb///// + ////////////////////AgD/////////////b23/////JP////////8m/////yf///////////// + /////wIA//////////////////////////////////////8AAABl//////8m/////////y1CRUdJ + TiA9Y290cnVlVlZWVlZWVlZWVjEAAAAAAAAArQdVUwNVBAsTA0RvRDEaMBhAA1UAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAEXDTAzMDcxNTE2MjAwNFqgHzAdMA8GA1UdHAEB/wQFMAPyAf8wCgYDVR0P + BAMCAQIwCwYHKoZIzjgEAwUAAzBkARkTA2NvbTEYMBYGCgmSJomT8ixkARkTCG15VGVzdENBMBIC + AQHyAjZG+RfHdO4="""; + + Reproducer(byte[] data) { + try { + java.security.cert.CertificateFactory. + getInstance("X.509").generateCRLs(new ByteArrayInputStream(data)); + } catch (CertificateException | CRLException e) { + if (System.getProperty("dbg", "false").equals("true")) { + e.printStackTrace(); + } + } + } + + public static void main(String[] a) throws Exception { + byte[] decodedBytes = Base64.getMimeDecoder().decode(INPUT); + new Reproducer(decodedBytes); + } +} diff --git a/test/jdk/sun/tools/jcmd/TestJcmdPIDSubstitution.java b/test/jdk/sun/tools/jcmd/TestJcmdPIDSubstitution.java new file mode 100644 index 00000000000..f462ea1a52b --- /dev/null +++ b/test/jdk/sun/tools/jcmd/TestJcmdPIDSubstitution.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Red Hat Inc. + * 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 8334492 + * @summary Test to verify jcmd accepts %p in output filenames and substitutes for PID + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * @run driver TestJcmdPIDSubstitution + * + */ + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class TestJcmdPIDSubstitution { + + private static final String FILENAME = "myfile%p"; + + public static void main(String[] args) throws Exception { + verifyOutputFilenames("Thread.dump_to_file", FILENAME); + verifyOutputFilenames("GC.heap_dump", FILENAME); + verifyOutputFilenames("Compiler.perfmap", FILENAME); + verifyOutputFilenames("System.dump_map", "-F=%s".formatted(FILENAME)); + } + + private static void verifyOutputFilenames(String... args) throws Exception { + long pid = ProcessTools.getProcessId(); + String test_dir = System.getProperty("test.dir", "."); + Path path = Paths.get("%s/myfile%d".formatted(test_dir, pid)); + OutputAnalyzer output = JcmdBase.jcmd(args); + output.shouldHaveExitValue(0); + if (Files.exists(path)) { + Files.delete(path); + } else { + throw new Exception("File %s was not created as expected for diagnostic cmd %s".formatted(path, args[0])); + } + } +} diff --git a/test/jdk/tools/jpackage/apps/ChildProcessAppLauncher.java b/test/jdk/tools/jpackage/apps/ChildProcessAppLauncher.java index 11be90bc456..b599488b563 100644 --- a/test/jdk/tools/jpackage/apps/ChildProcessAppLauncher.java +++ b/test/jdk/tools/jpackage/apps/ChildProcessAppLauncher.java @@ -26,12 +26,17 @@ import java.nio.file.Path; public class ChildProcessAppLauncher { - - public static void main(String[] args) throws IOException { - String calcPath = Path.of(System.getenv("SystemRoot"), "system32", "calc.exe").toString(); - ProcessBuilder processBuilder = new ProcessBuilder(calcPath); + public static void main(String[] args) throws IOException, InterruptedException { + if (args.length == 1 && "noexit".equals(args[0])) { + var lock = new Object(); + synchronized (lock) { + lock.wait(); + } + } else { + var childPath = System.getProperty("jpackage.app-path"); // get the path to the current jpackage app launcher + ProcessBuilder processBuilder = new ProcessBuilder(childPath, "noexit"); //ChildProcessAppLauncher acts as third party app Process process = processBuilder.start(); - System.out.println("Calc id=" + process.pid()); - System.exit(0); + System.out.println("Child id=" + process.pid()); + } } } diff --git a/test/jdk/tools/jpackage/windows/WinChildProcessTest.java b/test/jdk/tools/jpackage/windows/WinChildProcessTest.java index 2928f7f1c5f..4fa7581d2b5 100644 --- a/test/jdk/tools/jpackage/windows/WinChildProcessTest.java +++ b/test/jdk/tools/jpackage/windows/WinChildProcessTest.java @@ -53,7 +53,7 @@ public class WinChildProcessTest { @Test public static void test() throws Throwable { - long calcPid = 0; + long childPid = 0; try { JPackageCommand cmd = JPackageCommand .helloAppImage(TEST_APP_JAVA + "*Hello"); @@ -68,21 +68,20 @@ public static void test() throws Throwable { .execute(0).getOutput(); String pidStr = output.get(0); - // parse calculator PID - calcPid = Long.parseLong(pidStr.split("=", 2)[1]); + // parse child PID + childPid = Long.parseLong(pidStr.split("=", 2)[1]); // Check whether the termination of third party application launcher // also terminating the launched third party application // If third party application is not terminated the test is // successful else failure - Optional processHandle = ProcessHandle.of(calcPid); + Optional processHandle = ProcessHandle.of(childPid); boolean isAlive = processHandle.isPresent() && processHandle.get().isAlive(); - System.out.println("Is Alive " + isAlive); - TKit.assertTrue(isAlive, "Check is calculator process is alive"); + TKit.assertTrue(isAlive, "Check is child process is alive"); } finally { - // Kill only a specific calculator instance - Runtime.getRuntime().exec("taskkill /F /PID " + calcPid); + // Kill only a specific child instance + Runtime.getRuntime().exec("taskkill /F /PID " + childPid); } } -} \ No newline at end of file +} diff --git a/test/langtools/jdk/javadoc/doclet/testErasure/TestErasure.java b/test/langtools/jdk/javadoc/doclet/testErasure/TestErasure.java index 3c9f9b6fd9d..f80074c50e2 100644 --- a/test/langtools/jdk/javadoc/doclet/testErasure/TestErasure.java +++ b/test/langtools/jdk/javadoc/doclet/testErasure/TestErasure.java @@ -82,15 +82,19 @@ class Y { }

Constructor Summary

Constructors
-
-
Constructor
+
+
Modifier
+
Constructor
Description
+
 
\ Foo(T arg)
 
+
 <T extends X>
\ Foo(T arg)
 
+
 <T extends Y>
\ Foo(T arg)
 
@@ -188,13 +192,16 @@ class X { }

Constructor Summary

Constructors
-
-
Constructor
+
+
Modifier
+
Constructor
Description
+
 
\ Foo\ (T arg)
 
+
 <T extends X>
\ Foo(T arg)
 
diff --git a/test/langtools/jdk/javadoc/doclet/testTypeParams/TestTypeParameters.java b/test/langtools/jdk/javadoc/doclet/testTypeParams/TestTypeParameters.java index 7f586b3539d..0d95ee38cdc 100644 --- a/test/langtools/jdk/javadoc/doclet/testTypeParams/TestTypeParameters.java +++ b/test/langtools/jdk/javadoc/doclet/testTypeParams/TestTypeParameters.java @@ -23,12 +23,13 @@ /* * @test - * @bug 4927167 4974929 7010344 8025633 8081854 8182765 8187288 8261976 + * @bug 4927167 4974929 6381729 7010344 8025633 8081854 8182765 8187288 8261976 * @summary When the type parameters are more than 10 characters in length, * make sure there is a line break between type params and return type * in member summary. Also, test for type parameter links in package-summary and * class-use pages. The class/annotation pages should check for type * parameter links in the class/annotation signature section when -linksource is set. + * Verify that generic type parameters on constructors are documented. * @library ../../lib * @modules jdk.javadoc/jdk.javadoc.internal.tool * @build javadoc.tester.* @@ -94,4 +95,25 @@ public class ParamTest2<java.util.List<? extends Foo4>>>"""); } + + @Test + public void test3() { + javadoc("-d", "out-3", + "-Xdoclint:none", + "--no-platform-links", + "-sourcepath", testSrc, + "pkg"); + checkExit(Exit.OK); + + checkOutput("pkg/CtorTypeParam.html", true, + """ +
 <T extends java.lang.Runnable>
+ +
 
""", + """ +
public\ +  <T extends java.lang.Runnable>\ +  CtorTypeParam()
"""); + } } diff --git a/src/hotspot/share/gc/shenandoah/mode/shenandoahIUMode.hpp b/test/langtools/jdk/javadoc/doclet/testTypeParams/pkg/CtorTypeParam.java similarity index 60% rename from src/hotspot/share/gc/shenandoah/mode/shenandoahIUMode.hpp rename to test/langtools/jdk/javadoc/doclet/testTypeParams/pkg/CtorTypeParam.java index c20c483c77d..a7f2309475d 100644 --- a/src/hotspot/share/gc/shenandoah/mode/shenandoahIUMode.hpp +++ b/test/langtools/jdk/javadoc/doclet/testTypeParams/pkg/CtorTypeParam.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Red Hat, Inc. All rights reserved. + * Copyright (c) 2024, 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 @@ -19,24 +19,11 @@ * 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. - * */ -#ifndef SHARE_GC_SHENANDOAH_MODE_SHENANDOAHIUMODE_HPP -#define SHARE_GC_SHENANDOAH_MODE_SHENANDOAHIUMODE_HPP - -#include "gc/shenandoah/mode/shenandoahMode.hpp" - -class ShenandoahHeuristics; - -class ShenandoahIUMode : public ShenandoahMode { -public: - virtual void initialize_flags() const; - virtual ShenandoahHeuristics* initialize_heuristics() const; - - virtual const char* name() { return "Incremental-Update (IU)"; } - virtual bool is_diagnostic() { return false; } - virtual bool is_experimental() { return true; } -}; +package pkg; -#endif // SHARE_GC_SHENANDOAH_MODE_SHENANDOAHIUMODE_HPP +public class CtorTypeParam { + public CtorTypeParam() { + } +} diff --git a/test/langtools/tools/javac/annotations/repeatingAnnotations/CompletionErrorOnRepeatingAnnosTest.java b/test/langtools/tools/javac/annotations/repeatingAnnotations/CompletionErrorOnRepeatingAnnosTest.java new file mode 100644 index 00000000000..b075a510155 --- /dev/null +++ b/test/langtools/tools/javac/annotations/repeatingAnnotations/CompletionErrorOnRepeatingAnnosTest.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2024, 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 8332850 + * @summary javac crashes if container for repeatable annotation is not found + * @library /tools/javac/lib /tools/lib + * @modules jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + */ + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.List; + +import toolbox.*; +import toolbox.Task.*; + +public class CompletionErrorOnRepeatingAnnosTest { + ToolBox tb = new ToolBox(); + + public static void main(String... args) throws Exception { + CompletionErrorOnRepeatingAnnosTest t = new CompletionErrorOnRepeatingAnnosTest(); + //t.testMissingContainerAnno(); + t.testMissingContainerTypeAnno(); + } + + void testMissingContainerTypeAnno() throws Exception { + doTest( + """ + import java.lang.annotation.*; + import static java.lang.annotation.RetentionPolicy.*; + import static java.lang.annotation.ElementType.*; + @Target({TYPE_USE,FIELD}) @Repeatable( As.class) @interface A { } + @Target({TYPE_USE,FIELD}) @interface As { A[] value(); } + """, + """ + class T { + @A @A String data = "test"; + } + """, + List.of( + "T.java:2:5: compiler.err.cant.access: As, (compiler.misc.class.file.not.found: As)", + "T.java:2:8: compiler.err.invalid.repeatable.annotation.no.value: As", + "2 errors" + ) + ); + } + + void testMissingContainerAnno() throws Exception { + doTest( + """ + import java.lang.annotation.Repeatable; + @Repeatable(As.class) + @interface A {} + @interface As { + A[] value(); + } + """, + "@A @A class T {}", + List.of( + "T.java:1:1: compiler.err.cant.access: As, (compiler.misc.class.file.not.found: As)", + "T.java:1:4: compiler.err.invalid.repeatable.annotation.no.value: As", + "2 errors" + ) + ); + } + + private void doTest(String annosSrc, String annotatedSrc, List expectedOutput) throws Exception { + Path base = Paths.get("."); + Path src = base.resolve("src"); + tb.createDirectories(src); + tb.writeJavaFiles(src, annosSrc); + Path out = base.resolve("out"); + tb.createDirectories(out); + new JavacTask(tb) + .outdir(out) + .files(tb.findJavaFiles(src)) + .run(); + // let's now compile T.java which uses repeated annotations, we want to load the anno classes from the CP + tb.deleteFiles(src.resolve("A.java")); + tb.writeJavaFiles(src, annotatedSrc); + new JavacTask(tb) + .outdir(out) + .classpath(out) + .files(tb.findJavaFiles(src)) + .run(); + // now if we remove As.class there will be an error but javac should not crash + tb.deleteFiles(out.resolve("As.class")); + List log = new JavacTask(tb) + .outdir(out) + .classpath(out) + .options("-XDrawDiagnostics") + .files(tb.findJavaFiles(src)) + .run(Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + if (!expectedOutput.equals(log)) + throw new Exception("expected output not found: " + log); + } +} diff --git a/test/jdk/java/lang/Thread/virtual/ThreadBuilders.java b/test/lib/jdk/test/lib/thread/VThreadScheduler.java similarity index 63% rename from test/jdk/java/lang/Thread/virtual/ThreadBuilders.java rename to test/lib/jdk/test/lib/thread/VThreadScheduler.java index a6a5d6abdbc..46b75fbe5eb 100644 --- a/test/jdk/java/lang/Thread/virtual/ThreadBuilders.java +++ b/test/lib/jdk/test/lib/thread/VThreadScheduler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, 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 @@ -21,28 +21,50 @@ * questions. */ +package jdk.test.lib.thread; + import java.lang.reflect.Constructor; +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.concurrent.Executor; import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; /** - * Helper class for creating Thread buidlers. + * Helper class to allow tests run virtual threads with a custom scheduler. * * Tests using this class need to open java.base/java.lang. */ -class ThreadBuilders { - private ThreadBuilders() { } +public class VThreadScheduler { + private VThreadScheduler() { } - private static final Constructor VTBUILDER_CTOR; - static { + /** + * Returns the scheduler for the given virtual thread. + */ + public static Executor scheduler(Thread thread) { + if (!thread.isVirtual()) + throw new IllegalArgumentException("Not a virtual thread"); try { - Class clazz = Class.forName("java.lang.ThreadBuilders$VirtualThreadBuilder"); - Constructor ctor = clazz.getDeclaredConstructor(Executor.class); - ctor.setAccessible(true); - VTBUILDER_CTOR = ctor; + Field scheduler = Class.forName("java.lang.VirtualThread") + .getDeclaredField("scheduler"); + scheduler.setAccessible(true); + return (Executor) scheduler.get(thread); } catch (Exception e) { - throw new InternalError(e); + throw new RuntimeException(e); + } + } + + /** + * Return true if custom schedulers are supported. + */ + public static boolean supportsCustomScheduler() { + try (var pool = Executors.newCachedThreadPool()) { + try { + virtualThreadBuilder(pool); + return true; + } catch (UnsupportedOperationException e) { + return false; + } } } @@ -50,9 +72,12 @@ private ThreadBuilders() { } * Returns a builder to create virtual threads that use the given scheduler. * @throws UnsupportedOperationException if custom schedulers are not supported */ - static Thread.Builder.OfVirtual virtualThreadBuilder(Executor scheduler) { + public static Thread.Builder.OfVirtual virtualThreadBuilder(Executor scheduler) { try { - return (Thread.Builder.OfVirtual) VTBUILDER_CTOR.newInstance(scheduler); + Class clazz = Class.forName("java.lang.ThreadBuilders$VirtualThreadBuilder"); + Constructor ctor = clazz.getDeclaredConstructor(Executor.class); + ctor.setAccessible(true); + return (Thread.Builder.OfVirtual) ctor.newInstance(scheduler); } catch (InvocationTargetException e) { Throwable cause = e.getCause(); if (cause instanceof RuntimeException re) { @@ -65,16 +90,10 @@ static Thread.Builder.OfVirtual virtualThreadBuilder(Executor scheduler) { } /** - * Return true if custom schedulers are supported. + * Returns a ThreadFactory to create virtual threads that use the given scheduler. + * @throws UnsupportedOperationException if custom schedulers are not supported */ - static boolean supportsCustomScheduler() { - try (var pool = Executors.newCachedThreadPool()) { - try { - virtualThreadBuilder(pool); - return true; - } catch (UnsupportedOperationException e) { - return false; - } - } + public static ThreadFactory virtualThreadFactory(Executor scheduler) { + return virtualThreadBuilder(scheduler).factory(); } } diff --git a/test/lib/jdk/test/whitebox/parser/DiagnosticCommand.java b/test/lib/jdk/test/whitebox/parser/DiagnosticCommand.java index adb699bac6a..6b7efa2dc87 100644 --- a/test/lib/jdk/test/whitebox/parser/DiagnosticCommand.java +++ b/test/lib/jdk/test/whitebox/parser/DiagnosticCommand.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2024, 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 @@ -26,7 +26,7 @@ public class DiagnosticCommand { public enum DiagnosticArgumentType { - JLONG, BOOLEAN, STRING, NANOTIME, STRINGARRAY, MEMORYSIZE + JLONG, BOOLEAN, STRING, NANOTIME, STRINGARRAY, MEMORYSIZE, FILE } private String name; diff --git a/test/micro/org/openjdk/bench/jdk/classfile/Write.java b/test/micro/org/openjdk/bench/jdk/classfile/Write.java index 59d07b05927..e5304f0e51c 100644 --- a/test/micro/org/openjdk/bench/jdk/classfile/Write.java +++ b/test/micro/org/openjdk/bench/jdk/classfile/Write.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, 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 @@ -22,7 +22,6 @@ */ package org.openjdk.bench.jdk.classfile; -import java.lang.classfile.AccessFlags; import java.lang.reflect.AccessFlag; import java.lang.classfile.ClassFile; import java.lang.classfile.TypeKind; @@ -30,6 +29,8 @@ import jdk.internal.org.objectweb.asm.*; import org.openjdk.jmh.annotations.*; import java.io.FileOutputStream; +import static java.lang.classfile.ClassFile.ACC_PUBLIC; +import static java.lang.classfile.ClassFile.ACC_STATIC; import static java.lang.constant.ConstantDescs.*; import java.nio.file.Files; @@ -148,7 +149,7 @@ public byte[] jdkTree() { ); for (int xi = 0; xi < 40; ++xi) { cb.withMethod("main" + ((xi == 0) ? "" : "" + xi), MTD_void_StringArray, - AccessFlags.ofMethod(AccessFlag.STATIC, AccessFlag.PUBLIC).flagsMask(), + ACC_PUBLIC | ACC_STATIC, mb -> mb.withCode(c0 -> { java.lang.classfile.Label loopTop = c0.newLabel(); java.lang.classfile.Label loopEnd = c0.newLabel(); @@ -196,7 +197,7 @@ public byte[] jdkTreePrimitive() { ); for (int xi = 0; xi < 40; ++xi) { cb.withMethod("main" + ((xi == 0) ? "" : "" + xi), MTD_void_StringArray, - AccessFlags.ofMethod(AccessFlag.STATIC, AccessFlag.PUBLIC).flagsMask(), + ACC_PUBLIC | ACC_STATIC, mb -> mb.withCode(c0 -> { java.lang.classfile.Label loopTop = c0.newLabel(); java.lang.classfile.Label loopEnd = c0.newLabel(); diff --git a/test/micro/org/openjdk/bench/vm/compiler/MergeStoreBench.java b/test/micro/org/openjdk/bench/vm/compiler/MergeStoreBench.java new file mode 100644 index 00000000000..26c8287c4de --- /dev/null +++ b/test/micro/org/openjdk/bench/vm/compiler/MergeStoreBench.java @@ -0,0 +1,1132 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, Alibaba Group Holding Limited. 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. + */ +package org.openjdk.bench.vm.compiler; + +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.lang.reflect.Field; +import java.nio.ByteOrder; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +import jdk.internal.misc.Unsafe; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 5, time = 1000, timeUnit = TimeUnit.MILLISECONDS) +@Fork(value = 3, jvmArgsAppend = {"--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED"}) +public class MergeStoreBench { + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); + + final static VarHandle INT_L = MethodHandles.byteArrayViewVarHandle(int[].class , ByteOrder.LITTLE_ENDIAN); + final static VarHandle INT_B = MethodHandles.byteArrayViewVarHandle(int[].class , ByteOrder.BIG_ENDIAN); + final static VarHandle LONG_L = MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.LITTLE_ENDIAN); + final static VarHandle LONG_B = MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.BIG_ENDIAN); + final static VarHandle CHAR_L = MethodHandles.byteArrayViewVarHandle(char[].class, ByteOrder.LITTLE_ENDIAN); + final static VarHandle CHAR_B = MethodHandles.byteArrayViewVarHandle(char[].class, ByteOrder.BIG_ENDIAN); + + final static int NUMBERS = 8192; + + final byte[] bytes4 = new byte[NUMBERS * 4]; + final byte[] bytes8 = new byte[NUMBERS * 8]; + final int [] ints = new int [NUMBERS ]; + final long[] longs = new long[NUMBERS ]; + final char[] chars = new char[NUMBERS ]; + + @Setup + public void setup() { + Random r = new Random(); + for (int i = 0; i < ints.length; i++) { + ints[i] = r.nextInt(); + INT_L.set(bytes4, i * 4, i); + } + + for (int i = 0; i < longs.length; i++) { + longs[i] = r.nextLong(); + LONG_L.set(bytes8, i * 8, i); + } + } + + /* + * The names of these cases have the following `B/L/V/U` suffixes, which are: + * ``` + * B BigEndian + * L LittleEndian + * V VarHandle + * U Unsafe + * R ReverseBytes + * C Unsafe.getChar & putChar + * S Unsafe.getShort & putShort + * ``` + */ + + @Benchmark + public void getIntB(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + sum += getIntB(bytes4, i * 4); + } + BH.consume(sum); + } + + @Benchmark + public void getIntBU(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + sum += getIntBU(bytes4, i * 4); + } + BH.consume(sum); + } + + @Benchmark + public void getIntBV(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + sum += (int) INT_B.get(bytes4, i * 4); + } + BH.consume(sum); + } + + @Benchmark + public void getIntL(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + sum += getIntL(bytes4, i * 4); + } + BH.consume(sum); + } + + @Benchmark + public void getIntLU(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + sum += getIntLU(bytes4, i * 4); + } + BH.consume(sum); + } + + @Benchmark + public void getIntLV(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + sum += (int) INT_L.get(bytes4, i * 4); + } + BH.consume(sum); + } + + @Benchmark + public void getIntRB(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + sum += getIntRB(bytes4, i * 4); + } + BH.consume(sum); + } + + @Benchmark + public void getIntRBU(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + sum += getIntRBU(bytes4, i * 4); + } + BH.consume(sum); + } + + @Benchmark + public void getIntRL(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + sum += getIntRL(bytes4, i * 4); + } + BH.consume(sum); + } + + @Benchmark + public void getIntRLU(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + sum += getIntRLU(bytes4, i * 4); + } + BH.consume(sum); + } + + @Benchmark + public void getIntRU(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + sum += Integer.reverseBytes( + UNSAFE.getInt(bytes4, Unsafe.ARRAY_BYTE_BASE_OFFSET + i * 4)); + } + BH.consume(sum); + } + + @Benchmark + public void getIntU(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + sum += UNSAFE.getInt(bytes4, Unsafe.ARRAY_BYTE_BASE_OFFSET + i * 4); + } + BH.consume(sum); + } + + @Benchmark + public void setIntB(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + int v = ints[i]; + setIntB(bytes4, i * 4, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setIntBU(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + int v = ints[i]; + setIntBU(bytes4, i * 4, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setIntBV(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + int v = ints[i]; + INT_B.set(bytes4, i * 4, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setIntL(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + int v = ints[i]; + setIntL(bytes4, i * 4, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setIntLU(Blackhole BH) { + int sum = 0; + for (int i = 0; i < ints.length; i++) { + int v = ints[i]; + setIntLU(bytes4, i * 4, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setIntLV(Blackhole BH) { + long sum = 0; + for (int i = 0; i < ints.length; i++) { + int v = ints[i]; + INT_L.set(bytes4, i * 4, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setIntRB(Blackhole BH) { + long sum = 0; + for (int i = 0; i < ints.length; i++) { + int v = ints[i]; + setIntRB(bytes4, i * 4, ints[i]); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setIntRBU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < ints.length; i++) { + int v = ints[i]; + setIntRBU(bytes4, i * 4, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setIntRL(Blackhole BH) { + long sum = 0; + for (int i = 0; i < ints.length; i++) { + int v = ints[i]; + setIntRL(bytes4, i * 4, ints[i]); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setIntRLU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < ints.length; i++) { + int v = ints[i]; + setIntRLU(bytes4, i * 4, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setIntRU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < ints.length; i++) { + int v = ints[i]; + v = Integer.reverseBytes(v); + UNSAFE.putInt(bytes4, Unsafe.ARRAY_BYTE_BASE_OFFSET + i * 4, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setIntU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < ints.length; i++) { + int v = ints[i]; + UNSAFE.putInt(bytes4, Unsafe.ARRAY_BYTE_BASE_OFFSET + i * 4, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void getLongB(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + sum += getLongB(bytes8, i * 8); + } + BH.consume(sum); + } + + @Benchmark + public void getLongBU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + sum += getLongBU(bytes8, i * 8); + } + BH.consume(sum); + } + + @Benchmark + public void getLongBV(Blackhole BH) { + long sum = 0; + for (int i = 0; i < ints.length; i++) { + sum += (long) LONG_B.get(bytes8, i * 8); + } + BH.consume(sum); + } + + @Benchmark + public void getLongL(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + sum += getLongL(bytes8, i * 8); + } + BH.consume(sum); + } + + @Benchmark + public void getLongLU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + sum += getLongLU(bytes8, i * 8); + } + BH.consume(sum); + } + + @Benchmark + public void getLongLV(Blackhole BH) { + long sum = 0; + for (int i = 0; i < ints.length; i++) { + sum += (long) LONG_L.get(bytes8, i * 8); + } + BH.consume(sum); + } + + @Benchmark + public void getLongRB(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + sum += getLongRB(bytes8, i * 8); + } + BH.consume(sum); + } + + @Benchmark + public void getLongRBU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + sum += getLongRBU(bytes8, i * 8); + } + BH.consume(sum); + } + + @Benchmark + public void getLongRL(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + sum += getLongRL(bytes8, i * 8); + } + BH.consume(sum); + } + + @Benchmark + public void getLongRLU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + sum += getLongRLU(bytes8, i * 8); + } + BH.consume(sum); + } + + @Benchmark + public void getLongRU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + sum += Long.reverseBytes( + UNSAFE.getLong(bytes8, Unsafe.ARRAY_BYTE_BASE_OFFSET + i * 8)); + } + BH.consume(sum); + } + + @Benchmark + public void getLongU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + sum += UNSAFE.getLong(bytes8, Unsafe.ARRAY_BYTE_BASE_OFFSET + i * 8); + } + BH.consume(sum); + } + + @Benchmark + public void setLongB(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + long v = longs[i]; + setLongB(bytes8, i * 8, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setLongBU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + long v = longs[i]; + setLongBU(bytes8, i * 8, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setLongBV(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + long v = longs[i]; + LONG_B.set(bytes8, i * 8, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setLongL(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + long v = longs[i]; + setLongL(bytes8, i * 8, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setLongLU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + long v = longs[i]; + setLongLU(bytes8, i * 8, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setLongLV(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + long v = longs[i]; + LONG_L.set(bytes8, i * 8, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setLongRB(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + long v = longs[i]; + setLongRB(bytes8, i * 8, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setLongRBU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + long v = longs[i]; + setLongRBU(bytes8, i * 8, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setLongRL(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + long v = longs[i]; + setLongRL(bytes8, i * 8, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setLongRLU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + long v = longs[i]; + setLongRLU(bytes8, i * 8, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setLongRU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + long v = longs[i]; + v = Long.reverseBytes(v); + UNSAFE.putLong(bytes8, Unsafe.ARRAY_BYTE_BASE_OFFSET + i * 8, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void setLongU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + long v = longs[i]; + UNSAFE.putLong(bytes8, Unsafe.ARRAY_BYTE_BASE_OFFSET + i * 8, v); + sum += v; + } + BH.consume(sum); + } + + @Benchmark + public void getCharB(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + char c = getCharB(bytes4, i); + sum += c; + } + BH.consume(sum); + } + + @Benchmark + public void getCharBV(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + char c = (char) CHAR_B.get(bytes4, Unsafe.ARRAY_BYTE_BASE_OFFSET + i * 2); + sum += c; + } + BH.consume(sum); + } + + @Benchmark + public void getCharBU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + char c = getCharBU(bytes4, i); + sum += c; + } + BH.consume(sum); + } + + @Benchmark + public void getCharL(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + char c = getCharL(bytes4, i); + sum += c; + } + BH.consume(sum); + } + @Benchmark + public void getCharLU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + char c = getCharLU(bytes4, i); + sum += c; + } + BH.consume(sum); + } + + + @Benchmark + public void getCharLV(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + char c = (char) CHAR_L.get(bytes4, Unsafe.ARRAY_BYTE_BASE_OFFSET + i * 2); + sum += c; + } + BH.consume(sum); + } + + @Benchmark + public void getCharC(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + char c = UNSAFE.getChar(bytes4, Unsafe.ARRAY_BYTE_BASE_OFFSET + i * 2); + sum += c; + } + BH.consume(sum); + } + + @Benchmark + public void setCharBS(Blackhole BH) { + long sum = 0; + for (int i = 0; i < chars.length; i++) { + char c = chars[i]; + putShortB(bytes4, i * 2, c); + sum += c; + } + BH.consume(sum); + } + + @Benchmark + public void setCharBV(Blackhole BH) { + long sum = 0; + for (int i = 0; i < chars.length; i++) { + char c = chars[i]; + CHAR_B.set(bytes4, i * 2, c); + sum += c; + } + BH.consume(sum); + } + + @Benchmark + public void setCharLS(Blackhole BH) { + long sum = 0; + for (int i = 0; i < chars.length; i++) { + char c = chars[i]; + putShortL(bytes4, i * 2, c); + sum += c; + } + BH.consume(sum); + } + + @Benchmark + public void setCharLV(Blackhole BH) { + long sum = 0; + for (int i = 0; i < chars.length; i++) { + char c = chars[i]; + CHAR_L.set(bytes4, i * 2, c); + sum += c; + } + BH.consume(sum); + } + + @Benchmark + public void setCharC(Blackhole BH) { + long sum = 0; + for (int i = 0; i < chars.length; i++) { + char c = chars[i]; + UNSAFE.putChar(bytes4, Unsafe.ARRAY_BYTE_BASE_OFFSET + i * 2, c); + sum += c; + } + BH.consume(sum); + } + + /* + * putChars4 Test whether four constant chars can be MergeStored + * + */ + @Benchmark + public void putChars4B(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + putChars4B(bytes8, i * 4); + sum += longs[i]; + } + BH.consume(sum); + } + + @Benchmark + public void putChars4BU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + putChars4BU(bytes8, i * 4); + sum += longs[i]; + } + BH.consume(sum); + } + + @Benchmark + public void putChars4BV(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + putChars4BV(bytes8, i * 4); + sum += longs[i]; + } + BH.consume(sum); + } + + @Benchmark + public void putChars4L(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + putChars4L(bytes8, i * 4); + sum += longs[i]; + } + BH.consume(sum); + } + + @Benchmark + public void putChars4LU(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + putChars4LU(bytes8, i * 4); + sum += longs[i]; + } + BH.consume(sum); + } + + @Benchmark + public void putChars4LV(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + putChars4LV(bytes8, i * 4); + sum += longs[i]; + } + BH.consume(sum); + } + + @Benchmark + public void putChars4C(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + putChars4C(bytes8, i * 4); + sum += longs[i]; + } + BH.consume(sum); + } + + @Benchmark + public void putChars4S(Blackhole BH) { + long sum = 0; + for (int i = 0; i < longs.length; i++) { + putChars4S(bytes8, i * 4); + sum += longs[i]; + } + BH.consume(sum); + } + + static int getIntB(byte[] array, int offset) { + return ((array[offset ] & 0xff) << 24) + | ((array[offset + 1] & 0xff) << 16) + | ((array[offset + 2] & 0xff) << 8) + | ((array[offset + 3] & 0xff) ); + } + + static int getIntBU(byte[] array, int offset) { + final long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + offset; + return ((UNSAFE.getByte(array, address ) & 0xff) << 24) + | ((UNSAFE.getByte(array, address + 1) & 0xff) << 16) + | ((UNSAFE.getByte(array, address + 2) & 0xff) << 8) + | ((UNSAFE.getByte(array, address + 3) & 0xff) ); + } + + static int getIntL(byte[] array, int offset) { + return ((array[offset ] & 0xff) ) + | ((array[offset + 1] & 0xff) << 8) + | ((array[offset + 2] & 0xff) << 16) + | ((array[offset + 3] & 0xff) << 24); + } + + static int getIntRB(byte[] array, int offset) { + return Integer.reverseBytes(getIntB(array, offset)); + } + + static int getIntRBU(byte[] array, int offset) { + return Integer.reverseBytes(getIntBU(array, offset)); + } + + static int getIntRL(byte[] array, int offset) { + return Integer.reverseBytes(getIntL(array, offset)); + } + + static int getIntRLU(byte[] array, int offset) { + return Integer.reverseBytes(getIntLU(array, offset)); + } + + static void setIntB(byte[] array, int offset, int value) { + array[offset ] = (byte) (value >> 24); + array[offset + 1] = (byte) (value >> 16); + array[offset + 2] = (byte) (value >> 8); + array[offset + 3] = (byte) (value ); + } + + static void setIntBU(byte[] array, int offset, int value) { + final long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + offset; + UNSAFE.putByte(array, address , (byte) (value >> 24)); + UNSAFE.putByte(array, address + 1, (byte) (value >> 16)); + UNSAFE.putByte(array, address + 2, (byte) (value >> 8)); + UNSAFE.putByte(array, address + 3, (byte) (value )); + } + + public static void setIntL(byte[] array, int offset, int value) { + array[offset ] = (byte) value; + array[offset + 1] = (byte) (value >> 8); + array[offset + 2] = (byte) (value >> 16); + array[offset + 3] = (byte) (value >> 24); + } + + public static void setIntLU(byte[] array, int offset, int value) { + final long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + offset; + UNSAFE.putByte(array, address , (byte) value ); + UNSAFE.putByte(array, address + 1, (byte) (value >> 8)); + UNSAFE.putByte(array, address + 2, (byte) (value >> 16)); + UNSAFE.putByte(array, address + 3, (byte) (value >> 24)); + } + + public static void setIntRL(byte[] array, int offset, int value) { + value = Integer.reverseBytes(value); + setIntL(array, offset, value); + } + + public static void setIntRLU(byte[] array, int offset, int value) { + value = Integer.reverseBytes(value); + setIntLU(array, offset, value); + } + + public static void setIntRB(byte[] array, int offset, int value) { + value = Integer.reverseBytes(value); + setIntB(array, offset, value); + } + + public static void setIntRBU(byte[] array, int offset, int value) { + value = Integer.reverseBytes(value); + setIntBU(array, offset, value); + } + + static long getLongB(byte[] array, int offset) { + return (((long) array[offset ] & 0xff) << 56) + | (((long) array[offset + 1] & 0xff) << 48) + | (((long) array[offset + 2] & 0xff) << 40) + | (((long) array[offset + 3] & 0xff) << 32) + | (((long) array[offset + 4] & 0xff) << 24) + | (((long) array[offset + 5] & 0xff) << 16) + | (((long) array[offset + 6] & 0xff) << 8) + | (((long) array[offset + 7] & 0xff) ); + } + + static long getLongBU(byte[] array, int offset) { + final long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + offset; + return (((long)(UNSAFE.getByte(array, address) & 0xff)) << 56) + | (((long)(UNSAFE.getByte(array, address + 1) & 0xff)) << 48) + | (((long)(UNSAFE.getByte(array, address + 2) & 0xff)) << 40) + | (((long)(UNSAFE.getByte(array, address + 3) & 0xff)) << 32) + | (((long)(UNSAFE.getByte(array, address + 4) & 0xff)) << 24) + | (((long)(UNSAFE.getByte(array, address + 5) & 0xff)) << 16) + | (((long)(UNSAFE.getByte(array, address + 6) & 0xff)) << 8) + | (((long)(UNSAFE.getByte(array, address + 7) & 0xff)) ); + } + + public static long getLongL(byte[] array, int offset) { + return (((long) array[offset ] & 0xff) ) + | (((long) array[offset + 1] & 0xff) << 8) + | (((long) array[offset + 2] & 0xff) << 16) + | (((long) array[offset + 3] & 0xff) << 24) + | (((long) array[offset + 4] & 0xff) << 32) + | (((long) array[offset + 5] & 0xff) << 40) + | (((long) array[offset + 6] & 0xff) << 48) + | (((long) array[offset + 7] & 0xff) << 56); + } + + static long getLongLU(byte[] array, int offset) { + final long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + offset; + return (((long)(UNSAFE.getByte(array, address ) & 0xff)) ) + | (((long)(UNSAFE.getByte(array, address + 1) & 0xff)) << 8) + | (((long)(UNSAFE.getByte(array, address + 2) & 0xff)) << 16) + | (((long)(UNSAFE.getByte(array, address + 3) & 0xff)) << 24) + | (((long)(UNSAFE.getByte(array, address + 4) & 0xff)) << 32) + | (((long)(UNSAFE.getByte(array, address + 5) & 0xff)) << 40) + | (((long)(UNSAFE.getByte(array, address + 6) & 0xff)) << 48) + | (((long)(UNSAFE.getByte(array, address + 7) & 0xff)) << 56); + } + + static long getLongRB(byte[] array, int offset) { + return getLongB(array, offset); + } + + static long getLongRBU(byte[] array, int offset) { + return getLongBU(array, offset); + } + + static long getLongRL(byte[] array, int offset) { + return getLongL(array, offset); + } + + static long getLongRLU(byte[] array, int offset) { + return getLongLU(array, offset); + } + + static void setLongB(byte[] array, int offset, long value) { + array[offset] = (byte) (value >> 56); + array[offset + 1] = (byte) (value >> 48); + array[offset + 2] = (byte) (value >> 40); + array[offset + 3] = (byte) (value >> 32); + array[offset + 4] = (byte) (value >> 24); + array[offset + 5] = (byte) (value >> 16); + array[offset + 6] = (byte) (value >> 8); + array[offset + 7] = (byte) (value ); + } + + public static void setLongL(byte[] array, int offset, long value) { + array[offset] = (byte) value ; + array[offset + 1] = (byte) (value >> 8 ); + array[offset + 2] = (byte) (value >> 16); + array[offset + 3] = (byte) (value >> 24); + array[offset + 4] = (byte) (value >> 32); + array[offset + 5] = (byte) (value >> 40); + array[offset + 6] = (byte) (value >> 48); + array[offset + 7] = (byte) (value >> 56); + } + + public static void setLongRL(byte[] array, int offset, long value) { + value = Long.reverseBytes(value); + setLongL(array, offset, value); + } + + public static void setLongRLU(byte[] array, int offset, long value) { + value = Long.reverseBytes(value); + setLongLU(array, offset, value); + } + + public static void setLongRB(byte[] array, int offset, long value) { + value = Long.reverseBytes(value); + setLongB(array, offset, value); + } + + public static void setLongRBU(byte[] array, int offset, long value) { + value = Long.reverseBytes(value); + setLongBU(array, offset, value); + } + + public static void setLongBU(byte[] array, int offset, long value) { + final long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + offset; + UNSAFE.putByte(array, address , (byte) (value >> 56)); + UNSAFE.putByte(array, address + 1, (byte) (value >> 48)); + UNSAFE.putByte(array, address + 2, (byte) (value >> 40)); + UNSAFE.putByte(array, address + 3, (byte) (value >> 32)); + UNSAFE.putByte(array, address + 4, (byte) (value >> 24)); + UNSAFE.putByte(array, address + 5, (byte) (value >> 16)); + UNSAFE.putByte(array, address + 6, (byte) (value >> 8)); + UNSAFE.putByte(array, address + 7, (byte) value ); + } + + public static void setLongLU(byte[] array, int offset, long value) { + final long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + offset; + UNSAFE.putByte(array, address , (byte) value ); + UNSAFE.putByte(array, address + 1, (byte) (value >> 8)); + UNSAFE.putByte(array, address + 2, (byte) (value >> 16)); + UNSAFE.putByte(array, address + 3, (byte) (value >> 24)); + UNSAFE.putByte(array, address + 4, (byte) (value >> 32)); + UNSAFE.putByte(array, address + 5, (byte) (value >> 40)); + UNSAFE.putByte(array, address + 6, (byte) (value >> 48)); + UNSAFE.putByte(array, address + 7, (byte) (value >> 56)); + } + + public static int getIntLU(byte[] array, int offset) { + final long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + offset; + return ((UNSAFE.getByte(array, address ) & 0xff) ) + | ((UNSAFE.getByte(array, address + 1) & 0xff) << 8) + | ((UNSAFE.getByte(array, address + 2) & 0xff) << 16) + | ((UNSAFE.getByte(array, address + 3) & 0xff) << 24); + } + + public static char getCharB(byte[] val, int index) { + index <<= 1; + return (char)(((val[index ] & 0xff) << 8) + | ((val[index + 1] & 0xff))); + } + + public static char getCharBR(byte[] val, int index) { + return Character.reverseBytes(getCharB(val, index)); + } + + public static char getCharL(byte[] val, int index) { + index <<= 1; + return (char)(((val[index ] & 0xff)) + | ((val[index + 1] & 0xff) << 8)); + } + + public static char getCharLR(byte[] val, int index) { + return Character.reverseBytes(getCharL(val, index)); + } + + public static char getCharBU(byte[] array, int offset) { + final long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + (offset << 1); + return (char) (((UNSAFE.getByte(array, address ) & 0xff) << 8) + | ((UNSAFE.getByte(array, address + 1) & 0xff) )); + } + + public static char getCharLU(byte[] array, int offset) { + final long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + (offset << 1); + return (char) (((UNSAFE.getByte(array, address ) & 0xff) ) + | ((UNSAFE.getByte(array, address + 1) & 0xff) << 8)); + } + + public void putChars4B(byte[] bytes, int offset) { + char c0 = 'n', c1 = 'u', c2 = 'l', c3 = 'l'; + putShortB(bytes, offset , c0); + putShortB(bytes, offset + 1, c1); + putShortB(bytes, offset + 2, c2); + putShortB(bytes, offset + 3, c3); + } + + public void putChars4BU(byte[] bytes, int offset) { + char c0 = 'n', c1 = 'u', c2 = 'l', c3 = 'l'; + putShortBU(bytes, offset , c0); + putShortBU(bytes, offset + 1, c1); + putShortBU(bytes, offset + 2, c2); + putShortBU(bytes, offset + 3, c3); + } + + public void putChars4BV(byte[] bytes, int offset) { + char c0 = 'n', c1 = 'u', c2 = 'l', c3 = 'l'; + offset <<= 1; + CHAR_B.set(bytes, offset , c0); + CHAR_B.set(bytes, offset + 2, c1); + CHAR_B.set(bytes, offset + 4, c2); + CHAR_B.set(bytes, offset + 6, c3); + } + + public void putChars4L(byte[] bytes, int offset) { + char c0 = 'n', c1 = 'u', c2 = 'l', c3 = 'l'; + putShortL(bytes, offset , c0); + putShortL(bytes, offset + 1, c1); + putShortL(bytes, offset + 2, c2); + putShortL(bytes, offset + 3, c3); + } + + public void putChars4LV(byte[] bytes, int offset) { + char c0 = 'n', c1 = 'u', c2 = 'l', c3 = 'l'; + offset <<= 1; + CHAR_L.set(bytes, offset , c0); + CHAR_L.set(bytes, offset + 2, c1); + CHAR_L.set(bytes, offset + 4, c2); + CHAR_L.set(bytes, offset + 6, c3); + } + + public void putChars4LU(byte[] bytes, int offset) { + char c0 = 'n', c1 = 'u', c2 = 'l', c3 = 'l'; + putShortLU(bytes, offset , c0); + putShortLU(bytes, offset + 1, c1); + putShortLU(bytes, offset + 2, c2); + putShortLU(bytes, offset + 3, c3); + } + + public void putChars4C(byte[] bytes, int offset) { + char c0 = 'n', c1 = 'u', c2 = 'l', c3 = 'l'; + final long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + (offset << 1); + UNSAFE.putChar(bytes, address , c0); + UNSAFE.putChar(bytes, address + 2, c1); + UNSAFE.putChar(bytes, address + 4, c2); + UNSAFE.putChar(bytes, address + 6, c3); + } + + public void putChars4S(byte[] bytes, int offset) { + char c0 = 'n', c1 = 'u', c2 = 'l', c3 = 'l'; + final long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + (offset << 1); + UNSAFE.putShort(bytes, address , (short) c0); + UNSAFE.putShort(bytes, address + 2, (short) c1); + UNSAFE.putShort(bytes, address + 4, (short) c2); + UNSAFE.putShort(bytes, address + 6, (short) c3); + } + + private static void putShortB(byte[] val, int index, int c) { + index <<= 1; + val[index ] = (byte)(c >> 8); + val[index + 1] = (byte)(c ); + } + + public static void putShortBU(byte[] array, int offset, int c) { + final long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + (offset << 1); + UNSAFE.putByte(array, address , (byte) (c >> 8)); + UNSAFE.putByte(array, address + 1, (byte) (c )); + } + + private static void putShortL(byte[] val, int index, int c) { + index <<= 1; + val[index ] = (byte)(c ); + val[index + 1] = (byte)(c >> 8); + } + + public static void putShortLU(byte[] array, int offset, int c) { + final long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + (offset << 1); + UNSAFE.putByte(array, address , (byte) (c )); + UNSAFE.putByte(array, address + 1, (byte) (c >> 8)); + } +}